From fbe1a460182241cd962656c735f9f99491db5674 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 4 Jun 2026 05:25:46 +0800 Subject: [PATCH 0001/1190] fix(route/twitter): throw exception on invalid user --- lib/routes/twitter/api/web-api/api.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts index b412517badef..d6801babbd17 100644 --- a/lib/routes/twitter/api/web-api/api.ts +++ b/lib/routes/twitter/api/web-api/api.ts @@ -164,6 +164,13 @@ const getList = async (id: string, params?: Record) => const getUser = async (id: string) => { const userData: any = await getUserData(id); + + if (!userData.data.user) { + throw new InvalidParameterError("This account doesn't exist"); + } else if (userData.data.user.result.__typename === 'UserUnavailable') { + throw new InvalidParameterError(userData.data.user.result.message || 'User is unavailable'); + } + return { profile_image_url: userData.data?.user?.result?.avatar?.image_url, ...userData.data?.user?.result?.core, From f5dc400cb2b6655623e57d665fdb723141e6ee4a Mon Sep 17 00:00:00 2001 From: CaoMeiYouRen233 <87434371+CaoMeiYouRen233@users.noreply.github.com> Date: Thu, 4 Jun 2026 05:30:53 +0800 Subject: [PATCH 0002/1190] =?UTF-8?q?fix(route/iwara):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20apiRootUrl=20=E5=BC=95=E7=94=A8=E9=94=99=E8=AF=AF?= =?UTF-8?q?=20(#22176)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/routes/iwara/ranking.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/iwara/ranking.ts b/lib/routes/iwara/ranking.ts index fe2aef5abb10..f08a0c90ef0d 100644 --- a/lib/routes/iwara/ranking.ts +++ b/lib/routes/iwara/ranking.ts @@ -4,7 +4,7 @@ import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import { getPlaywrightPage } from '@/utils/playwright'; -import { apiqRootUrl, parseThumbnail, rootUrl, typeMap } from './utils'; +import { apiRootUrl, parseThumbnail, rootUrl, typeMap } from './utils'; const sortMap = { date: 'Latest', @@ -53,7 +53,7 @@ async function handler(ctx) { const { type = 'video', sort = 'date', rating = 'ecchi' } = ctx.req.param(); const limit = ctx.req.query('limit') || 32; - const url = `${apiqRootUrl}/${type === 'video' ? 'videos' : 'images'}?sort=${sort}&rating=${rating}&limit=${limit}`; + const url = `${apiRootUrl}/${type === 'video' ? 'videos' : 'images'}?sort=${sort}&rating=${rating}&limit=${limit}`; const items = await cache.tryGet( `iwara:ranking:${type}:${sort}:${rating}`, From 2fa4fd6d582297eb584ee54934d6e6cc3a0d7dfc Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 4 Jun 2026 05:45:02 +0800 Subject: [PATCH 0003/1190] revert: "chore: workaround playwright install hangs in node 26.1.0" This reverts commit f20edaf7c7191ff2d525e9d0cf88dbcea79be159. refs: https://github.com/microsoft/playwright/issues/40724 --- .github/workflows/test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aedd27b554ed..7efaa00416dd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,8 +27,7 @@ jobs: strategy: fail-fast: false matrix: - # playwright install hangs in node v26.1.0, https://github.com/microsoft/playwright/issues/40724 - node-version: [26.0.0, lts/*, lts/-1] + node-version: [latest, lts/*, lts/-1] name: Vitest on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -63,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [26.0.0, lts/*, lts/-1] + node-version: [latest, lts/*, lts/-1] chromium: - name: bundled Chromium dependency: '' From 7a8c5c6c62bb7b7342d0f7a9eb40e30edf95a34d Mon Sep 17 00:00:00 2001 From: Jiamin Date: Thu, 4 Jun 2026 06:03:31 +0800 Subject: [PATCH 0004/1190] feat(route): add Henan Museum exhibition route (#22138) * feat(route): add Henan Museum exhibition route * fix: remove trim for title --- lib/routes/chnmus/exhibition.tsx | 210 +++++++++++++++++++++++++++++++ lib/routes/chnmus/namespace.ts | 10 ++ 2 files changed, 220 insertions(+) create mode 100644 lib/routes/chnmus/exhibition.tsx create mode 100644 lib/routes/chnmus/namespace.ts diff --git a/lib/routes/chnmus/exhibition.tsx b/lib/routes/chnmus/exhibition.tsx new file mode 100644 index 000000000000..96bf680ef668 --- /dev/null +++ b/lib/routes/chnmus/exhibition.tsx @@ -0,0 +1,210 @@ +import { load } from 'cheerio'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +// format the date to YYYY-MM-DD and handle missing year or month +const extractDates = (durationStr: string) => { + let startDate: string | undefined; + let endDate: string | undefined; + + if (!durationStr) { + return { startDate, endDate }; + } + + const parts = durationStr.split(/——|-|—|~/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency + const startStr = parts[0]; + const endStr = parts[1]; + + let startYear: string | undefined; + let startMonth: string | undefined; + + const startRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日/; + const startMatch = startStr.match(startRegex); + + if (startMatch) { + startYear = startMatch[1]; + startMonth = startMatch[2].padStart(2, '0'); + const startDay = startMatch[3].padStart(2, '0'); + startDate = `${startYear}-${startMonth}-${startDay}`; + } + + if (endStr && startDate) { + const endRegex = /(?:(\d{4})年)?(?:(\d{1,2})月)?(\d{1,2})日/; + const endMatch = endStr.match(endRegex); + + if (endMatch) { + const matchYear = endMatch[1]; + const matchMonth = endMatch[2]?.padStart(2, '0'); + const matchDay = endMatch[3].padStart(2, '0'); + + const finalEndYear = matchYear || startYear; + const finalEndMonth = matchMonth || startMonth; + const finalEndDay = matchDay; + endDate = `${finalEndYear}-${finalEndMonth}-${finalEndDay}`; + } + } + + return { startDate, endDate }; +}; + +export const route: Route = { + path: '/information/exhibition/:type?', + categories: ['travel'], + example: '/chnmus/information/exhibition/special', + parameters: { + type: 'Exhibition type, supported values: special(特展详情). Default: All.', + }, + name: 'Special Exhibitions', + maintainers: ['magazian'], + radar: [ + { + source: ['www.chnmus.net/ch/information/exhibition/index.html'], + target: '/information/exhibition', + }, + ], + + handler: async (ctx) => { + const type = ctx.req.param('type'); + const isSpecial = type === 'special'; + + const baseUrl = 'https://www.chnmus.net'; + const apiUrl = `${baseUrl}/ch/information/exhibition/index.html`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: apiUrl, + }); + + const $ = load(response.data); + + const list = $('.col-md-6.d-flex.fadeInBottom') + .toArray() + .map((item) => { + const $item = $(item); + const link = $item.attr('href'); + const imgUrlRaw = $item.find('.lazyload').attr('data-bg'); + const listTitle = $item.find('.common-component-box-title').text(); + + return { + title: listTitle, + itemLink: `https:${link}`, + imgUrl: `https:${imgUrlRaw}`, + }; + }); + + const items = await Promise.all( + list.map((item) => { + // use seperate cache key for special path + const cacheKey = isSpecial ? `${item.itemLink}-special` : item.itemLink; + + return cache.tryGet(cacheKey, async (): Promise> => { + const detailResponse = await got({ + method: 'get', + url: item.itemLink, + }); + const content = load(detailResponse.data); + + const pubDateRaw = content('.common-component-content-attribute-item') + .toArray() + .map((el) => content(el).text()) + .find((text) => text.includes('发布日期'))! + .replaceAll('发布日期:', '') + .trim(); + const pubDate = parseDate(pubDateRaw); + + // Default path: return as news, no detail information for return + if (!isSpecial) { + return { + title: item.title, + link: item.itemLink, + pubDate, + description: renderToString( +
+ +
+ ), + } as Record; + } + + // Special path to return detail exhibition information + const texts = content('.common-component-content-text p') + .toArray() + .map((el) => content(el).text()); + + const title = texts.find((text) => text.includes('展览名称:'))?.replaceAll('展览名称:', ''); + + // filter out items without title, for example: https://www.chnmus.net/ch/information/exhibition/details.html?id=7400076083917230080#list + if (!title) { + return {} as Record; + } + + const location = texts + .find((text) => text.includes('展览地点:'))! + .replaceAll('展览地点:', '') + .trim(); + + const fullDuration = texts + .find((text) => text.includes('展览时间:') || text.includes('开展时间:')) + ?.replaceAll(/(展览|开展)时间:/g, '') + ?.trim(); + + const { startDate, endDate } = extractDates(fullDuration || ''); + const { imgUrl, itemLink } = item; + + const description = renderToString( +
+ +
+

+ 地点: + {location} +

+

+ 开展: + {startDate ?? '未定/常设'} +

+

+ 闭展: + {endDate ?? '未定/常设'} +

+ {fullDuration && ( +

+ 原始展期:{fullDuration} +

+ )} +
+ ); + + return { + title, + link: itemLink, + pubDate, + description, + _extra: { + museumName, + title, + location, + startDate, + endDate, + itemLink, + }, + } as Record; + }) as Promise; + }) + ); + + return { + title: `${museumName} - 展览资讯${isSpecial ? ' - 特展详情' : ''}`, + link: apiUrl, + language: 'zh-CN', + item: items.filter((item) => item.title) as DataItem[], + }; + }, +}; diff --git a/lib/routes/chnmus/namespace.ts b/lib/routes/chnmus/namespace.ts new file mode 100644 index 000000000000..e5fe10c6228f --- /dev/null +++ b/lib/routes/chnmus/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Henan Museum', + url: 'www.chnmus.net', + + zh: { + name: '河南博物院', + }, +}; From 0a2e5b91db4c3b8686c6486c52597f1d874afbca Mon Sep 17 00:00:00 2001 From: TonyRL Date: Wed, 3 Jun 2026 22:50:26 +0000 Subject: [PATCH 0005/1190] fix: fromCodePoint RangeError error #22172 --- lib/routes/toutiao/a-bogus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/toutiao/a-bogus.ts b/lib/routes/toutiao/a-bogus.ts index e3b916232b62..adf82161a7ce 100644 --- a/lib/routes/toutiao/a-bogus.ts +++ b/lib/routes/toutiao/a-bogus.ts @@ -304,7 +304,7 @@ function generate_rc4_bb_str(url_search_params, user_agent, window_env_str, suff // 对后缀两次sm3之的结果 const cus = sm3.sum(sm3.sum(suffix)); // 对ua处理之后的结果 - const ua = sm3.sum(result_encrypt(rc4_encrypt(user_agent, Reflect.apply(String.fromCodePoint, null, [0.003_906_25, 1, 14])), 's3')); + const ua = sm3.sum(result_encrypt(rc4_encrypt(user_agent, Reflect.apply(String.fromCodePoint, null, [Math.floor(0.003_906_25), 1, 14])), 's3')); // const end_time = Date.now(); // b From 3ce8fda699cd6246f6150144d436a28b742b9950 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 23:13:31 +0000 Subject: [PATCH 0006/1190] chore(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#22177) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-assets.yml | 4 ++-- .github/workflows/codeql.yml | 2 +- .github/workflows/comment-on-issue.yml | 4 ++-- .github/workflows/dependabot-fork.yml | 2 +- .github/workflows/docker-release.yml | 4 ++-- .github/workflows/docker-test-cont.yml | 2 +- .github/workflows/docker-test.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/issue-command.yml | 8 ++++---- .github/workflows/lint.yml | 4 ++-- .github/workflows/npm-publish.yml | 2 +- .github/workflows/pr-review.yml | 2 +- .github/workflows/semgrep.yml | 2 +- .github/workflows/similar-issues.yml | 2 +- .github/workflows/test-full-routes.yml | 2 +- .github/workflows/test.yml | 6 +++--- .github/workflows/update-nix-hash.yml | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 44e45a852264..1b7a88b6a84d 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -18,7 +18,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install pnpm uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - name: Use Node.js Active LTS @@ -51,7 +51,7 @@ jobs: if: ${{ env.DOCS_API_TOKEN != '' }} run: echo "defined=true" >> $GITHUB_OUTPUT - name: Checkout docs - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 if: steps.check-docs-env.outputs.defined == 'true' with: repository: 'RSSNext/rsshub-docs' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a870789efa99..1b1c81be83f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # Initializes the CodeQL tools for scanning. # TODO: use hash pinning when https://github.com/dependabot/dependabot-core/pull/13007 pass diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 06d6825c0088..f39a8c99dee1 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -26,7 +26,7 @@ jobs: outputs: closed: ${{ steps.check.outputs.closed }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -60,7 +60,7 @@ jobs: (needs.checkIssue.result == 'success' || needs.checkIssue.result == 'skipped') && needs.checkIssue.outputs.closed != 'true' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: diff --git a/.github/workflows/dependabot-fork.yml b/.github/workflows/dependabot-fork.yml index 40494f4e0b40..48b76b399419 100644 --- a/.github/workflows/dependabot-fork.yml +++ b/.github/workflows/dependabot-fork.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Comment Dependabot PR uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 9c542fad47ad..d0775bcc611e 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -61,7 +61,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Extract repository name id: repo-name @@ -277,7 +277,7 @@ jobs: if: needs.check-env.outputs.check-docker == 'true' timeout-minutes: 5 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Docker Hub Description uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0 diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index 71de05f45bbe..3bf00faeb22d 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # https://github.com/orgs/community/discussions/25220#discussioncomment-11316244 - name: Search the PR that triggered this workflow diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 80e317c45364..71f5dc94965f 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -27,7 +27,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Set up Docker Buildx # needed by `cache-from` uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 776717f4fc1c..254304d1204d 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 15 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 87e7edd967d4..e56334150310 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - name: Checkout the latest code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 - name: Automatic Rebase @@ -49,7 +49,7 @@ jobs: group: vouch-manage cancel-in-progress: false steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - id: vouch uses: mitchellh/vouch/action/manage-by-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 @@ -102,11 +102,11 @@ jobs: - name: Checkout if: ${{ !github.event.issue.pull_request }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Checkout PR if: github.event.issue.pull_request - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ fromJson(steps.pr-data.outputs.data).head.ref }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c269b1497144..fd649a1ffcb0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -80,7 +80,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Check if PR author is denounced uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 5dcd201dc749..c81a75ce1f53 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -22,7 +22,7 @@ jobs: env: HUSKY: 0 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index 512b7b734d51..eb21a98f0b30 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -22,7 +22,7 @@ jobs: pull-requests: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # https://github.com/orgs/community/discussions/25220#discussioncomment-11316244 - name: Search the PR that triggered this workflow diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index b7bda03ddc4f..be4068a3bdc5 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -22,7 +22,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - run: semgrep ci --sarif > semgrep.sarif env: SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} diff --git a/.github/workflows/similar-issues.yml b/.github/workflows/similar-issues.yml index 95372a91e63f..496219130867 100644 --- a/.github/workflows/similar-issues.yml +++ b/.github/workflows/similar-issues.yml @@ -18,7 +18,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Set up Bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index bf306839795d..27efbe25c481 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -14,7 +14,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install pnpm uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - name: Use Node.js Active LTS diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7efaa00416dd..05b04ca7ad2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,7 @@ jobs: node-version: [latest, lts/*, lts/-1] name: Vitest on Node ${{ matrix.node-version }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -75,7 +75,7 @@ jobs: environment: '{ "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": "1" }' name: Vitest Playwright on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -124,7 +124,7 @@ jobs: node-version: [26, 24, 22] name: Build radar and maintainer on Node ${{ matrix.node-version }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: diff --git a/.github/workflows/update-nix-hash.yml b/.github/workflows/update-nix-hash.yml index 51f1f97812c6..01828d0303ac 100644 --- a/.github/workflows/update-nix-hash.yml +++ b/.github/workflows/update-nix-hash.yml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Install Nix uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 with: From 5b1c0c0b275d32e45c64d1af671bdf2a23564d1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 23:21:03 +0000 Subject: [PATCH 0007/1190] chore(deps): bump @scalar/hono-api-reference from 0.10.19 to 0.10.20 (#22180) Bumps [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono) from 0.10.19 to 0.10.20. - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) --- updated-dependencies: - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.10.20 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 46 +++++++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index b868841f9c6c..5570491d9a02 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@opentelemetry/sdk-trace-base": "2.7.1", "@opentelemetry/semantic-conventions": "1.41.1", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.10.19", + "@scalar/hono-api-reference": "0.10.20", "@sentry/node": "10.55.0", "aes-js": "3.1.2", "cheerio": "1.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1718807cf95e..d66e50226a2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,8 +80,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.10.19 - version: 0.10.19(hono@4.12.23) + specifier: 0.10.20 + version: 0.10.20(hono@4.12.23) '@sentry/node': specifier: 10.55.0 version: 10.55.0(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1)) @@ -2496,26 +2496,26 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.1.12': - resolution: {integrity: sha512-prwHK4ozTU268BHZ/5OstoKB23JSidDuvddAOp0bVz9c29ZxsyzzxPtPcVgF7X16LiZnS1OzY030FoDCM+iC9Q==} + '@scalar/client-side-rendering@0.1.13': + resolution: {integrity: sha512-p8V4HgEWjaCpqsnhclg1pTfjE9JA0AWRr0ocBQHexoHo+pqnSs1d83Mv9rjH7R0FZJrlCSandZZeY3DMX2gYXQ==} engines: {node: '>=22'} - '@scalar/helpers@0.8.0': - resolution: {integrity: sha512-gmOC6VravNB9VDl6wnt/GOj4K/hn48tj5bpW4AM4MhH8Ubil6uu7g1DSoKHwltu8Ks79KEtR6JmOrROi9R7jaQ==} + '@scalar/helpers@0.8.1': + resolution: {integrity: sha512-yuiuBCadP5bjAnIv23QvifVN/NaMi9xBF6b8Wdk4QOzwzLPJmp699MAdf33J0A5i2qKcvnu32iz/VkEJmQRe5g==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.10.19': - resolution: {integrity: sha512-6EfwN/lfPvePzAxe9UE8fr/ZuAAqS6ttUwQu9JTgk2Xl/clicaVVSOc0gyGt+8GLXdysoNinjZ74we8xqNWyCA==} + '@scalar/hono-api-reference@0.10.20': + resolution: {integrity: sha512-/Ae0M9rOpsJJb5b121G8LX4vvBo1t2qFX1ffRKkTgCS9VWfpsfq11r0yBcYy1fmAn1x8YLO21Ls1/9h8LzHuDw==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/schemas@0.3.2': - resolution: {integrity: sha512-iadXBgJ02XUU5C5s6/xh/PmGLzUPd7X8upXIvPWBXDcQ4FHACNgkG8PPZ/beYM8UPDDkTUPM3ygEs0G6jKwGjQ==} + '@scalar/schemas@0.3.3': + resolution: {integrity: sha512-qDcgFu6ta5Z90L9D2P6DFKzYesU+FW5+m55SGmdI4iRMRCwj5umHpec2Y2W/SJcCF6bbUZawMxuOH2Ja6rUNpQ==} engines: {node: '>=22'} - '@scalar/types@0.12.2': - resolution: {integrity: sha512-EzLkubCb7xioiTm9eYnmn/032akaq4kkrrdclgV2uezwtniR8ErQICjhMl2AjBWL6nstHiFZ9RnPZm2Z2/KM0Q==} + '@scalar/types@0.12.3': + resolution: {integrity: sha512-7zaXafbgTFmsJ/9AwYeExUWzXoZNyKOL0SEVAUWRaOndcjxpFCtwzuPrc1elMEWdHopWbY1Qe5pWKbE2aqG2HA==} engines: {node: '>=22'} '@scalar/validation@0.6.0': @@ -3394,7 +3394,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.48: @@ -7563,27 +7563,27 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.1.12': + '@scalar/client-side-rendering@0.1.13': dependencies: - '@scalar/schemas': 0.3.2 - '@scalar/types': 0.12.2 + '@scalar/schemas': 0.3.3 + '@scalar/types': 0.12.3 '@scalar/validation': 0.6.0 - '@scalar/helpers@0.8.0': {} + '@scalar/helpers@0.8.1': {} - '@scalar/hono-api-reference@0.10.19(hono@4.12.23)': + '@scalar/hono-api-reference@0.10.20(hono@4.12.23)': dependencies: - '@scalar/client-side-rendering': 0.1.12 + '@scalar/client-side-rendering': 0.1.13 hono: 4.12.23 - '@scalar/schemas@0.3.2': + '@scalar/schemas@0.3.3': dependencies: - '@scalar/helpers': 0.8.0 + '@scalar/helpers': 0.8.1 '@scalar/validation': 0.6.0 - '@scalar/types@0.12.2': + '@scalar/types@0.12.3': dependencies: - '@scalar/helpers': 0.8.0 + '@scalar/helpers': 0.8.1 nanoid: 5.1.11 type-fest: 5.7.0 zod: 4.4.3 From 9a02b25b9d7fedc575ee672f38a9267ecc06c8fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 08:39:33 +0800 Subject: [PATCH 0008/1190] chore(deps-dev): bump the cloudflare group with 2 updates (#22178) Bumps the cloudflare group with 2 updates: [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/workers-types` from 4.20260602.1 to 4.20260603.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.96.0 to 4.97.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.97.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 4.20260603.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.97.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +-- pnpm-lock.yaml | 88 +++++++++++++++++++++++++------------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 5570491d9a02..942305e2a7c6 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.6", "@cloudflare/playwright": "1.3.0", - "@cloudflare/workers-types": "4.20260602.1", + "@cloudflare/workers-types": "4.20260603.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.68.0", @@ -204,7 +204,7 @@ "unified": "11.0.5", "vite-tsconfig-paths": "6.1.1", "vitest": "4.1.8", - "wrangler": "4.96.0", + "wrangler": "4.97.0", "yaml-eslint-parser": "2.0.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d66e50226a2a..1440481d931f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -297,8 +297,8 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/workers-types': - specifier: 4.20260602.1 - version: 4.20260602.1 + specifier: 4.20260603.1 + version: 4.20260603.1 '@eslint/eslintrc': specifier: 3.3.5 version: 3.3.5 @@ -468,8 +468,8 @@ importers: specifier: 4.1.8 version: 4.1.8(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.13.4(@types/node@25.9.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)) wrangler: - specifier: 4.96.0 - version: 4.96.0(@cloudflare/workers-types@4.20260602.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.97.0 + version: 4.97.0(@cloudflare/workers-types@4.20260603.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.0.0 version: 2.0.0 @@ -624,38 +624,38 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260529.1': - resolution: {integrity: sha512-gxh5sXw0CsBxNCNj8uJnrAxqFM7+R8SZI9WIqYMKz6uaPxgg+eTcBDTxjKczMs6bS21FkTEF6ohIzB5+UvxwKw==} + '@cloudflare/workerd-darwin-64@1.20260601.1': + resolution: {integrity: sha512-iXZBVuRbvuVqQ/63wul01hHCv/3R8G5S8zbkjfoHvyPZFynmlKTV59Hk+H8whyGwFAZuB71UJGLr+G5mJKfjWA==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260529.1': - resolution: {integrity: sha512-B8xOwqd8ok8oaWBPhrpmNVSYou6AejFrYf3VzsJF6pg6TEA2tYbdThAGXgtLPQ8d1RD7GXYjVth2dSMg9napDA==} + '@cloudflare/workerd-darwin-arm64@1.20260601.1': + resolution: {integrity: sha512-veGpZQGBw07Twt+Y4z3oyo+/obKHt0iWUwvDV5GOiDAYjC/zW+YGstgVzg4SHq+k1sLH3ElqL2TXx20I5WBv3Q==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260529.1': - resolution: {integrity: sha512-M1EKzsfoKmmno7MNPkuIc8iOdHLhFnE7ltEYaGGEoOj1MTJfMBK/JkIrhdkzc/06wpyPZPiBfBBmUppbeaMqUg==} + '@cloudflare/workerd-linux-64@1.20260601.1': + resolution: {integrity: sha512-n/9hDz7fPGpYF0J684+Xr5zgjcS2jdmY2Of5m6e+eQ/M9+RfR+UaU8Ee/tkA1dDC0LYQB13hfPafZG66Ff1CsA==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260529.1': - resolution: {integrity: sha512-Mn/Qpl1FAHDLtPthw6ti5gsHRj582jJdtK4OMUlW1CN0v+pmmxaav3KSqq7CS6a+5W0o2e8o9fKnjVilBxVVmQ==} + '@cloudflare/workerd-linux-arm64@1.20260601.1': + resolution: {integrity: sha512-VHRZZbexATS+n+1j3x/CZaYbIJEye0J3iIHgG0Wp+l+NrZCKQ8qi8Lq1uTV0dLJQ67FuZtJtWdQ95mm9F7Fc+A==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260529.1': - resolution: {integrity: sha512-78xgJJeXxkKYumWdKGH1pybUsEjTreSvbJqirW9cth7ZGonqdv5pzAVt+WWcbu0OFcSHrtQFX6zWioPNFp0/xQ==} + '@cloudflare/workerd-windows-64@1.20260601.1': + resolution: {integrity: sha512-ye0C7MFLkeH16iTo8Tcjv2KiFmp23+sZGvUzSQa4xhP0QMe6EoJ+H/4SqqvnZ5nfN54slqKvx2VnXceENWe2CQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260602.1': - resolution: {integrity: sha512-0VssYYXHUn4VR1BaV+GXfhFpI53P2f6AIi17qyA9lQFyTs/u5ZF6IDPda2enDTIPFz/02872RM8CYVlXvRKtUA==} + '@cloudflare/workers-types@4.20260603.1': + resolution: {integrity: sha512-TLeVHoBbcYv35S5TdRWUoj3IJ56BhHtrsuci+O7ithU8yz7ttNdCk6rAl1QUSGNVEWSIp54bWOuV/xmX1zu79g==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4594,8 +4594,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260529.0: - resolution: {integrity: sha512-4pj7WZQR/uYqVMa0cpAmmPBKEb0JegSocuystaXCubY455iqWdPUqgVD9R6N28oneWyPiUyAu5N8QpLbK+MU/Q==} + miniflare@4.20260601.0: + resolution: {integrity: sha512-56TFiulSEQu43cYxdXgCiA3U3i+Ls0NoXwJXd6DmpNsx8yl/1Il2T3DQ4CMXjR6yfE7CSvC5MuXaqcSAMREjgw==} engines: {node: '>=22.0.0'} hasBin: true @@ -5963,17 +5963,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260529.1: - resolution: {integrity: sha512-G1rurOKEdzCtFE0yUPR9J9mUnPzMU8NdsD7NKM1/oMyCr1j3VEtWJzc5VbhgFQHNBVWrHzCL0JgVPuBirRW31g==} + workerd@1.20260601.1: + resolution: {integrity: sha512-Bg4+HF3B8TW0urAv8chiz25HSQ/aJxMBjgheUzu/nB1NQa+CaKGrUPv+Z3bf0np/WxLHYW1kcseVEtzZVPbX4g==} engines: {node: '>=16'} hasBin: true - wrangler@4.96.0: - resolution: {integrity: sha512-8WuiMutalyfBB74wwRyy4VKKJEHjQuEnwcvdUav1M5AfQ8VaTYY5ZQnzvVZPOVXap40k5Mntz1LY3SPWpPukTg==} + wrangler@4.97.0: + resolution: {integrity: sha512-jzW/aNvjerV+4TmwbvwGY6lpcuBk7EFUTonMDNfci45wSmMTj2/OJN+83cc/CeepKdb+6ZjGJw9NRjmcQoxqRg==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260529.1 + '@cloudflare/workers-types': ^4.20260601.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6278,28 +6278,28 @@ snapshots: '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260529.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260601.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260529.1 + workerd: 1.20260601.1 - '@cloudflare/workerd-darwin-64@1.20260529.1': + '@cloudflare/workerd-darwin-64@1.20260601.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260529.1': + '@cloudflare/workerd-darwin-arm64@1.20260601.1': optional: true - '@cloudflare/workerd-linux-64@1.20260529.1': + '@cloudflare/workerd-linux-64@1.20260601.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260529.1': + '@cloudflare/workerd-linux-arm64@1.20260601.1': optional: true - '@cloudflare/workerd-windows-64@1.20260529.1': + '@cloudflare/workerd-windows-64@1.20260601.1': optional: true - '@cloudflare/workers-types@4.20260602.1': {} + '@cloudflare/workers-types@4.20260603.1': {} '@colors/colors@1.6.0': {} @@ -10044,12 +10044,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260529.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260601.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.24.8 - workerd: 1.20260529.1 + workerd: 1.20260601.1 ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -11504,26 +11504,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260529.1: + workerd@1.20260601.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260529.1 - '@cloudflare/workerd-darwin-arm64': 1.20260529.1 - '@cloudflare/workerd-linux-64': 1.20260529.1 - '@cloudflare/workerd-linux-arm64': 1.20260529.1 - '@cloudflare/workerd-windows-64': 1.20260529.1 + '@cloudflare/workerd-darwin-64': 1.20260601.1 + '@cloudflare/workerd-darwin-arm64': 1.20260601.1 + '@cloudflare/workerd-linux-64': 1.20260601.1 + '@cloudflare/workerd-linux-arm64': 1.20260601.1 + '@cloudflare/workerd-windows-64': 1.20260601.1 - wrangler@4.96.0(@cloudflare/workers-types@4.20260602.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.97.0(@cloudflare/workers-types@4.20260603.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260529.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260601.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260529.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260601.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260529.1 + workerd: 1.20260601.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260602.1 + '@cloudflare/workers-types': 4.20260603.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 527577627b0b898b59e4e549e68c3215da9c62d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 09:24:26 +0800 Subject: [PATCH 0009/1190] chore(deps): bump devenv from `d5e9138` to `57f7e43` (#22181) Bumps [devenv](https://github.com/cachix/devenv) from `d5e9138` to `57f7e43`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/d5e9138bae90fe199fbe5de7675014d76d28873b...57f7e43b3c8adb43a7f3df0d393a87e6939d5141) --- updated-dependencies: - dependency-name: devenv dependency-version: 57f7e43b3c8adb43a7f3df0d393a87e6939d5141 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cef7e05c4f14..0570de84d449 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1780316214, - "narHash": "sha256-X3EG0oxt03MegwC/MnQv1saoq9nphEGSSGEAj8mZQOg=", + "lastModified": 1780511534, + "narHash": "sha256-AFc1M/svHCsGWO+tA/t7ZeWckFIuJpML0vJpxGW65Us=", "owner": "cachix", "repo": "devenv", - "rev": "d5e9138bae90fe199fbe5de7675014d76d28873b", + "rev": "57f7e43b3c8adb43a7f3df0d393a87e6939d5141", "type": "github" }, "original": { From 0ab40d02a4869f7fa739e5c59e4ae3ced434fc30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 09:39:17 +0800 Subject: [PATCH 0010/1190] chore(deps): bump @sentry/node from 10.55.0 to 10.56.0 (#22179) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.55.0 to 10.56.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.55.0...10.56.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.56.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 49 +++++++++++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 942305e2a7c6..96942334f4c3 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@opentelemetry/semantic-conventions": "1.41.1", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.10.20", - "@sentry/node": "10.55.0", + "@sentry/node": "10.56.0", "aes-js": "3.1.2", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1440481d931f..a6274638bba8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,8 +83,8 @@ importers: specifier: 0.10.20 version: 0.10.20(hono@4.12.23) '@sentry/node': - specifier: 10.55.0 - version: 10.55.0(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1)) + specifier: 10.56.0 + version: 10.56.0(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1)) aes-js: specifier: 3.1.2 version: 3.1.2 @@ -2533,12 +2533,16 @@ packages: peerDependencies: selderee: ~0.12.0 - '@sentry/core@10.55.0': - resolution: {integrity: sha512-XUyoNtDSYCvgJnoNzlh+YeAXfIPhCRIXbhWqqM3GQ3AFtZICi85lkyfsrwXEl9wzlPGYnU+Eg8F4tOfScx+FcQ==} + '@sentry-internal/server-utils@10.56.0': + resolution: {integrity: sha512-6kuZI/vAjyVKMm1cTzc2pdUmVR4Px4etMG6wnCPyFnwEaGbUKQnTynUBFpTuo/q6Js6QBQvhLNoAnO4YsOfW4w==} engines: {node: '>=18'} - '@sentry/node-core@10.55.0': - resolution: {integrity: sha512-M8XMMIk9Y0PGZoEt37Oe5dQCdqDdJlBcwLXidpz/s5k4QtJvCO/BbtcivcuKI2htw5FwxJkSrHUzRvT36tlDpg==} + '@sentry/core@10.56.0': + resolution: {integrity: sha512-L+u1dIz5SANrmST5jhIwETtt4apILgKrylv12X4hKJU0PvZl+NorjeV/ty3MwzpKQPg6b6q6qMOSLc1rLpy3iQ==} + engines: {node: '>=18'} + + '@sentry/node-core@10.56.0': + resolution: {integrity: sha512-61lD2Wjtv5Lw2F3lJarcD0ORjR4GlVxrEd6w6Of/uF3DH73dD6K3n/3wXEeCIRfV/kgiCFIrCIq76nz0LVgE5g==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2561,12 +2565,12 @@ packages: '@opentelemetry/semantic-conventions': optional: true - '@sentry/node@10.55.0': - resolution: {integrity: sha512-+fB/ByoHVWPLGgoafYciiMatTNyX1FHj1bsqZBN+Pw3McbuEU1nwCPLt9zuyZZiWlQtXKsyuACS4ZhXnID5l8A==} + '@sentry/node@10.56.0': + resolution: {integrity: sha512-qvgtXHkcR4CH3fh0VEVyw4Ysc6MMiAnm727NdTTm0yU5e53erCeo2521+yfJkqmRTGiOSgwA7B5Bs+ot9j0vFQ==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.55.0': - resolution: {integrity: sha512-0+YrNmVNrttki4rWP4DW+UTt5MziepwDLNBde39tgc3cGCcy5fLSdDfhb4JfTaE5TXt4kd5XrkgvS/sDgm3RZg==} + '@sentry/opentelemetry@10.56.0': + resolution: {integrity: sha512-PtMudApHMHvttjos3b7JZ2gJ+nstHAOYE3vKPYB5o0WQO95ldiaYnpLKMCRIGZWF3Dk7ynrqqnBpn8LZLt+Mrg==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -7600,12 +7604,16 @@ snapshots: domhandler: 5.0.3 selderee: 0.12.0 - '@sentry/core@10.55.0': {} + '@sentry-internal/server-utils@10.56.0': + dependencies: + '@sentry/core': 10.56.0 + + '@sentry/core@10.56.0': {} - '@sentry/node-core@10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + '@sentry/node-core@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': dependencies: - '@sentry/core': 10.55.0 - '@sentry/opentelemetry': 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/core': 10.56.0 + '@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) import-in-the-middle: 3.0.1 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -7615,28 +7623,29 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@sentry/node@10.55.0(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.56.0(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@sentry/core': 10.55.0 - '@sentry/node-core': 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) - '@sentry/opentelemetry': 10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry-internal/server-utils': 10.56.0 + '@sentry/core': 10.56.0 + '@sentry/node-core': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.218.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/opentelemetry': 10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) import-in-the-middle: 3.0.1 transitivePeerDependencies: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.55.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + '@sentry/opentelemetry@10.56.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@sentry/core': 10.55.0 + '@sentry/core': 10.56.0 '@sindresorhus/is@4.6.0': {} From 42e99cb1c5ddf08563c8836cdf30fe9b69964b08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:00:47 +0800 Subject: [PATCH 0011/1190] chore(deps): bump undici from 7.25.0 to 8.3.0 (#22032) Bumps [undici](https://github.com/nodejs/undici) from 7.25.0 to 8.3.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.25.0...v8.3.0) --- updated-dependencies: - dependency-name: undici dependency-version: 8.3.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 96942334f4c3..b659c748d1dc 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "tsx": "4.22.4", "twitter-api-v2": "1.29.0", "ufo": "1.6.4", - "undici": "7.25.0", + "undici": "8.3.0", "uuid": "14.0.0", "winston": "3.19.0", "xxhash-wasm": "1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6274638bba8..1f49f909bd1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,7 +138,7 @@ importers: version: 10.0.0 http-cookie-agent: specifier: 8.0.0 - version: 8.0.0(tough-cookie@6.0.1)(undici@7.25.0) + version: 8.0.0(tough-cookie@6.0.1)(undici@8.3.0) https-proxy-agent: specifier: 9.0.0 version: 9.0.0 @@ -260,8 +260,8 @@ importers: specifier: 1.6.4 version: 1.6.4 undici: - specifier: 7.25.0 - version: 7.25.0 + specifier: 8.3.0 + version: 8.3.0 uuid: specifier: 14.0.0 version: 14.0.0 @@ -427,7 +427,7 @@ importers: version: 2.13.4(@types/node@25.9.1)(typescript@5.9.3) node-network-devtools: specifier: 1.0.30 - version: 1.0.30(undici@7.25.0)(utf-8-validate@5.0.10) + version: 1.0.30(undici@8.3.0)(utf-8-validate@5.0.10) oxfmt: specifier: 0.53.0 version: 0.53.0 @@ -5691,8 +5691,8 @@ packages: undici-types@7.25.0: resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} - undici@6.25.0: - resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} + undici@6.26.0: + resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} engines: {node: '>=18.17'} undici@7.24.8: @@ -5703,6 +5703,10 @@ packages: resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} engines: {node: '>=20.18.1'} + undici@8.3.0: + resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==} + engines: {node: '>=22.19.0'} + unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -6142,17 +6146,17 @@ snapshots: '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) '@octokit/request': 10.0.8 '@octokit/request-error': 7.1.0 - undici: 6.25.0 + undici: 6.26.0 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.25.0 + undici: 6.26.0 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.25.0 + undici: 6.26.0 '@actions/io@3.0.2': {} @@ -9244,12 +9248,12 @@ snapshots: http-cache-semantics@4.2.0: {} - http-cookie-agent@8.0.0(tough-cookie@6.0.1)(undici@7.25.0): + http-cookie-agent@8.0.0(tough-cookie@6.0.1)(undici@8.3.0): dependencies: agent-base: 9.0.0 tough-cookie: 6.0.1 optionalDependencies: - undici: 7.25.0 + undici: 8.3.0 http-proxy-agent@9.0.0: dependencies: @@ -10159,13 +10163,13 @@ snapshots: dependencies: write-file-atomic: 1.3.4 - node-network-devtools@1.0.30(undici@7.25.0)(utf-8-validate@5.0.10): + node-network-devtools@1.0.30(undici@8.3.0)(utf-8-validate@5.0.10): dependencies: bufferutil: 4.1.0 iconv-lite: 0.7.2 inspector: 0.5.0 open: 8.4.2 - undici: 7.25.0 + undici: 8.3.0 ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -11257,12 +11261,14 @@ snapshots: undici-types@7.25.0: {} - undici@6.25.0: {} + undici@6.26.0: {} undici@7.24.8: {} undici@7.25.0: {} + undici@8.3.0: {} + unenv@2.0.0-rc.24: dependencies: pathe: 2.0.3 From 6c037dd734e5b94cebc9c9ba445fe65733efe3fc Mon Sep 17 00:00:00 2001 From: Andvari <31068367+dzx-dzx@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:15:03 +0800 Subject: [PATCH 0012/1190] fix(route/apnews): Change default path for mobile API (#22105) * fix(route/apnews): Change default path for mobile API * Update mobile-api.ts * Update mobile-api.ts --- lib/routes/apnews/mobile-api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/routes/apnews/mobile-api.ts b/lib/routes/apnews/mobile-api.ts index 3c894ca3a36e..ac182d98c7bc 100644 --- a/lib/routes/apnews/mobile-api.ts +++ b/lib/routes/apnews/mobile-api.ts @@ -10,12 +10,12 @@ import { fetchArticle } from './utils'; export const route: Route = { path: '/mobile/:path{.+}?', categories: ['traditional-media'], - example: '/apnews/mobile/ap-top-news', + example: '/apnews/mobile', view: ViewType.Articles, parameters: { path: { description: 'Corresponding path from AP News website', - default: 'ap-top-news', + default: '/', }, }, features: { @@ -37,7 +37,7 @@ export const route: Route = { }; async function handler(ctx) { - const path = ctx.req.param('path') ? `/${ctx.req.param('path')}` : '/hub/ap-top-news'; + const path = ctx.req.param('path') ? `/${ctx.req.param('path')}` : '/'; const apiRootUrl = 'https://apnews.com/graphql/delivery/ap/v1'; const res = await ofetch(apiRootUrl, { query: { From 3173ff1d5c93869df805676e2f7526dab5c654c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:23:09 +0800 Subject: [PATCH 0013/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22182) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 4.20260603.1 to 4.20260604.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 4.20260604.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 236 ++++++++++++++++++++++++------------------------- 2 files changed, 119 insertions(+), 119 deletions(-) diff --git a/package.json b/package.json index b659c748d1dc..109a1564f554 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.6", "@cloudflare/playwright": "1.3.0", - "@cloudflare/workers-types": "4.20260603.1", + "@cloudflare/workers-types": "4.20260604.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.68.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f49f909bd1d..b536ceaf922e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -297,8 +297,8 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/workers-types': - specifier: 4.20260603.1 - version: 4.20260603.1 + specifier: 4.20260604.1 + version: 4.20260604.1 '@eslint/eslintrc': specifier: 3.3.5 version: 3.3.5 @@ -367,7 +367,7 @@ importers: version: 8.60.1(eslint@10.4.1(jiti@2.6.1))(typescript@5.9.3) '@vercel/nft': specifier: 1.10.2 - version: 1.10.2(rollup@4.61.0) + version: 1.10.2(rollup@4.61.1) '@vitest/coverage-v8': specifier: 4.1.8 version: 4.1.8(vitest@4.1.8) @@ -469,7 +469,7 @@ importers: version: 4.1.8(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.13.4(@types/node@25.9.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)) wrangler: specifier: 4.97.0 - version: 4.97.0(@cloudflare/workers-types@4.20260603.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.97.0(@cloudflare/workers-types@4.20260604.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.0.0 version: 2.0.0 @@ -654,8 +654,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260603.1': - resolution: {integrity: sha512-TLeVHoBbcYv35S5TdRWUoj3IJ56BhHtrsuci+O7ithU8yz7ttNdCk6rAl1QUSGNVEWSIp54bWOuV/xmX1zu79g==} + '@cloudflare/workers-types@4.20260604.1': + resolution: {integrity: sha512-nVTydUwPcz9WfwZ/6xAmqs8uUVeGbDVmyPvSYrYAo4CQTyi0122SoE1Suw8RvqnN60XXHPChAjDqSIjnr/mbmg==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -2349,141 +2349,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.61.0': - resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==} + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.61.0': - resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==} + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.61.0': - resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==} + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.61.0': - resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==} + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.61.0': - resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==} + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.61.0': - resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==} + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': - resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==} + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.61.0': - resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==} + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.61.0': - resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==} + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.61.0': - resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==} + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.61.0': - resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==} + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.61.0': - resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==} + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.61.0': - resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==} + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.61.0': - resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==} + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.61.0': - resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==} + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.61.0': - resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==} + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.61.0': - resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==} + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.61.0': - resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==} + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.61.0': - resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==} + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.61.0': - resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==} + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.61.0': - resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==} + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.61.0': - resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==} + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.61.0': - resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==} + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.61.0': - resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==} + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.61.0': - resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==} + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} cpu: [x64] os: [win32] @@ -3398,7 +3398,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.48: @@ -5193,8 +5193,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.61.0: - resolution: {integrity: sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==} + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6307,7 +6307,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260601.1': optional: true - '@cloudflare/workers-types@4.20260603.1': {} + '@cloudflare/workers-types@4.20260604.1': {} '@colors/colors@1.6.0': {} @@ -7473,87 +7473,87 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.3.0(rollup@4.61.0)': + '@rollup/pluginutils@5.3.0(rollup@4.61.1)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.4 optionalDependencies: - rollup: 4.61.0 + rollup: 4.61.1 - '@rollup/rollup-android-arm-eabi@4.61.0': + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true - '@rollup/rollup-android-arm64@4.61.0': + '@rollup/rollup-android-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-arm64@4.61.0': + '@rollup/rollup-darwin-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-x64@4.61.0': + '@rollup/rollup-darwin-x64@4.61.1': optional: true - '@rollup/rollup-freebsd-arm64@4.61.0': + '@rollup/rollup-freebsd-arm64@4.61.1': optional: true - '@rollup/rollup-freebsd-x64@4.61.0': + '@rollup/rollup-freebsd-x64@4.61.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.61.0': + '@rollup/rollup-linux-arm-musleabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.61.0': + '@rollup/rollup-linux-arm64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.61.0': + '@rollup/rollup-linux-arm64-musl@4.61.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.61.0': + '@rollup/rollup-linux-loong64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.61.0': + '@rollup/rollup-linux-loong64-musl@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.61.0': + '@rollup/rollup-linux-ppc64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.61.0': + '@rollup/rollup-linux-ppc64-musl@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.61.0': + '@rollup/rollup-linux-riscv64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.61.0': + '@rollup/rollup-linux-riscv64-musl@4.61.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.61.0': + '@rollup/rollup-linux-s390x-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.61.0': + '@rollup/rollup-linux-x64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-musl@4.61.0': + '@rollup/rollup-linux-x64-musl@4.61.1': optional: true - '@rollup/rollup-openbsd-x64@4.61.0': + '@rollup/rollup-openbsd-x64@4.61.1': optional: true - '@rollup/rollup-openharmony-arm64@4.61.0': + '@rollup/rollup-openharmony-arm64@4.61.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.61.0': + '@rollup/rollup-win32-arm64-msvc@4.61.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.61.0': + '@rollup/rollup-win32-ia32-msvc@4.61.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.61.0': + '@rollup/rollup-win32-x64-gnu@4.61.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.61.0': + '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true '@rss3/api-core@0.0.25': @@ -7937,10 +7937,10 @@ snapshots: '@typescript-eslint/types': 8.60.1 eslint-visitor-keys: 5.0.1 - '@vercel/nft@1.10.2(rollup@4.61.0)': + '@vercel/nft@1.10.2(rollup@4.61.1)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.3.0(rollup@4.61.0) + '@rollup/pluginutils': 5.3.0(rollup@4.61.1) acorn: 8.16.0 acorn-import-attributes: 1.9.5(acorn@8.16.0) async-sema: 3.1.1 @@ -10758,35 +10758,35 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 - rollup@4.61.0: + rollup@4.61.1: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.61.0 - '@rollup/rollup-android-arm64': 4.61.0 - '@rollup/rollup-darwin-arm64': 4.61.0 - '@rollup/rollup-darwin-x64': 4.61.0 - '@rollup/rollup-freebsd-arm64': 4.61.0 - '@rollup/rollup-freebsd-x64': 4.61.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.61.0 - '@rollup/rollup-linux-arm-musleabihf': 4.61.0 - '@rollup/rollup-linux-arm64-gnu': 4.61.0 - '@rollup/rollup-linux-arm64-musl': 4.61.0 - '@rollup/rollup-linux-loong64-gnu': 4.61.0 - '@rollup/rollup-linux-loong64-musl': 4.61.0 - '@rollup/rollup-linux-ppc64-gnu': 4.61.0 - '@rollup/rollup-linux-ppc64-musl': 4.61.0 - '@rollup/rollup-linux-riscv64-gnu': 4.61.0 - '@rollup/rollup-linux-riscv64-musl': 4.61.0 - '@rollup/rollup-linux-s390x-gnu': 4.61.0 - '@rollup/rollup-linux-x64-gnu': 4.61.0 - '@rollup/rollup-linux-x64-musl': 4.61.0 - '@rollup/rollup-openbsd-x64': 4.61.0 - '@rollup/rollup-openharmony-arm64': 4.61.0 - '@rollup/rollup-win32-arm64-msvc': 4.61.0 - '@rollup/rollup-win32-ia32-msvc': 4.61.0 - '@rollup/rollup-win32-x64-gnu': 4.61.0 - '@rollup/rollup-win32-x64-msvc': 4.61.0 + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -11406,7 +11406,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.61.0 + rollup: 4.61.1 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 25.9.1 @@ -11527,7 +11527,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260601.1 '@cloudflare/workerd-windows-64': 1.20260601.1 - wrangler@4.97.0(@cloudflare/workers-types@4.20260603.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.97.0(@cloudflare/workers-types@4.20260604.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260601.1) @@ -11538,7 +11538,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260601.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260603.1 + '@cloudflare/workers-types': 4.20260604.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 4eeb5ae4ec0172d0e50722d654c79baeb9dd1628 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:33:21 +0800 Subject: [PATCH 0014/1190] chore(deps): bump devenv from `57f7e43` to `f693b47` (#22183) Bumps [devenv](https://github.com/cachix/devenv) from `57f7e43` to `f693b47`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/57f7e43b3c8adb43a7f3df0d393a87e6939d5141...f693b472c731e7dda69402daa88c06369d54fd3a) --- updated-dependencies: - dependency-name: devenv dependency-version: f693b472c731e7dda69402daa88c06369d54fd3a dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 0570de84d449..fd1d053b5677 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1780511534, - "narHash": "sha256-AFc1M/svHCsGWO+tA/t7ZeWckFIuJpML0vJpxGW65Us=", + "lastModified": 1780543372, + "narHash": "sha256-FCGxk82Lc4koWcFw5xgr+W5vbwLVFLCnSMwm2gQOgr0=", "owner": "cachix", "repo": "devenv", - "rev": "57f7e43b3c8adb43a7f3df0d393a87e6939d5141", + "rev": "f693b472c731e7dda69402daa88c06369d54fd3a", "type": "github" }, "original": { From 6135a6db71f55abf39c2d973a561eb789b8e8707 Mon Sep 17 00:00:00 2001 From: Andvari <31068367+dzx-dzx@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:50:02 +0800 Subject: [PATCH 0015/1190] fix(route/apnews): Adapt to new author format (#22184) --- lib/routes/apnews/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/apnews/utils.ts b/lib/routes/apnews/utils.ts index 083c19fb9aa7..6ccca1098359 100644 --- a/lib/routes/apnews/utils.ts +++ b/lib/routes/apnews/utils.ts @@ -45,7 +45,7 @@ export function fetchArticle(item) { description: $('div.RichTextStoryBody').html() || $(':is(.VideoLead, .VideoPage-pageSubHeading)').html(), category: [...(section ? [section] : []), ...(ldjson.keywords ?? [])], guid: $("meta[name='brightspot.contentId']").attr('content'), - author: ldjson.author?.map((e) => e.mainEntity), + author: ldjson.author, }; } else { // Live From 3567ced4d2a6646311f5f8667933689a2acc63ba Mon Sep 17 00:00:00 2001 From: Cod1doc Date: Fri, 5 Jun 2026 03:51:04 +0800 Subject: [PATCH 0016/1190] fix: transform json number item link (#22168) * fix: transform json number item link * docs(route/rsshub): use concrete json transform example * Revert "docs(route/rsshub): use concrete json transform example" This reverts commit e4d916451afa2207959312d999dfbc6c71919e55. --------- Co-authored-by: jack Co-authored-by: jack --- lib/routes/rsshub/transform/json.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/rsshub/transform/json.ts b/lib/routes/rsshub/transform/json.ts index 0786c8f172ae..c39e979e4f95 100644 --- a/lib/routes/rsshub/transform/json.ts +++ b/lib/routes/rsshub/transform/json.ts @@ -93,7 +93,7 @@ async function handler(ctx) { } const items = jsonGet(response.data, routeParams.get('item')).map((item) => { - let link = jsonGet(item, routeParams.get('itemLink')).trim(); + let link = String(jsonGet(item, routeParams.get('itemLink')) ?? '').trim(); const linkPrefix = routeParams.get('itemLinkPrefix'); if (link && linkPrefix) { From 08c3ba83f9dae83c2b16a24fce8519994dae6a99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:12:37 +0000 Subject: [PATCH 0017/1190] chore(deps): bump ioredis from 5.11.0 to 5.11.1 (#22191) Bumps [ioredis](https://github.com/luin/ioredis) from 5.11.0 to 5.11.1. - [Release notes](https://github.com/luin/ioredis/releases) - [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md) - [Commits](https://github.com/luin/ioredis/compare/v5.11.0...v5.11.1) --- updated-dependencies: - dependency-name: ioredis dependency-version: 5.11.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 109a1564f554..e212440bb395 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "iconv-lite": "0.7.2", "imapflow": "1.3.5", "instagram-private-api": "1.46.1", - "ioredis": "5.11.0", + "ioredis": "5.11.1", "ip-regex": "5.0.0", "jsdom": "29.1.1", "json-bigint": "1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b536ceaf922e..eb3c5febb771 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,8 +152,8 @@ importers: specifier: 1.46.1 version: 1.46.1 ioredis: - specifier: 5.11.0 - version: 5.11.0 + specifier: 5.11.1 + version: 5.11.1 ip-regex: specifier: 5.0.0 version: 5.0.0 @@ -3398,7 +3398,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.48: @@ -4101,8 +4101,8 @@ packages: re2: optional: true - ioredis@5.11.0: - resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==} + ioredis@5.11.1: + resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==} engines: {node: '>=12.22.0'} ip-address@10.2.0: @@ -9378,7 +9378,7 @@ snapshots: transitivePeerDependencies: - supports-color - ioredis@5.11.0: + ioredis@5.11.1: dependencies: '@ioredis/commands': 1.10.0 cluster-key-slot: 1.1.1 From 7ab88a5059e7b031712fc45c1ab334fcdfe9f48d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:15:10 +0000 Subject: [PATCH 0018/1190] chore(deps-dev): bump tsdown from 0.22.1 to 0.22.2 (#22192) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.1 to 0.22.2. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.1...v0.22.2) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 246 ++++++++++++++++++++++--------------------------- 2 files changed, 112 insertions(+), 136 deletions(-) diff --git a/package.json b/package.json index e212440bb395..716836e24639 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.1", + "tsdown": "0.22.2", "typescript": "5.9.3", "unified": "11.0.5", "vite-tsconfig-paths": "6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb3c5febb771..7afdb4e7851f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -453,8 +453,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.1 - version: 0.22.1(tsx@4.22.4)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)) + specifier: 0.22.2 + version: 0.22.2(tsx@4.22.4)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)) typescript: specifier: 5.9.3 version: 5.9.3 @@ -518,8 +518,8 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/generator@8.0.0-rc.5': - resolution: {integrity: sha512-nFZPWz3FHIS7y6rMIVoa/WBwjdutfIaRJIBQjzn+t3RnecZoRNlGmGcyR2wb0T/IgSd50Kz/6dG8/LvMCRunjg==} + '@babel/generator@8.0.0-rc.6': + resolution: {integrity: sha512-6mIzgVK8DgEzvIapoQwhXTMnnkuE4STQmVv9H03i/tZ2ml8oev3TRvZJgTenK2Bsq0YWNtzOrFdTyNzCMFtjJQ==} engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-string-parser@7.29.7': @@ -538,10 +538,6 @@ packages: resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@8.0.0-rc.5': - resolution: {integrity: sha512-ehJDxHvtbZ85RtX/L2fi0h9AGsBNqB5Euv1EB8RMAvGYvD+2X+QbpzzOpbklnNXO+WSZJNOaetw2BBj27xsWVg==} - engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-validator-identifier@8.0.0-rc.6': resolution: {integrity: sha512-nVJ+1JcCgntv8d78rRo++o2wuODT0Irknx2BF8Np4Ft2CRgjLqIs4qzSZ8b66yGbBdMWGmZBO9WEZv1hhNiSpg==} engines: {node: ^22.18.0 || >=24.11.0} @@ -551,11 +547,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@8.0.0-rc.4': - resolution: {integrity: sha512-0S/1yefMa15N4i2v3t8Fw9pgMHhf2gF6Lc1UEXI96Ls6FNAjqvHHZouZ2ZS/deqLhbMFtmfVeFac6iTsvFbLwA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - '@babel/parser@8.0.0-rc.6': resolution: {integrity: sha512-rOS8IpdO7mQELkTPlCsTgPejO0bFuZdEDCGQJouYbYf9e1FLTym7Fei2pEjq8q7MWbX0ravcd7QQYKs1TxOuog==} engines: {node: ^22.18.0 || >=24.11.0} @@ -1796,8 +1787,8 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxc-project/types@0.133.0': - resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@oxc-project/types@0.134.0': + resolution: {integrity: sha512-T0xuRRKrQFmocH8y+jGfpmSkGcheaJExY9lEihmR1Gm2aH+75B8CzgU2rABRQSzzDxLjZ15Sc0bRVLj5lVeNXQ==} '@oxfmt/binding-android-arm-eabi@0.53.0': resolution: {integrity: sha512-XfVM8AmIovBTKXCt14Op5wbfcoM8418nttd+nhMgM3RAVaJg1MtJc73FyWfUt0oxLyBGVwfniNVUsbV/b3VmPg==} @@ -2150,8 +2141,8 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.0.3': - resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + '@rolldown/binding-android-arm64@1.1.0': + resolution: {integrity: sha512-gCYzGOSkYY6Z034suzd20euvds7lPzMEEla62DJGE/ZAlR4OMBnNbvnBSsIGUCAr52gaWMsloGxP4tVGtN5aCA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -2162,8 +2153,8 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.0.3': - resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + '@rolldown/binding-darwin-arm64@1.1.0': + resolution: {integrity: sha512-JQBD77MNgu+4Z6RAyg69acugdrhhVoWesr3l47zohYZ2YV2fwkWMArkN/2p4l6Ei+Sno7W5q+UsKdVWq5Ens0w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -2174,8 +2165,8 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.3': - resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + '@rolldown/binding-darwin-x64@1.1.0': + resolution: {integrity: sha512-p/8cXUTK4Sob604e+xxPhVSbDFf29E6J0l/xESM9rdCfn3aDai3nEs6TnMHUsdD5aNlFz0+gDbiGlozLKGa2YA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -2186,8 +2177,8 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.0.3': - resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + '@rolldown/binding-freebsd-x64@1.1.0': + resolution: {integrity: sha512-KbtOSlVv6fElujiZWMcC3aQYhEwLVVf073RcwlSmpGQvIsKZFUqc0ef4sjUuurRwfbiI6JJXji9DQn+86hawmQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -2198,8 +2189,8 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.0.3': - resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + '@rolldown/binding-linux-arm-gnueabihf@1.1.0': + resolution: {integrity: sha512-9fZ9i0o0/MQaw7om6Z6TsT7tfCk0jtbEFtC+aPqZL5RNsGWNcHvn6EHgL3dAprjq+AZzPTAQjg2JtpJaMt+6pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -2211,8 +2202,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.0.3': - resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + '@rolldown/binding-linux-arm64-gnu@1.1.0': + resolution: {integrity: sha512-+tog7T66i+yFyIuuAnjL6xmW182W/qTBOUt6BtQ6lBIM1Eikh/fSMz4HGgvuCp5uU0zuIVWng7kDYthjCMOHcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2225,8 +2216,8 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.0.3': - resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + '@rolldown/binding-linux-arm64-musl@1.1.0': + resolution: {integrity: sha512-4b7yruLIIj/oZ3GpcLOvxcLCLDMraohn3IhQfN2hBP4w9UekG0DTIajWguJosRGfySf/+h/NwRUiMKoCpxCrqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2239,8 +2230,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-ppc64-gnu@1.0.3': - resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + '@rolldown/binding-linux-ppc64-gnu@1.1.0': + resolution: {integrity: sha512-QRDOVZd0bhQ5jLsUsCC3dUxDWdTSVY9WMznowZgCGOrZfLLgctWpelhUASEiBwsXfat/JwYnVd1EaxMhqyT+UQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -2253,8 +2244,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.3': - resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + '@rolldown/binding-linux-s390x-gnu@1.1.0': + resolution: {integrity: sha512-ypxT+Hq76NFG7woFbNbySnGEajFuYuIXeKz/jfCU+lXUoxfi3zLE6OG/ZQNeK3RpZSYJlAe2bokpsQ046CaieQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -2267,8 +2258,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.3': - resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + '@rolldown/binding-linux-x64-gnu@1.1.0': + resolution: {integrity: sha512-IdovCmfROFmpTLahdecTDFL74aLERVYN68F/mLZjfVh6LfoplPfI6deyHNMTcVujbokDV5k05XrFO22zfv+qjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2281,8 +2272,8 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-musl@1.0.3': - resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + '@rolldown/binding-linux-x64-musl@1.1.0': + resolution: {integrity: sha512-pcA8xlFp2tyk9T2R6Fi/rPe3bQ1MA+sSMDNUU5Ogu80GHOatkE4P8YCreGAvZErm5Ho2YRXnyvNrWiRncfVysQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2294,8 +2285,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.0.3': - resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + '@rolldown/binding-openharmony-arm64@1.1.0': + resolution: {integrity: sha512-4+fexHayrLCWpriPh4c6dNvL4an34DEZCG7zOM/FD5QNF6h8DT+bDXzyB/kfC8lDJbaFb7jKShtnjDQFXVQEjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -2305,8 +2296,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.0.3': - resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + '@rolldown/binding-wasm32-wasi@1.1.0': + resolution: {integrity: sha512-SbL++MNmOw6QamrwIGDMSSfM4ceTzFr+RjbOExJSLLBinScU4WI5OdA413h1qwPw2yH7lVF1+H4svQ+6mSXKTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] @@ -2316,8 +2307,8 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.0.3': - resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + '@rolldown/binding-win32-arm64-msvc@1.1.0': + resolution: {integrity: sha512-+xTE6XC7wBgk0VKRXGG+QAnyW5S9b8vfsFpiMjf0waQTmSQSU8onsH/beyZ8X4aXVveJnotiy7VDjLOaW8bTrg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -2328,8 +2319,8 @@ packages: cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.3': - resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + '@rolldown/binding-win32-x64-msvc@1.1.0': + resolution: {integrity: sha512-Ogji1TQNqH3ACLnYr+1Ns1nyrJ0CO2P585u9Hsh02pXvtFiFpgtgT2b3P4PnCOU86VVCvqtAeCN4OftMT8KU4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2934,8 +2925,8 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - ansis@4.3.0: - resolution: {integrity: sha512-44mvgtPvohuU/70DdY5Oz2AIrLJ9k6/5x4KmoSvPwO+5Moijo0+N9D0fKbbYZQWP1hNm5CpOf+E01jhxG/r8xg==} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} arg@5.0.2: @@ -4757,6 +4748,10 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -5164,8 +5159,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown-plugin-dts@0.25.1: - resolution: {integrity: sha512-zK82aC/8z1iVW+g0bCnlQZq04Y5bNeL/RcRwTYBwsnU6wH0N+6vpIFkN7JC0kYRS5qKA+pxQyfIPvXJ6Q5xSpQ==} + rolldown-plugin-dts@0.25.2: + resolution: {integrity: sha512-nMhN/R+vmR8GM45ZW1FWMSjRTSDDn/6w4GTf8RNrEFCBdl8B1kySWrU1ixPtbwzXoRlcO+R/S88VgXuJQwfdDg==} engines: {node: ^22.18.0 || >=24.0.0} peerDependencies: '@ts-macro/tsc': ^0.3.6 @@ -5188,8 +5183,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.0.3: - resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + rolldown@1.1.0: + resolution: {integrity: sha512-zpMvlJhs5PkXRTtKc0CaLBVI9AR/VDiJFpM+kx//hgToEca7FgMlGjaRIisXBcb19T76LswgmKECSQ96hjWr5A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -5228,8 +5223,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.8.1: - resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + semver@7.8.2: + resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} engines: {node: '>=10'} hasBin: true @@ -5454,18 +5449,10 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.2.2: - resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} - engines: {node: '>=18'} - tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -5568,14 +5555,14 @@ packages: typescript: optional: true - tsdown@0.22.1: - resolution: {integrity: sha512-Ldx1jLyDFEzsN/fMBi2TBVaZe4fuEJhIiHjQhX0pV7oa5uYz5Imdivs5mNzEXOrMEtFRR6C9BQ2YqLoroffB+Q==} + tsdown@0.22.2: + resolution: {integrity: sha512-VX9gsyKXsTnBZjnIM4jsHl9aRv+GfgkE/k1hQslilaBfZMlaw3JuGR+6yhiU0QxWBtOCDnTjwOSoXzgB7Rr50g==} engines: {node: ^22.18.0 || >=24.0.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.1 - '@tsdown/exe': 0.22.1 + '@tsdown/css': 0.22.2 + '@tsdown/exe': 0.22.2 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6191,7 +6178,7 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/generator@8.0.0-rc.5': + '@babel/generator@8.0.0-rc.6': dependencies: '@babel/parser': 8.0.0-rc.6 '@babel/types': 8.0.0-rc.6 @@ -6208,18 +6195,12 @@ snapshots: '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-identifier@8.0.0-rc.5': {} - '@babel/helper-validator-identifier@8.0.0-rc.6': {} '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 - '@babel/parser@8.0.0-rc.4': - dependencies: - '@babel/types': 8.0.0-rc.6 - '@babel/parser@8.0.0-rc.6': dependencies: '@babel/types': 8.0.0-rc.6 @@ -6947,7 +6928,7 @@ snapshots: https-proxy-agent: 7.0.6 node-fetch: 2.7.0 nopt: 8.1.0 - semver: 7.8.1 + semver: 7.8.2 tar: 7.5.15 transitivePeerDependencies: - encoding @@ -7168,7 +7149,7 @@ snapshots: '@oxc-project/types@0.127.0': optional: true - '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.134.0': {} '@oxfmt/binding-android-arm-eabi@0.53.0': optional: true @@ -7373,73 +7354,73 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-android-arm64@1.0.3': + '@rolldown/binding-android-arm64@1.1.0': optional: true '@rolldown/binding-darwin-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-arm64@1.0.3': + '@rolldown/binding-darwin-arm64@1.1.0': optional: true '@rolldown/binding-darwin-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-x64@1.0.3': + '@rolldown/binding-darwin-x64@1.1.0': optional: true '@rolldown/binding-freebsd-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-freebsd-x64@1.0.3': + '@rolldown/binding-freebsd-x64@1.1.0': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + '@rolldown/binding-linux-arm-gnueabihf@1.1.0': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.3': + '@rolldown/binding-linux-arm64-gnu@1.1.0': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.3': + '@rolldown/binding-linux-arm64-musl@1.1.0': optional: true '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.3': + '@rolldown/binding-linux-ppc64-gnu@1.1.0': optional: true '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.3': + '@rolldown/binding-linux-s390x-gnu@1.1.0': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.3': + '@rolldown/binding-linux-x64-gnu@1.1.0': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-musl@1.0.3': + '@rolldown/binding-linux-x64-musl@1.1.0': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-openharmony-arm64@1.0.3': + '@rolldown/binding-openharmony-arm64@1.1.0': optional: true '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': @@ -7449,7 +7430,7 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-wasm32-wasi@1.0.3': + '@rolldown/binding-wasm32-wasi@1.1.0': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 @@ -7459,13 +7440,13 @@ snapshots: '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.3': + '@rolldown/binding-win32-arm64-msvc@1.1.0': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.3': + '@rolldown/binding-win32-x64-msvc@1.1.0': optional: true '@rolldown/pluginutils@1.0.0-rc.17': @@ -7914,7 +7895,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.60.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.1 + semver: 7.8.2 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -8068,7 +8049,7 @@ snapshots: ansi-styles@6.2.3: {} - ansis@4.3.0: {} + ansis@4.3.1: {} arg@5.0.2: {} @@ -8084,7 +8065,7 @@ snapshots: ast-kit@3.0.0-beta.1: dependencies: - '@babel/parser': 8.0.0-rc.4 + '@babel/parser': 8.0.0-rc.6 estree-walker: 3.0.3 pathe: 2.0.3 @@ -8550,7 +8531,7 @@ snapshots: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.9 - semver: 7.8.1 + semver: 7.8.2 electron-to-chromium@1.5.330: {} @@ -8727,7 +8708,7 @@ snapshots: eslint-compat-utils@0.5.1(eslint@10.4.1(jiti@2.6.1)): dependencies: eslint: 10.4.1(jiti@2.6.1) - semver: 7.7.4 + semver: 7.8.2 eslint-filtered-fix@0.3.0(eslint@10.4.1(jiti@2.6.1)): dependencies: @@ -9710,7 +9691,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.1 + semver: 7.8.2 map-obj@4.3.0: {} @@ -10209,6 +10190,8 @@ snapshots: obug@2.1.1: {} + obug@2.1.2: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -10699,17 +10682,17 @@ snapshots: rfdc@1.4.1: {} - rolldown-plugin-dts@0.25.1(rolldown@1.0.3)(typescript@5.9.3): + rolldown-plugin-dts@0.25.2(rolldown@1.1.0)(typescript@5.9.3): dependencies: - '@babel/generator': 8.0.0-rc.5 - '@babel/helper-validator-identifier': 8.0.0-rc.5 - '@babel/parser': 8.0.0-rc.4 + '@babel/generator': 8.0.0-rc.6 + '@babel/helper-validator-identifier': 8.0.0-rc.6 + '@babel/parser': 8.0.0-rc.6 ast-kit: 3.0.0-beta.1 birpc: 4.0.0 dts-resolver: 3.0.0 get-tsconfig: 5.0.0-beta.5 - obug: 2.1.1 - rolldown: 1.0.3 + obug: 2.1.2 + rolldown: 1.1.0 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -10737,26 +10720,26 @@ snapshots: '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 optional: true - rolldown@1.0.3: + rolldown@1.1.0: dependencies: - '@oxc-project/types': 0.133.0 + '@oxc-project/types': 0.134.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.3 - '@rolldown/binding-darwin-arm64': 1.0.3 - '@rolldown/binding-darwin-x64': 1.0.3 - '@rolldown/binding-freebsd-x64': 1.0.3 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 - '@rolldown/binding-linux-arm64-gnu': 1.0.3 - '@rolldown/binding-linux-arm64-musl': 1.0.3 - '@rolldown/binding-linux-ppc64-gnu': 1.0.3 - '@rolldown/binding-linux-s390x-gnu': 1.0.3 - '@rolldown/binding-linux-x64-gnu': 1.0.3 - '@rolldown/binding-linux-x64-musl': 1.0.3 - '@rolldown/binding-openharmony-arm64': 1.0.3 - '@rolldown/binding-wasm32-wasi': 1.0.3 - '@rolldown/binding-win32-arm64-msvc': 1.0.3 - '@rolldown/binding-win32-x64-msvc': 1.0.3 + '@rolldown/binding-android-arm64': 1.1.0 + '@rolldown/binding-darwin-arm64': 1.1.0 + '@rolldown/binding-darwin-x64': 1.1.0 + '@rolldown/binding-freebsd-x64': 1.1.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.0 + '@rolldown/binding-linux-arm64-gnu': 1.1.0 + '@rolldown/binding-linux-arm64-musl': 1.1.0 + '@rolldown/binding-linux-ppc64-gnu': 1.1.0 + '@rolldown/binding-linux-s390x-gnu': 1.1.0 + '@rolldown/binding-linux-x64-gnu': 1.1.0 + '@rolldown/binding-linux-x64-musl': 1.1.0 + '@rolldown/binding-openharmony-arm64': 1.1.0 + '@rolldown/binding-wasm32-wasi': 1.1.0 + '@rolldown/binding-win32-arm64-msvc': 1.1.0 + '@rolldown/binding-win32-x64-msvc': 1.1.0 rollup@4.61.1: dependencies: @@ -10822,7 +10805,7 @@ snapshots: semver@7.7.4: {} - semver@7.8.1: {} + semver@7.8.2: {} set-cookie-parser@3.1.0: {} @@ -10830,7 +10813,7 @@ snapshots: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.1 + semver: 7.8.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -11076,15 +11059,8 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.2.2: {} - tinyexec@1.2.4: {} - tinyglobby@0.2.16: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -11163,21 +11139,21 @@ snapshots: optionalDependencies: typescript: 5.9.3 - tsdown@0.22.1(tsx@4.22.4)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)): + tsdown@0.22.2(tsx@4.22.4)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)): dependencies: - ansis: 4.3.0 + ansis: 4.3.1 cac: 7.0.0 defu: 6.1.7 empathic: 2.0.1 hookable: 6.1.1 import-without-cache: 0.4.0 - obug: 2.1.1 + obug: 2.1.2 picomatch: 4.0.4 - rolldown: 1.0.3 - rolldown-plugin-dts: 0.25.1(rolldown@1.0.3)(typescript@5.9.3) - semver: 7.8.1 - tinyexec: 1.2.2 - tinyglobby: 0.2.16 + rolldown: 1.1.0 + rolldown-plugin-dts: 0.25.2(rolldown@1.1.0)(typescript@5.9.3) + semver: 7.8.2 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 tree-kill: 1.2.2 unconfig-core: 7.5.0 optionalDependencies: From 742f49e61d0ad60f206d7c8762beefc93ca5a345 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:33:48 +0800 Subject: [PATCH 0019/1190] chore(deps-dev): bump the cloudflare group with 3 updates (#22190) Bumps the cloudflare group with 3 updates: [@cloudflare/containers](https://github.com/cloudflare/containers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/containers` from 0.3.6 to 0.3.7 - [Release notes](https://github.com/cloudflare/containers/releases) - [Changelog](https://github.com/cloudflare/containers/blob/main/CHANGELOG.md) - [Commits](https://github.com/cloudflare/containers/compare/v0.3.6...v0.3.7) Updates `@cloudflare/workers-types` from 4.20260604.1 to 4.20260605.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.97.0 to 4.98.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/HEAD/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/containers" dependency-version: 0.3.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 4.20260605.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.98.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 105 ++++++++++++++++++++++++++----------------------- 2 files changed, 59 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 716836e24639..7098c691f57e 100644 --- a/package.json +++ b/package.json @@ -145,9 +145,9 @@ "@actions/core": "3.0.1", "@actions/github": "9.1.1", "@bbob/types": "4.3.1", - "@cloudflare/containers": "0.3.6", + "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", - "@cloudflare/workers-types": "4.20260604.1", + "@cloudflare/workers-types": "4.20260605.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.68.0", @@ -204,7 +204,7 @@ "unified": "11.0.5", "vite-tsconfig-paths": "6.1.1", "vitest": "4.1.8", - "wrangler": "4.97.0", + "wrangler": "4.98.0", "yaml-eslint-parser": "2.0.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7afdb4e7851f..565988640ae9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,14 +291,14 @@ importers: specifier: 4.3.1 version: 4.3.1 '@cloudflare/containers': - specifier: 0.3.6 - version: 0.3.6 + specifier: 0.3.7 + version: 0.3.7 '@cloudflare/playwright': specifier: 1.3.0 version: 1.3.0 '@cloudflare/workers-types': - specifier: 4.20260604.1 - version: 4.20260604.1 + specifier: 4.20260605.1 + version: 4.20260605.1 '@eslint/eslintrc': specifier: 3.3.5 version: 3.3.5 @@ -468,8 +468,8 @@ importers: specifier: 4.1.8 version: 4.1.8(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.13.4(@types/node@25.9.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)) wrangler: - specifier: 4.97.0 - version: 4.97.0(@cloudflare/workers-types@4.20260604.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.98.0 + version: 4.98.0(@cloudflare/workers-types@4.20260605.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.0.0 version: 2.0.0 @@ -596,8 +596,8 @@ packages: '@bufbuild/protobuf@2.11.0': resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==} - '@cloudflare/containers@0.3.6': - resolution: {integrity: sha512-8RrbK/Et165gjvXccui3pgkUuySVWysTC6bJRXfgqmbCA2vAmh8pm7cAKDh2nZFR/GSjW4BgxeKpffCTD8SJEg==} + '@cloudflare/containers@0.3.7': + resolution: {integrity: sha512-DM9dm3FnIBSyiSJ1FLavKwl/lk3oAmTaynCzZQ9pZR0ncRPquSxkxd8Nu2MFILxmDDsPkxKsSNEh9mHHMty4Fw==} '@cloudflare/kv-asset-handler@0.5.0': resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} @@ -615,38 +615,38 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260601.1': - resolution: {integrity: sha512-iXZBVuRbvuVqQ/63wul01hHCv/3R8G5S8zbkjfoHvyPZFynmlKTV59Hk+H8whyGwFAZuB71UJGLr+G5mJKfjWA==} + '@cloudflare/workerd-darwin-64@1.20260603.1': + resolution: {integrity: sha512-cEXDWu6V3ZrpmwWkM4OJE9AeXjdAgOY5rh8EHhcBVCuP5rxnzUbPzLtrVOHx0UUUAcCrFq0Xsa6mZKL1VUZsKQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260601.1': - resolution: {integrity: sha512-veGpZQGBw07Twt+Y4z3oyo+/obKHt0iWUwvDV5GOiDAYjC/zW+YGstgVzg4SHq+k1sLH3ElqL2TXx20I5WBv3Q==} + '@cloudflare/workerd-darwin-arm64@1.20260603.1': + resolution: {integrity: sha512-uBPK4LaWJNbbCYwPnUAehlHbbVulhVZPZsdcAhBPfZhHb3QAuAEPAQepO/P67R3V6Cni4YGx1fLbL8A5wwoaNA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260601.1': - resolution: {integrity: sha512-n/9hDz7fPGpYF0J684+Xr5zgjcS2jdmY2Of5m6e+eQ/M9+RfR+UaU8Ee/tkA1dDC0LYQB13hfPafZG66Ff1CsA==} + '@cloudflare/workerd-linux-64@1.20260603.1': + resolution: {integrity: sha512-ht9l6/8Tk7Rp6kA4S9oFZ4X8u0VjnnFdmU/6B3fnABYKREYTKh2RdOqXqXxcp5eNJseireKnWik/hQOPK1CutQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260601.1': - resolution: {integrity: sha512-VHRZZbexATS+n+1j3x/CZaYbIJEye0J3iIHgG0Wp+l+NrZCKQ8qi8Lq1uTV0dLJQ67FuZtJtWdQ95mm9F7Fc+A==} + '@cloudflare/workerd-linux-arm64@1.20260603.1': + resolution: {integrity: sha512-LJZ6x00rAjSrobV4m0ZW0TpH5ilBbKcWBzlH+y+KOUsIE/CpTuhAzKV43TbSnFLRX5+jrWKiz2v0hO91lPXy6A==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260601.1': - resolution: {integrity: sha512-ye0C7MFLkeH16iTo8Tcjv2KiFmp23+sZGvUzSQa4xhP0QMe6EoJ+H/4SqqvnZ5nfN54slqKvx2VnXceENWe2CQ==} + '@cloudflare/workerd-windows-64@1.20260603.1': + resolution: {integrity: sha512-DvwqkXMAJRPoDN4PxapAwhlz/6ouD+6R1ttbAEK3cWD/QBvFF5STx7Ds/9Irf+rBly3np3uHWkeX+wZnNFEuzA==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260604.1': - resolution: {integrity: sha512-nVTydUwPcz9WfwZ/6xAmqs8uUVeGbDVmyPvSYrYAo4CQTyi0122SoE1Suw8RvqnN60XXHPChAjDqSIjnr/mbmg==} + '@cloudflare/workers-types@4.20260605.1': + resolution: {integrity: sha512-9YJaGPQwuQYFHoElVhJP40ZmUO/Z2OiBon58sKpHjgwq5btC/B2BZLC45AQ14k+1I+hjcY2z2t2R6uUxU9AqwQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4589,8 +4589,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260601.0: - resolution: {integrity: sha512-56TFiulSEQu43cYxdXgCiA3U3i+Ls0NoXwJXd6DmpNsx8yl/1Il2T3DQ4CMXjR6yfE7CSvC5MuXaqcSAMREjgw==} + miniflare@4.20260603.0: + resolution: {integrity: sha512-+kMQYB82gC8MPOuojHur3icQsUeZUEJ+Sphuo5rVC3Ri9txBLAW/mH33b9OVrpmkogQeaaqPS4tPtugJZhk5Kw==} engines: {node: '>=22.0.0'} hasBin: true @@ -5228,6 +5228,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.2: + resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} + engines: {node: '>=10'} + hasBin: true + set-cookie-parser@3.1.0: resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} @@ -5958,17 +5963,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260601.1: - resolution: {integrity: sha512-Bg4+HF3B8TW0urAv8chiz25HSQ/aJxMBjgheUzu/nB1NQa+CaKGrUPv+Z3bf0np/WxLHYW1kcseVEtzZVPbX4g==} + workerd@1.20260603.1: + resolution: {integrity: sha512-NPcbhI1++CS+fnELyXtsIR52en+5kwr/OrKeiQeYXGy10HxmPdsQBv9N+DU7hJIOOmBHhOGAAsoGDjyiQ2YCaA==} engines: {node: '>=16'} hasBin: true - wrangler@4.97.0: - resolution: {integrity: sha512-jzW/aNvjerV+4TmwbvwGY6lpcuBk7EFUTonMDNfci45wSmMTj2/OJN+83cc/CeepKdb+6ZjGJw9NRjmcQoxqRg==} + wrangler@4.98.0: + resolution: {integrity: sha512-cXfFUuF4rMIvE0hiMnXjEAB27ERryaCgquBJdUoPIjFzYYE1rbRdMUkEdQ18qDPUtsPvhJdqxLntixT9OfSzQw==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260601.1 + '@cloudflare/workers-types': ^4.20260603.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6261,34 +6266,34 @@ snapshots: '@bufbuild/protobuf@2.11.0': {} - '@cloudflare/containers@0.3.6': {} + '@cloudflare/containers@0.3.7': {} '@cloudflare/kv-asset-handler@0.5.0': {} '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260601.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260603.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260601.1 + workerd: 1.20260603.1 - '@cloudflare/workerd-darwin-64@1.20260601.1': + '@cloudflare/workerd-darwin-64@1.20260603.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260601.1': + '@cloudflare/workerd-darwin-arm64@1.20260603.1': optional: true - '@cloudflare/workerd-linux-64@1.20260601.1': + '@cloudflare/workerd-linux-64@1.20260603.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260601.1': + '@cloudflare/workerd-linux-arm64@1.20260603.1': optional: true - '@cloudflare/workerd-windows-64@1.20260601.1': + '@cloudflare/workerd-windows-64@1.20260603.1': optional: true - '@cloudflare/workers-types@4.20260604.1': {} + '@cloudflare/workers-types@4.20260605.1': {} '@colors/colors@1.6.0': {} @@ -10038,12 +10043,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260601.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260603.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.24.8 - workerd: 1.20260601.1 + workerd: 1.20260603.1 ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -10807,6 +10812,8 @@ snapshots: semver@7.8.2: {} + semver@7.8.2: {} + set-cookie-parser@3.1.0: {} sharp@0.34.5: @@ -11495,26 +11502,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260601.1: + workerd@1.20260603.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260601.1 - '@cloudflare/workerd-darwin-arm64': 1.20260601.1 - '@cloudflare/workerd-linux-64': 1.20260601.1 - '@cloudflare/workerd-linux-arm64': 1.20260601.1 - '@cloudflare/workerd-windows-64': 1.20260601.1 + '@cloudflare/workerd-darwin-64': 1.20260603.1 + '@cloudflare/workerd-darwin-arm64': 1.20260603.1 + '@cloudflare/workerd-linux-64': 1.20260603.1 + '@cloudflare/workerd-linux-arm64': 1.20260603.1 + '@cloudflare/workerd-windows-64': 1.20260603.1 - wrangler@4.97.0(@cloudflare/workers-types@4.20260604.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.98.0(@cloudflare/workers-types@4.20260605.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260601.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260603.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260601.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260603.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260601.1 + workerd: 1.20260603.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260604.1 + '@cloudflare/workers-types': 4.20260605.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 433c3ad12d938b14d17ae71426d69b69fa1d764f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 19:36:37 +0800 Subject: [PATCH 0020/1190] chore(deps): bump devenv from `f693b47` to `90ed622` (#22193) Bumps [devenv](https://github.com/cachix/devenv) from `f693b47` to `90ed622`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/f693b472c731e7dda69402daa88c06369d54fd3a...90ed6227ab389dd4e874a69a724f25dba312b754) --- updated-dependencies: - dependency-name: devenv dependency-version: 90ed6227ab389dd4e874a69a724f25dba312b754 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index fd1d053b5677..dbe055057f10 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1780543372, - "narHash": "sha256-FCGxk82Lc4koWcFw5xgr+W5vbwLVFLCnSMwm2gQOgr0=", + "lastModified": 1780630679, + "narHash": "sha256-hhQyVAYmNKziZ0T+T4Gsk0PYmnz4vdzOzpkJAmDASKM=", "owner": "cachix", "repo": "devenv", - "rev": "f693b472c731e7dda69402daa88c06369d54fd3a", + "rev": "90ed6227ab389dd4e874a69a724f25dba312b754", "type": "github" }, "original": { From bedeb4edaac3b7628e6f45bcf614e024cf8ac6ec Mon Sep 17 00:00:00 2001 From: TonyRL Date: Fri, 5 Jun 2026 12:01:56 +0000 Subject: [PATCH 0021/1190] chore: fix pnpm lock --- pnpm-lock.yaml | 183 +++++++------------------------------------------ 1 file changed, 25 insertions(+), 158 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 565988640ae9..1d66ca14c9a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -530,10 +530,6 @@ packages: resolution: {integrity: sha512-BCkFy+zN6kXQed3YOT7aJl93NfDSzQc3pBfsvTVPs9gU9X3V0aefEF5kwBT0E+mDWH9QgKaZstYUQN9VdQZT4g==} engines: {node: ^22.18.0 || >=24.11.0} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} @@ -1225,10 +1221,6 @@ packages: resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.7.1': - resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.7.2': resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2117,9 +2109,6 @@ packages: '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - '@protobufjs/inquire@1.1.1': resolution: {integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==} @@ -2641,9 +2630,6 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -2790,10 +2776,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.58.0': - resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.60.1': resolution: {integrity: sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2899,9 +2881,6 @@ packages: resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} engines: {node: '>= 20'} - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} @@ -3082,10 +3061,6 @@ packages: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} - cacheable-request@13.0.18: - resolution: {integrity: sha512-rFWadDRKJs3s2eYdXlGggnBZKG7MTblkFBB0YllFds+UYnfogDp2wcR6JN97FhRkHTvq59n2vhNoHNZn29dh/Q==} - engines: {node: '>=18'} - cacheable-request@13.0.19: resolution: {integrity: sha512-SVXGH037+Mo1aIMO5B2UcleR43FGjFdN+M8JObSyEoQ2Mn4CODRWx28gN5jiTF0n5ItsgtIZfyargMNs8GX4kg==} engines: {node: '>=18'} @@ -4745,9 +4720,6 @@ packages: oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - obug@2.1.2: resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} engines: {node: '>=12.20.0'} @@ -4959,10 +4931,6 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.15: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} @@ -5218,16 +5186,6 @@ packages: selderee@0.12.0: resolution: {integrity: sha512-b1YMh3+DHZp59DLna3qVwQ5iOla/nrI6mLBNW02XxU77M3046Df6VLkoaJyFz20VsGIG5kkp+FK0kg4K4HnUFw==} - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - semver@7.8.2: - resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.2: resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} engines: {node: '>=10'} @@ -5285,10 +5243,6 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - socks@2.8.9: resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} @@ -5638,14 +5592,6 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-fest@5.5.0: - resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} - engines: {node: '>=20'} - - type-fest@5.6.0: - resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} - engines: {node: '>=20'} - type-fest@5.7.0: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} @@ -5683,8 +5629,8 @@ packages: undici-types@7.25.0: resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} - undici@6.26.0: - resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} + undici@6.25.0: + resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} undici@7.24.8: @@ -6004,18 +5950,6 @@ packages: ws@0.7.2: resolution: {integrity: sha512-8mJ1Ku743qD/PKmO9Dg+y7BXTwzUgKdXguecfIyOVHFmez4JMqMF+V+M684btmQXHlwzyrJqRl3NYDltGDf6CQ==} - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.20.1: resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} engines: {node: '>=10.0.0'} @@ -6066,11 +6000,6 @@ packages: resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} - hasBin: true - yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -6138,17 +6067,17 @@ snapshots: '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) '@octokit/request': 10.0.8 '@octokit/request-error': 7.1.0 - undici: 6.26.0 + undici: 6.25.0 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.26.0 + undici: 6.25.0 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.26.0 + undici: 6.25.0 '@actions/io@3.0.2': {} @@ -6179,7 +6108,7 @@ snapshots: '@babel/code-frame@7.29.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 @@ -6196,8 +6125,6 @@ snapshots: '@babel/helper-string-parser@8.0.0-rc.6': {} - '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-identifier@7.29.7': {} '@babel/helper-validator-identifier@8.0.0-rc.6': {} @@ -6618,7 +6545,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 @@ -6636,11 +6563,6 @@ snapshots: '@eslint/object-schema@3.0.5': {} - '@eslint/plugin-kit@0.7.1': - dependencies: - '@eslint/core': 1.2.1 - levn: 0.4.1 - '@eslint/plugin-kit@0.7.2': dependencies: '@eslint/core': 1.2.1 @@ -7338,12 +7260,10 @@ snapshots: '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 + '@protobufjs/inquire': 1.1.1 '@protobufjs/float@1.0.2': {} - '@protobufjs/inquire@1.1.0': {} - '@protobufjs/inquire@1.1.1': {} '@protobufjs/path@1.1.2': {} @@ -7655,7 +7575,7 @@ snapshots: '@stylistic/eslint-plugin@5.10.0(eslint@10.4.1(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.6.1)) - '@typescript-eslint/types': 8.58.0 + '@typescript-eslint/types': 8.60.1 eslint: 10.4.1(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -7703,13 +7623,11 @@ snapshots: '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/etag@1.8.4': @@ -7888,8 +7806,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.60.1': {} '@typescript-eslint/typescript-estree@8.60.1(typescript@5.9.3)': @@ -7951,7 +7867,7 @@ snapshots: istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 magicast: 0.5.3 - obug: 2.1.1 + obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 vitest: 4.1.8(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.13.4(@types/node@25.9.1)(typescript@5.9.3))(vite@7.3.1(@types/node@25.9.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)) @@ -8026,13 +7942,6 @@ snapshots: agent-base@9.0.0: {} - ajv@6.14.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -8189,16 +8098,6 @@ snapshots: cacheable-lookup@7.0.0: {} - cacheable-request@13.0.18: - dependencies: - '@types/http-cache-semantics': 4.2.0 - get-stream: 9.0.1 - http-cache-semantics: 4.2.0 - keyv: 5.6.0 - mimic-response: 4.0.0 - normalize-url: 8.1.1 - responselike: 4.0.2 - cacheable-request@13.0.19: dependencies: '@types/http-cache-semantics': 4.2.0 @@ -8751,7 +8650,7 @@ snapshots: globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 - semver: 7.7.4 + semver: 7.8.2 optionalDependencies: ts-declaration-location: 1.0.7(typescript@5.9.3) typescript: 5.9.3 @@ -8762,7 +8661,7 @@ snapshots: eslint-plugin-unicorn@64.0.0(eslint@10.4.1(jiti@2.6.1)): dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.6.1)) change-case: 5.4.4 ci-info: 4.4.0 @@ -8777,13 +8676,13 @@ snapshots: pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.13.0 - semver: 7.7.4 + semver: 7.8.2 strip-indent: 4.1.1 eslint-plugin-yml@3.4.0(eslint@10.4.1(jiti@2.6.1)): dependencies: '@eslint/core': 1.2.1 - '@eslint/plugin-kit': 0.7.1 + '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 @@ -9157,14 +9056,14 @@ snapshots: '@sindresorhus/is': 8.0.0 byte-counter: 0.1.0 cacheable-lookup: 7.0.0 - cacheable-request: 13.0.18 + cacheable-request: 13.0.19 chunk-data: 0.1.0 decompress-response: 10.0.0 http2-wrapper: 2.2.1 keyv: 5.6.0 lowercase-keys: 4.0.1 responselike: 4.0.2 - type-fest: 5.6.0 + type-fest: 5.7.0 uint8array-extras: 1.5.0 graceful-fs@4.2.11: {} @@ -10102,7 +10001,7 @@ snapshots: statuses: 2.0.2 strict-event-emitter: 0.5.1 tough-cookie: 6.0.1 - type-fest: 5.5.0 + type-fest: 5.7.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: @@ -10156,7 +10055,7 @@ snapshots: inspector: 0.5.0 open: 8.4.2 undici: 8.3.0 - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -10193,8 +10092,6 @@ snapshots: oauth-sign@0.9.0: {} - obug@2.1.1: {} - obug@2.1.2: {} ofetch@1.5.1: @@ -10446,12 +10343,6 @@ snapshots: pluralize@8.0.0: {} - postcss@8.5.14: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.15: dependencies: nanoid: 3.3.12 @@ -10796,7 +10687,7 @@ snapshots: is-plain-object: 5.0.0 launder: 1.7.1 parse-srcset: 1.0.2 - postcss: 8.5.14 + postcss: 8.5.15 sax@1.6.0: {} @@ -10808,10 +10699,6 @@ snapshots: dependencies: parseley: 0.13.1 - semver@7.7.4: {} - - semver@7.8.2: {} - semver@7.8.2: {} set-cookie-parser@3.1.0: {} @@ -10882,7 +10769,7 @@ snapshots: dependencies: agent-base: 9.0.0 debug: 4.4.3 - socks: 2.8.7 + socks: 2.8.9 transitivePeerDependencies: - supports-color @@ -10894,11 +10781,6 @@ snapshots: transitivePeerDependencies: - supports-color - socks@2.8.7: - dependencies: - ip-address: 10.2.0 - smart-buffer: 4.2.0 - socks@2.8.9: dependencies: ip-address: 10.2.0 @@ -11041,7 +10923,7 @@ snapshots: pako: 2.1.0 path-browserify: 1.0.1 real-cancellable-promise: 1.2.3 - socks: 2.8.7 + socks: 2.8.9 store2: 2.14.4 ts-custom-error: 3.3.1 websocket: 1.0.35 @@ -11207,14 +11089,6 @@ snapshots: type-fest@4.41.0: {} - type-fest@5.5.0: - dependencies: - tagged-tag: 1.0.0 - - type-fest@5.6.0: - dependencies: - tagged-tag: 1.0.0 - type-fest@5.7.0: dependencies: tagged-tag: 1.0.0 @@ -11244,7 +11118,7 @@ snapshots: undici-types@7.25.0: {} - undici@6.26.0: {} + undici@6.25.0: {} undici@7.24.8: {} @@ -11410,7 +11284,7 @@ snapshots: es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.2 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 @@ -11571,11 +11445,6 @@ snapshots: bufferutil: 1.1.0 utf-8-validate: 1.1.0 - ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.1.0 - utf-8-validate: 5.0.10 - ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.1.0 @@ -11607,9 +11476,7 @@ snapshots: yaml-eslint-parser@2.0.0: dependencies: eslint-visitor-keys: 5.0.1 - yaml: 2.8.3 - - yaml@2.8.3: {} + yaml: 2.9.0 yaml@2.9.0: {} From 75d43dd0868d3a169cb0bc94eabf354d26af5e9c Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 5 Jun 2026 20:04:39 +0800 Subject: [PATCH 0022/1190] chore: add eslint-plugin-regexp (#22189) * chore: add eslint-plugin-regexp * chore: autofix * chore: fix no-useless-flag * chore: fix optimal-lookaround-quantifier * chore: fix no-lazy-ends * chore: fix no-contradiction-with-assertion * chore: fix no-useless-assertions * chore: fix no-useless-quantifier * chore: fix remaining regexp issues * fix: regex for WFU news link validation --- .oxlintrc.json | 73 +++++++++++++++++-- lib/middleware/anti-hotlink.ts | 4 +- lib/middleware/template.tsx | 5 +- lib/routes/12371/zxfb.ts | 2 +- lib/routes/163/news/special.ts | 4 +- lib/routes/163/open/vip.tsx | 2 +- lib/routes/2048/index.tsx | 2 +- lib/routes/36kr/hot-list.ts | 2 +- lib/routes/36kr/index.ts | 4 +- lib/routes/36kr/utils.ts | 2 +- lib/routes/3dmgame/utils.ts | 2 +- lib/routes/4ksj/forum.tsx | 2 +- lib/routes/69shu/article.ts | 6 +- lib/routes/6park/index.ts | 2 +- lib/routes/6park/news.ts | 2 +- lib/routes/9to5/utils.ts | 2 +- lib/routes/abc/index.ts | 4 +- lib/routes/acfun/video.ts | 2 +- lib/routes/aip/journal.ts | 2 +- lib/routes/altotrain/news.ts | 2 +- lib/routes/anthropic/research.ts | 2 +- lib/routes/aqara/news.ts | 2 +- lib/routes/arcteryx/regear-new-arrivals.tsx | 2 +- lib/routes/bbc/utils.tsx | 2 +- lib/routes/bilibili/cache.ts | 4 +- lib/routes/bjsk/index.ts | 3 +- lib/routes/bjtu/gs.ts | 2 +- lib/routes/bjwxdxh/index.ts | 2 +- lib/routes/bloomberg/utils.ts | 2 +- lib/routes/booru/mmda.ts | 2 +- lib/routes/bse/index.ts | 2 +- lib/routes/caixin/category.ts | 2 +- lib/routes/caixin/utils-fulltext.ts | 2 +- lib/routes/cas/sim/kyjz.ts | 2 +- lib/routes/chinaratings/credit-research.ts | 2 +- lib/routes/chnmus/exhibition.tsx | 2 +- lib/routes/cih-index/report.ts | 2 +- lib/routes/cisia/index.ts | 2 +- lib/routes/cline/blog.ts | 2 +- lib/routes/cool18/index.ts | 2 +- lib/routes/ctinews/topic.ts | 2 +- lib/routes/dailypush/utils.ts | 2 +- lib/routes/daum/potplayer.ts | 6 +- lib/routes/dayanzai/index.ts | 2 +- lib/routes/dcard/utils.ts | 2 +- lib/routes/dealstreetasia/home.ts | 2 +- lib/routes/dedao/knowledge.tsx | 2 +- lib/routes/dedao/user.tsx | 2 +- lib/routes/dehenglaw/index.ts | 2 +- lib/routes/dianping/user.ts | 2 +- lib/routes/dlnews/category.tsx | 2 +- lib/routes/dnaindia/common.ts | 2 +- lib/routes/domp4/detail.ts | 2 +- lib/routes/dongqiudi/utils.ts | 2 +- lib/routes/dora-world/article.ts | 2 +- lib/routes/douban/other/replied.ts | 2 +- lib/routes/douban/other/replies.ts | 2 +- lib/routes/dribbble/utils.tsx | 2 +- lib/routes/ehentai/ehapi.ts | 2 +- lib/routes/fanqienovel/page.ts | 2 +- lib/routes/flashcat/blog.ts | 34 ++++----- lib/routes/gamer/ani/anime.ts | 2 +- lib/routes/getitfree/index.ts | 2 +- lib/routes/gigazine/en.ts | 2 +- lib/routes/globallawreview/index.ts | 2 +- lib/routes/google/album.ts | 2 +- lib/routes/google/scholar.ts | 2 +- lib/routes/gov/beijing/kw/index.ts | 4 +- lib/routes/gov/cac/index.ts | 2 +- lib/routes/gov/ccdi/utils.ts | 2 +- lib/routes/gov/cn/news/index.ts | 8 +- lib/routes/gov/general/general.ts | 4 +- lib/routes/gov/guangdong/tqyb/tfxtq.tsx | 2 +- lib/routes/gov/miit/wjfb.ts | 2 +- lib/routes/gov/miit/yjzj.ts | 2 +- lib/routes/gov/mofcom/article.ts | 2 +- lib/routes/gov/nrta/news.ts | 2 +- lib/routes/gov/nsfc/index.ts | 2 +- lib/routes/gov/stats/index.tsx | 2 +- lib/routes/gov/zhengce/govall.ts | 2 +- lib/routes/gov/zj/ningbogzw-notice.ts | 2 +- lib/routes/gov/zj/ningborsjnotice.ts | 2 +- lib/routes/guancha/personalpage.ts | 2 +- lib/routes/hk01/utils.tsx | 2 +- lib/routes/hkej/index.tsx | 2 +- lib/routes/hongkong/chp.ts | 2 +- lib/routes/hpoi/utils.ts | 4 +- lib/routes/hupu/utils.ts | 4 +- lib/routes/hypergryph/arknights/arktca.ts | 2 +- lib/routes/ifeng/feng.ts | 2 +- lib/routes/ifeng/news.tsx | 6 +- lib/routes/inewsweek/index.ts | 2 +- lib/routes/iwara/utils.ts | 2 +- lib/routes/ixigua/user-video.tsx | 2 +- lib/routes/jandan/utils.ts | 2 +- lib/routes/javbus/index.tsx | 4 +- lib/routes/jiemian/common.tsx | 2 +- lib/routes/jike/user.ts | 2 +- lib/routes/jike/utils.ts | 2 +- lib/routes/jimmyspa/books.ts | 2 +- lib/routes/kunchengblog/essay.ts | 2 +- .../leetcode/dailyquestion-solution-cn.ts | 2 +- lib/routes/lfsyd/utils.tsx | 2 +- lib/routes/line/utils.ts | 2 +- lib/routes/lorientlejour/index.tsx | 2 +- lib/routes/luolei/index.tsx | 2 +- lib/routes/magazinelib/latest-magazine.tsx | 2 +- lib/routes/mastodon/utils.ts | 4 +- lib/routes/maven/central.ts | 2 +- lib/routes/metacritic/index.tsx | 2 +- lib/routes/meteor/utils.ts | 2 +- lib/routes/mirror/index.ts | 2 +- lib/routes/modelscope/community.tsx | 2 +- lib/routes/mrinalxdev/blog.ts | 2 +- lib/routes/mydrivers/index.tsx | 2 +- lib/routes/mydrivers/rank.ts | 2 +- lib/routes/natgeo/dailyphoto.tsx | 2 +- .../nationalgeographic/latest-stories.tsx | 2 +- lib/routes/nature/utils.ts | 2 +- lib/routes/ncpssd/newlist.ts | 2 +- lib/routes/neu/yz.ts | 2 +- lib/routes/nga/forum.ts | 4 +- lib/routes/nga/post.ts | 36 ++++----- lib/routes/nhentai/util.tsx | 2 +- lib/routes/nikkei/cn/index.ts | 2 +- lib/routes/nintendo/eshop-hk.ts | 4 +- lib/routes/nintendo/system-update.ts | 2 +- lib/routes/nowcoder/discuss.ts | 2 +- lib/routes/odaily/activity.ts | 2 +- lib/routes/odaily/post.ts | 2 +- lib/routes/oeeee/utils.ts | 2 +- lib/routes/outagereport/index.ts | 4 +- lib/routes/papers/category.ts | 2 +- lib/routes/parliament/section77.ts | 4 +- lib/routes/patreon/feed.tsx | 2 +- lib/routes/pixiv/novel-api/content/utils.ts | 4 +- lib/routes/playno1/av.ts | 2 +- lib/routes/qingting/channel.ts | 2 +- lib/routes/qingting/podcast.ts | 2 +- lib/routes/quantamagazine/archive.ts | 6 +- lib/routes/rawkuma/manga.tsx | 2 +- lib/routes/readhub/index.ts | 2 +- lib/routes/readhub/util.ts | 2 +- lib/routes/reuters/common.tsx | 4 +- lib/routes/runyeah/posts.ts | 2 +- lib/routes/shisu/en.ts | 4 +- lib/routes/sina/utils.tsx | 2 +- lib/routes/sis001/common.ts | 4 +- lib/routes/smartlink/index.ts | 2 +- lib/routes/sohu/mobile.ts | 2 +- lib/routes/sohu/mp.tsx | 2 +- lib/routes/solidot/_article.ts | 2 +- lib/routes/sony/downloads.ts | 2 +- lib/routes/steam/curator.tsx | 2 +- lib/routes/steam/news.ts | 6 +- lib/routes/steam/workshop-search.tsx | 2 +- lib/routes/supchina/index.ts | 4 +- lib/routes/swjtu/gsee/yjs.ts | 2 +- lib/routes/swjtu/scai.ts | 2 +- lib/routes/swjtu/sports.ts | 2 +- lib/routes/swpu/utils.ts | 2 +- lib/routes/szse/disclosure/listed-notice.ts | 2 +- lib/routes/tass/news.ts | 2 +- lib/routes/tencent/news/author.tsx | 2 +- lib/routes/tesla/cx.ts | 2 +- lib/routes/threads/utils.ts | 2 +- lib/routes/transcriptforest/index.ts | 2 +- .../twitter/api/web-api/gql-id-resolver.ts | 2 +- lib/routes/twitter/utils.ts | 2 +- lib/routes/txrjy/fornumtopic.tsx | 4 +- lib/routes/udn/breaking-news.tsx | 4 +- lib/routes/upc/jwc.ts | 2 +- lib/routes/ups/track.ts | 4 +- lib/routes/uptimerobot/rss.tsx | 2 +- lib/routes/vcb-s/category.ts | 2 +- lib/routes/vcb-s/index.ts | 2 +- lib/routes/weibo/utils.ts | 6 +- lib/routes/wenku8/volume.ts | 2 +- lib/routes/wfu/news.ts | 2 +- lib/routes/wikipedia/current-events.ts | 8 +- lib/routes/wmc-bj/publish.tsx | 2 +- lib/routes/wnacg/common.tsx | 2 +- lib/routes/wordpress/index.ts | 4 +- lib/routes/wsj/news.ts | 2 +- lib/routes/xaufe/jiaowu.ts | 2 +- lib/routes/xhamster/index.ts | 2 +- lib/routes/xinpianchang/index.ts | 2 +- lib/routes/xueqiu/snb.ts | 2 +- lib/routes/xueqiu/user.ts | 2 +- lib/routes/xys/new.tsx | 2 +- lib/routes/yamibo/utils.ts | 6 +- lib/routes/yicai/utils.ts | 2 +- lib/routes/ynet/list.ts | 2 +- lib/routes/youtube/api/google.ts | 2 +- lib/routes/youtube/community.tsx | 2 +- lib/routes/youtube/custom.ts | 2 +- lib/routes/zaker/utils.ts | 2 +- lib/routes/zaobao/util.tsx | 2 +- lib/routes/zhihu/utils.ts | 4 +- lib/routes/zhonglun/index.ts | 2 +- lib/utils/camelcase-keys.ts | 2 +- lib/utils/common-config.ts | 2 +- lib/utils/parse-date.ts | 12 +-- lib/utils/valid-host.ts | 2 +- lib/utils/wechat-mp.ts | 2 +- package.json | 1 + pnpm-lock.yaml | 59 +++++++++++++++ scripts/workflow/format-description.ts | 2 +- 208 files changed, 418 insertions(+), 295 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 85967256d352..f3a91894a809 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -14,6 +14,7 @@ "jsPlugins": [ { "name": "n", "specifier": "eslint-plugin-n" }, { "name": "unicorn-js", "specifier": "eslint-plugin-unicorn" }, + { "name": "regexp", "specifier": "eslint-plugin-regexp" }, "@stylistic/eslint-plugin", "eslint-plugin-simple-import-sort", "oxlint-plugin-eslint", @@ -32,19 +33,19 @@ "no-const-assign": "error", "no-constant-binary-expression": "error", "no-constant-condition": "error", - // "no-control-regex": "error", -> off + "no-control-regex": "error", "no-debugger": "error", "no-dupe-class-members": "error", "no-dupe-else-if": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", - "no-empty-character-class": "error", + // "no-empty-character-class": "error", -> off, handled by eslint-plugin-regexp "no-empty-pattern": "error", "no-ex-assign": "error", "no-fallthrough": "error", "no-func-assign": "error", "no-import-assign": "error", - "no-invalid-regexp": "error", + // "no-invalid-regexp": "error", -> off, handled by eslint-plugin-regexp "no-irregular-whitespace": "error", "no-loss-of-precision": "error", "no-misleading-character-class": "error", @@ -65,7 +66,7 @@ "no-unsafe-optional-chaining": "error", "no-unused-private-class-members": "error", // "no-unused-vars": "error", -> off for @typescript-eslint/no-unused-vars - "no-useless-backreference": "error", + // "no-useless-backreference": "error", -> off, handled by eslint-plugin-regexp "use-isnan": "error", "valid-typeof": "error", // #endregion @@ -289,12 +290,74 @@ "unicorn/throw-new-error": "error", // #endregion + // #region --- regexp recommended --- + "regexp/confusing-quantifier": "warn", + "regexp/control-character-escape": "error", + "regexp/match-any": "error", + "regexp/negation": "error", + "regexp/no-contradiction-with-assertion": "error", + "regexp/no-dupe-characters-character-class": "error", + "regexp/no-dupe-disjunctions": "error", + "regexp/no-empty-alternative": "warn", + "regexp/no-empty-capturing-group": "error", + "regexp/no-empty-character-class": "error", + "regexp/no-empty-group": "error", + "regexp/no-empty-lookarounds-assertion": "error", + "regexp/no-empty-string-literal": "error", + "regexp/no-escape-backspace": "error", + "regexp/no-extra-lookaround-assertions": "error", + "regexp/no-invalid-regexp": "error", + "regexp/no-invisible-character": "error", + "regexp/no-lazy-ends": "warn", + "regexp/no-legacy-features": "error", + "regexp/no-misleading-capturing-group": "error", + "regexp/no-misleading-unicode-character": "error", + "regexp/no-missing-g-flag": "error", + "regexp/no-non-standard-flag": "error", + "regexp/no-obscure-range": "error", + "regexp/no-optional-assertion": "error", + "regexp/no-potentially-useless-backreference": "warn", + "regexp/no-super-linear-backtracking": "error", + "regexp/no-trivially-nested-assertion": "error", + "regexp/no-trivially-nested-quantifier": "error", + "regexp/no-unused-capturing-group": "error", + "regexp/no-useless-assertions": "error", + "regexp/no-useless-backreference": "error", + "regexp/no-useless-character-class": "error", + "regexp/no-useless-dollar-replacements": "error", + "regexp/no-useless-escape": "error", + "regexp/no-useless-flag": "warn", + "regexp/no-useless-lazy": "error", + "regexp/no-useless-non-capturing-group": "error", + "regexp/no-useless-quantifier": "error", + "regexp/no-useless-range": "error", + "regexp/no-useless-set-operand": "error", + "regexp/no-useless-string-literal": "error", + "regexp/no-useless-two-nums-quantifier": "error", + "regexp/no-zero-quantifier": "error", + "regexp/optimal-lookaround-quantifier": "warn", + "regexp/optimal-quantifier-concatenation": "error", + "regexp/prefer-character-class": "error", + "regexp/prefer-d": "error", + "regexp/prefer-plus-quantifier": "error", + "regexp/prefer-predefined-assertion": "error", + "regexp/prefer-question-quantifier": "error", + "regexp/prefer-range": "error", + "regexp/prefer-set-operation": "error", + "regexp/prefer-star-quantifier": "error", + "regexp/prefer-unicode-codepoint-escapes": "error", + "regexp/prefer-w": "error", + "regexp/simplify-set-operations": "error", + "regexp/sort-flags": "error", + "regexp/strict": "error", + "regexp/use-ignore-case": "error", + // #endregion + // --- custom rules --- // #region --- possible problems --- "array-callback-return": ["error", { "allowImplicit": true }], "no-await-in-loop": "error", - "no-control-regex": "off", "no-prototype-builtins": "off", "no-undef": "off", // typescript/eslint-recommended, ts(2552) // #endregion diff --git a/lib/middleware/anti-hotlink.ts b/lib/middleware/anti-hotlink.ts index 49a849570b04..7dd6e897661d 100644 --- a/lib/middleware/anti-hotlink.ts +++ b/lib/middleware/anti-hotlink.ts @@ -6,7 +6,7 @@ import { config } from '@/config'; import type { Data } from '@/types'; import logger from '@/utils/logger'; -const templateRegex = /\${([^{}]+)}/g; +const templateRegex = /\$\{([^{}]+)\}/g; const allowedUrlProperties = new Set(['hash', 'host', 'hostname', 'href', 'origin', 'password', 'pathname', 'port', 'protocol', 'search', 'searchParams', 'username']); // match path or sub-path @@ -150,7 +150,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { if (item.enclosure_url && item.enclosure_type) { if (item.enclosure_type.startsWith('image/')) { item.enclosure_url = replaceUrl(imageHotlinkTemplate, item.enclosure_url); - } else if (/^(video|audio)\//.test(item.enclosure_type)) { + } else if (/^(?:video|audio)\//.test(item.enclosure_type)) { item.enclosure_url = replaceUrl(multimediaHotlinkTemplate, item.enclosure_url); } } diff --git a/lib/middleware/template.tsx b/lib/middleware/template.tsx index d9f5ed98655c..78673a9504a5 100644 --- a/lib/middleware/template.tsx +++ b/lib/middleware/template.tsx @@ -28,7 +28,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { return ctx.json(ctx.get('json') || { message: 'plugin does not set debug json' }); } - if (/(\d+)\.debug\.html$/.test(outputType)) { + if (/\d+\.debug\.html$/.test(outputType)) { const index = Number.parseInt(outputType.match(/(\d+)\.debug\.html$/)?.[1] || '0'); return ctx.html(data?.item?.[index]?.description || `data.item[${index}].description not found`); } @@ -58,7 +58,8 @@ const middleware: MiddlewareHandler = async (ctx, next) => { // https://stackoverflow.com/questions/1497885/remove-control-characters-from-php-string/1497928#1497928 // remove unicode control characters // see #14940 #14943 #15262 - item.description = item.description.replaceAll(/[\u0000-\u0009\u000B\u000C\u000E-\u001F\u007F\u200B\uFFFF]/g, ''); + // oxlint-disable-next-line no-control-regex + item.description = item.description.replaceAll(/[\u0000-\u0009\v\f\u000E-\u001F\u007F\u200B\uFFFF]/g, ''); } if (typeof item.author === 'string') { diff --git a/lib/routes/12371/zxfb.ts b/lib/routes/12371/zxfb.ts index e5049220597f..b6b521b9254a 100644 --- a/lib/routes/12371/zxfb.ts +++ b/lib/routes/12371/zxfb.ts @@ -16,7 +16,7 @@ const handler = async (ctx) => { const $ = cheerio.load(response.data); - const pattern = /item=(\[{.*?}]);/; + const pattern = /item=(\[\{.*?\}\]);/; const newsList = JSON.parse($('script[language="javascript"]').text().match(pattern)?.[1].replaceAll("'", '"') || '[]'); const topNewsList = newsList.slice(0, limit).map((item) => ({ diff --git a/lib/routes/163/news/special.ts b/lib/routes/163/news/special.ts index ea7d1948c278..86f93caba8df 100644 --- a/lib/routes/163/news/special.ts +++ b/lib/routes/163/news/special.ts @@ -102,7 +102,7 @@ async function handler(ctx) { const url = `https://3g.163.com/touch/reconstruct/article/list/${type}/0-20.html`; const response = await got(url); const data = response.data; - const matches = data.replaceAll(/\s/g, '').match(/artiList\((.*?)]}\)/); + const matches = data.replaceAll(/\s/g, '').match(/artiList\((.*?)\]\}\)/); const articlelist0 = matches[1].replace(/".*?wangning/, '"articles') + ']}'; const articlelist = JSON.parse(articlelist0); const articles = articlelist.articles; @@ -112,7 +112,7 @@ async function handler(ctx) { let url = article.url; if (url === null || article.skipType === 'video') { const skipurl = article.skipURL; - const vid = skipurl.match(/vid=(.*?)$/); + const vid = skipurl.match(/vid=(.*)$/); if (vid !== null) { url = `https://3g.163.com/exclusive/video/${vid[1]}.html`; } diff --git a/lib/routes/163/open/vip.tsx b/lib/routes/163/open/vip.tsx index 4ee699dbc0dd..e34a1da598a2 100644 --- a/lib/routes/163/open/vip.tsx +++ b/lib/routes/163/open/vip.tsx @@ -65,7 +65,7 @@ async function handler() { const initialState = JSON.parse( $('script') .text() - .match(/window\.__INITIAL_STATE__=(.*);\(function\(\){var/)[1] + .match(/window\.__INITIAL_STATE__=(.*);\(function\(\)\{var/)[1] ); const list = Object.values(initialState.courseindex.myModules).flatMap((mod) => diff --git a/lib/routes/2048/index.tsx b/lib/routes/2048/index.tsx index 46c1e53563c2..4cf967857835 100644 --- a/lib/routes/2048/index.tsx +++ b/lib/routes/2048/index.tsx @@ -164,7 +164,7 @@ async function handler(ctx) { } } if (!item.enclosure_url) { - const hashMatch = readTpcHtml.match(/哈希校验[^;]*;\s*([a-fA-F0-9]{40})\s*[;;]/); + const hashMatch = readTpcHtml.match(/哈希校验[^;]*;\s*([a-f0-9]{40})\s*[;;]/i); const magnetFromHash = hashMatch ? `magnet:?xt=urn:btih:${hashMatch[1]}` : null; const magnetFromText = magnetText.match(/magnet:\?xt=urn:btih:[^\s"'<>]+/)?.[0]; const magnetLink = magnetFromText ?? readTpcHtml.match(/magnet:\?xt=urn:btih:[^\s"'<>]+/)?.[0] ?? magnetFromHash ?? copyLink; diff --git a/lib/routes/36kr/hot-list.ts b/lib/routes/36kr/hot-list.ts index c28b4f287e81..61825e4e6b3e 100644 --- a/lib/routes/36kr/hot-list.ts +++ b/lib/routes/36kr/hot-list.ts @@ -79,7 +79,7 @@ async function handler(ctx) { }, }); - const data = getProperty(JSON.parse(response.data.match(/window.initialState=({.*})/)[1]), categories[category].key); + const data = getProperty(JSON.parse(response.data.match(/window.initialState=(\{.*\})/)[1]), categories[category].key); let items = data .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10) diff --git a/lib/routes/36kr/index.ts b/lib/routes/36kr/index.ts index 6c24a1d83726..e3b085f41cec 100644 --- a/lib/routes/36kr/index.ts +++ b/lib/routes/36kr/index.ts @@ -48,7 +48,7 @@ async function handler(ctx) { const $ = load(response.data); - const data = JSON.parse(response.data.match(/"itemList":(\[.*?])/)[1]); + const data = JSON.parse(response.data.match(/"itemList":(\[.*?\])/)[1]); let items = data .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30) @@ -64,7 +64,7 @@ async function handler(ctx) { }; }); - if (!/^\/(search|newsflashes)/.test(path)) { + if (!/^\/(?:search|newsflashes)/.test(path)) { items = await Promise.all(items.map((item) => ProcessItem(item, cache.tryGet))); } diff --git a/lib/routes/36kr/utils.ts b/lib/routes/36kr/utils.ts index 5149e1ae2864..a909ece47841 100644 --- a/lib/routes/36kr/utils.ts +++ b/lib/routes/36kr/utils.ts @@ -11,7 +11,7 @@ export const ProcessItem = (item, tryGet) => tryGet(item.link, async () => { const detailResponse = await ofetch(item.link); - const cipherTextList = detailResponse.match(/{"state":"(.*)","isEncrypt":true}/) ?? []; + const cipherTextList = detailResponse.match(/\{"state":"(.*)","isEncrypt":true\}/) ?? []; if (cipherTextList.length === 0) { const $ = load(detailResponse); diff --git a/lib/routes/3dmgame/utils.ts b/lib/routes/3dmgame/utils.ts index 37bda824243a..e714f989c81b 100644 --- a/lib/routes/3dmgame/utils.ts +++ b/lib/routes/3dmgame/utils.ts @@ -11,7 +11,7 @@ const parseArticle = (item, tryGet) => if (item.link.startsWith('https://dl.3dmgame.com/')) { const lis = $('.patchtop .lis'); - const [, category, pubDate, author] = lis.text().match(/补丁类型:(.*?)\n.*整理时间:(.*?)\n.*补丁制作:(.*?)\n/s); + const [, category, pubDate, author] = lis.text().match(/补丁类型:([^\n]*)\n.*整理时间:([^\n]*)\n.*补丁制作:([^\n]*)\n/s); item.description = lis.html() + $('.L_title').html() + $('.GmL_1').html(); item.category = category; diff --git a/lib/routes/4ksj/forum.tsx b/lib/routes/4ksj/forum.tsx index 4762c3e87bb8..ae514a5d4c10 100644 --- a/lib/routes/4ksj/forum.tsx +++ b/lib/routes/4ksj/forum.tsx @@ -114,7 +114,7 @@ async function handler(ctx) { const scriptUrl = new URL(scriptPath, rootUrl).href; const scriptResponse = await ofetch(scriptUrl); - const key = scriptResponse.match(/{var key="(.*?)"/)?.[1]; + const key = scriptResponse.match(/\{var key="(.*?)"/)?.[1]; const value = scriptResponse.match(/",value="(.*?)"/)?.[1]; const getPath = scriptResponse.match(/\.get\("(.*?&key=)"/)?.[1]; diff --git a/lib/routes/69shu/article.ts b/lib/routes/69shu/article.ts index bc13b6c30ce7..39ca17180054 100644 --- a/lib/routes/69shu/article.ts +++ b/lib/routes/69shu/article.ts @@ -54,8 +54,8 @@ const createItem = (url: string) => cache.tryGet(url, async () => { const html = await get(url); const $ = load(html); - const { articleid, chapterid, chaptername } = parseObject(/bookinfo\s?=\s?{[\S\s]+?}/, $('head>script:not([src])').text()); - const decryptionMap = parseObject(/_\d+\s?=\s?{[\S\s]+?}/, $('.txtnav+script').text()); + const { articleid, chapterid, chaptername } = parseObject(/bookinfo\s?=\s?\{[\s\S]+?\}/, $('head>script:not([src])').text()); + const decryptionMap = parseObject(/_\d+\s?=\s?\{[\s\S]+?\}/, $('.txtnav+script').text()); return { title: chaptername, @@ -70,7 +70,7 @@ const parseObject = (reg: RegExp, str: string): Record => { const obj = {}; const match = reg.exec(str); if (match) { - for (const line of match[0].matchAll(/(\w+):\s?["']?([\S\s]+?)["']?[\n,}]/g)) { + for (const line of match[0].matchAll(/(\w+):\s?["']?([\s\S]+?)["']?[\n,}]/g)) { obj[line[1]] = line[2]; } } diff --git a/lib/routes/6park/index.ts b/lib/routes/6park/index.ts index 4e2015f42835..e3904f8f9342 100644 --- a/lib/routes/6park/index.ts +++ b/lib/routes/6park/index.ts @@ -66,7 +66,7 @@ async function handler(ctx) { const content = load(detailResponse.data); item.title = content('title').text().replace(' -6park.com', ''); - item.author = detailResponse.data.match(/送交者: .*>(.*)<.*\[/)[1]; + item.author = detailResponse.data.match(/送交者:[^>]*>([^<]*)<\/a>/)[1].trim(); item.pubDate = timezone(parseDate(detailResponse.data.match(/于 (.*) 已读/)[1], 'YYYY-MM-DD h:m'), +8); item.description = content('pre') .html() diff --git a/lib/routes/6park/news.ts b/lib/routes/6park/news.ts index 2050185e5d8e..30941bde8440 100644 --- a/lib/routes/6park/news.ts +++ b/lib/routes/6park/news.ts @@ -76,7 +76,7 @@ async function handler(ctx) { const content = load(detailResponse.data); - const matches = detailResponse.data.match(/新闻来源:(.*?)于.*(\d{4}(?:-\d{2}){2} (?:\d{1,2}:){2}\d{1,2})/); + const matches = detailResponse.data.match(/新闻来源:([^于]*)于.*(\d{4}(?:-\d{2}){2} (?:\d{1,2}:){2}\d{1,2})/); item.title = content('h2').text(); item.author = matches[1].trim(); diff --git a/lib/routes/9to5/utils.ts b/lib/routes/9to5/utils.ts index 3cbb329b1fc7..f4c39a2a89ab 100644 --- a/lib/routes/9to5/utils.ts +++ b/lib/routes/9to5/utils.ts @@ -22,7 +22,7 @@ const ProcessFeed = (data) => { content.find('div').each((i, e) => { if ($(e)[0].attribs.class) { const classes = $(e)[0].attribs.class; - if (/\w{10}\s\w{10}/g.test(classes)) { + if (/\w{10}\s\w{10}/.test(classes)) { $(e).remove(); } } diff --git a/lib/routes/abc/index.ts b/lib/routes/abc/index.ts index a4eea519884e..89a9b57ed96c 100644 --- a/lib/routes/abc/index.ts +++ b/lib/routes/abc/index.ts @@ -49,7 +49,7 @@ async function handler(ctx) { const feedUrl = new URL(`news/feed/${documentId}/rss.xml`, rootUrl).href; const feedResponse = await ofetch(feedUrl); - currentUrl = feedResponse.match(/([\w-./:?]+)<\/link>/)[1]; + currentUrl = feedResponse.match(/([\w./:?-]+)<\/link>/)[1]; } const currentResponse = await ofetch(currentUrl); @@ -124,7 +124,7 @@ async function handler(ctx) { item.title = content('meta[property="og:title"]').prop('content'); item.description = ''; - const enclosurePattern = String.raw`"(?:MIME|content)?Type":"([\w]+/[\w]+)".*?"(?:fileS|s)?ize":(\d+),.*?"url":"([\w-.:/?]+)"`; + const enclosurePattern = String.raw`"(?:MIME|content)?Type":"(\w+/\w+)".*?"(?:fileS|s)?ize":(\d+),.*?"url":"([\w.:/?-]+)"`; const enclosureMatches = detailResponse.match(new RegExp(enclosurePattern, 'g')); diff --git a/lib/routes/acfun/video.ts b/lib/routes/acfun/video.ts index 39ca0200be73..d6732a0dc41a 100644 --- a/lib/routes/acfun/video.ts +++ b/lib/routes/acfun/video.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const list = $('#ac-space-video-list a').toArray(); const image = $('head style:contains("user-photo")') .text() - .match(/.user-photo{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)?.[1]; + .match(/.user-photo\{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)?.[1]; return { title, diff --git a/lib/routes/aip/journal.ts b/lib/routes/aip/journal.ts index 68bea00e2867..f5c718e57e89 100644 --- a/lib/routes/aip/journal.ts +++ b/lib/routes/aip/journal.ts @@ -43,7 +43,7 @@ async function handler(ctx) { const $ = load(response); const jrnlName = $('meta[property="og:title"]') .attr('content') - .match(/(?:[^=]*=)?\s*([^>]+)\s*/)[1]; + .match(/(?:[^=]*=)?\s*([^>]+)/)[1]; const publication = $('.al-article-item-wrap.al-normal'); const list = publication.toArray().map((item) => { diff --git a/lib/routes/altotrain/news.ts b/lib/routes/altotrain/news.ts index f541b8ed2bb3..8bfbe028df29 100644 --- a/lib/routes/altotrain/news.ts +++ b/lib/routes/altotrain/news.ts @@ -71,7 +71,7 @@ function extractItem(a: Cheerio, language: string) { const descEl = a.find('p').first(); const description = descEl.text().trim(); - const dateMatch = language === 'fr' ? description.match(/(\d{1,2} [a-zéû]+[.]? \d{4})/i) : description.match(/([A-Z][a-z]+[.]? \d{1,2}, \d{4})/); + const dateMatch = language === 'fr' ? description.match(/(\d{1,2} [a-zéû]+\.? \d{4})/i) : description.match(/([A-Z][a-z]+\.? \d{1,2}, \d{4})/); const pubDateStr = dateMatch ? dateMatch[1].trim() : ''; const pubDate = parseDate(pubDateStr); diff --git a/lib/routes/anthropic/research.ts b/lib/routes/anthropic/research.ts index cfba01932e4d..c19564ebf810 100644 --- a/lib/routes/anthropic/research.ts +++ b/lib/routes/anthropic/research.ts @@ -47,7 +47,7 @@ async function handler() { } } - const partRegex = /^([0-9a-zA-Z]+):([0-9a-zA-Z]+)?(\[.*)$/; + const partRegex = /^([0-9a-z]+):([0-9a-z]+)?(\[.*)$/i; const fd = textList .join('') .split('\n') diff --git a/lib/routes/aqara/news.ts b/lib/routes/aqara/news.ts index 898ea0dac62d..6dc95b0fe148 100644 --- a/lib/routes/aqara/news.ts +++ b/lib/routes/aqara/news.ts @@ -23,7 +23,7 @@ async function handler(ctx) { const $ = load(response); let items = response - .match(/(parm\.newsTitle[\S\s]*?arr\.push\(parm\))/g) + .match(/(parm\.newsTitle[\s\S]*?arr\.push\(parm\))/g) .slice(0, limit) .map((item) => ({ title: item.match(/parm\.newsTitle = '(.*?)'/)[1], diff --git a/lib/routes/arcteryx/regear-new-arrivals.tsx b/lib/routes/arcteryx/regear-new-arrivals.tsx index 6785aa96c410..7394abf81ec0 100644 --- a/lib/routes/arcteryx/regear-new-arrivals.tsx +++ b/lib/routes/arcteryx/regear-new-arrivals.tsx @@ -42,7 +42,7 @@ async function handler() { const data = response.data; const $ = load(data); const contents = $('script:contains("window.__PRELOADED_STATE__")').text(); - const regex = /{.*}/; + const regex = /\{.*\}/; let items = JSON.parse(contents.match(regex)[0]).shop.items; items = items.filter((item) => item.availableSizes.length !== 0); diff --git a/lib/routes/bbc/utils.tsx b/lib/routes/bbc/utils.tsx index abfd5d3bc272..54b8a277ead9 100644 --- a/lib/routes/bbc/utils.tsx +++ b/lib/routes/bbc/utils.tsx @@ -355,7 +355,7 @@ export const extractInitialData = ($: CheerioAPI): any => { const initialDataText = JSON.parse( $('script:contains("window.__INITIAL_DATA__")') .text() - .match(/window\.__INITIAL_DATA__\s*=\s*(.*);/)?.[1] ?? '"{}"' + .match(/window\.__INITIAL_DATA__\s*=\s*(\S.*)?;/)?.[1] ?? '"{}"' ); return JSON.parse(initialDataText); diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index 213727ec6635..c37546af9bcc 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -117,7 +117,7 @@ const getWbiVerifyString = () => { // 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, // 62, 11, 36, 20, 34, 44, 52, // ]; - const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+]/)); + const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+\]/)); const o = []; for (const t of array) { r.charAt(t) && o.push(r.charAt(t)); @@ -350,7 +350,7 @@ const getArticleDataFromCvid = async (cvid, uid) => { const newFormatData = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*(\S.*?)?;\(/)[1] ); if (newFormatData?.readInfo?.opus?.content?.paragraphs) { diff --git a/lib/routes/bjsk/index.ts b/lib/routes/bjsk/index.ts index 11bf8d02db69..e5deceeea0ae 100644 --- a/lib/routes/bjsk/index.ts +++ b/lib/routes/bjsk/index.ts @@ -55,7 +55,8 @@ async function handler(ctx) { item.description = $('.article-main').html(); item.author = $('.info') .text() - .match(/作者:(.*)\s+来源/)[1]; + .match(/作者:(.*?)来源/)[1] + .trim(); return item; }) ) diff --git a/lib/routes/bjtu/gs.ts b/lib/routes/bjtu/gs.ts index dec0963cf217..4711d23ba439 100644 --- a/lib/routes/bjtu/gs.ts +++ b/lib/routes/bjtu/gs.ts @@ -130,7 +130,7 @@ const getItem = (item, selector) => { const newsDate = item .find('span') .text() - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)[0]; + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)[0]; const infoTitle = newsInfo.text(); const link = rootURL + newsInfo.attr('href'); diff --git a/lib/routes/bjwxdxh/index.ts b/lib/routes/bjwxdxh/index.ts index b90d7a8aff02..d8060cc9afcd 100644 --- a/lib/routes/bjwxdxh/index.ts +++ b/lib/routes/bjwxdxh/index.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const content = load(response.data); const info = content('div.info') .text() - .match(/作者:(.*?)\s+发布于:(.*?\s+.*?)\s/); + .match(/作者:(\S*)\s+发布于:(\S*\s+.*?)\s/); item.author = info[1]; item.pubDate = timezone(parseDate(info[2], 'YYYY-MM-DD HH:mm:ss'), +8); item.description = content('div#con').html().trim().replaceAll('\n', ''); diff --git a/lib/routes/bloomberg/utils.ts b/lib/routes/bloomberg/utils.ts index 215c3b716a57..3d9da657224d 100644 --- a/lib/routes/bloomberg/utils.ts +++ b/lib/routes/bloomberg/utils.ts @@ -55,7 +55,7 @@ const apiEndpoints = { }, }; -const pageTypeRegex1 = /\/(?[\w-]*?)\/(?\d{4}-\d{2}-\d{2}\/.*)/; +const pageTypeRegex1 = /\/(?[\w-]*)\/(?\d{4}-\d{2}-\d{2}\/.*)/; const pageTypeRegex2 = /(?features\/|graphics\/)(?.*)/; const regex = [pageTypeRegex1, pageTypeRegex2]; diff --git a/lib/routes/booru/mmda.ts b/lib/routes/booru/mmda.ts index 69cc18361fa6..779af1236384 100644 --- a/lib/routes/booru/mmda.ts +++ b/lib/routes/booru/mmda.ts @@ -95,7 +95,7 @@ async function handler(ctx) { statisticsTages.find('li, br, strong').remove(); const statisticsStr = statisticsTages.text(); - const regex = /(?[^\s:]+)\s*:\s*(?.+)/gm; + const regex = /(?[^\s:]+)\s*:\s*(?.+)/g; const result = {}; for (const match of statisticsStr.matchAll(regex)) { const { key, value } = match.groups ?? ({} as { key: string; value: string }); diff --git a/lib/routes/bse/index.ts b/lib/routes/bse/index.ts index 6154cf51cdc6..0add93200213 100644 --- a/lib/routes/bse/index.ts +++ b/lib/routes/bse/index.ts @@ -183,7 +183,7 @@ async function handler(ctx) { }, }); - const data = JSON.parse(response.data.match(/null\(\[({.*})]\)/)[1]); + const data = JSON.parse(response.data.match(/null\(\[(\{.*\})\]\)/)[1]); let items: DataItem[]; diff --git a/lib/routes/caixin/category.ts b/lib/routes/caixin/category.ts index 0fd9f0e2072b..9e4e7dc832c0 100644 --- a/lib/routes/caixin/category.ts +++ b/lib/routes/caixin/category.ts @@ -60,7 +60,7 @@ async function handler(ctx) { const entity = JSON.parse( $('script') .text() - .match(/var entity = ({.*?})/)[1] + .match(/var entity = (\{.*?\})/)[1] ); const { diff --git a/lib/routes/caixin/utils-fulltext.ts b/lib/routes/caixin/utils-fulltext.ts index e17c54b6b391..bc90876c35aa 100644 --- a/lib/routes/caixin/utils-fulltext.ts +++ b/lib/routes/caixin/utils-fulltext.ts @@ -14,7 +14,7 @@ export async function getFulltext(url: string) { if (!config.caixin.cookie) { return; } - if (!/(\d+)\.html/.test(url)) { + if (!/\d+\.html/.test(url)) { return; } const articleID = url.match(/(\d+)\.html/)[1]; diff --git a/lib/routes/cas/sim/kyjz.ts b/lib/routes/cas/sim/kyjz.ts index 116fc6ccb316..1e61deea23fd 100644 --- a/lib/routes/cas/sim/kyjz.ts +++ b/lib/routes/cas/sim/kyjz.ts @@ -55,7 +55,7 @@ async function handler() { const $ = load(response.data); const author = $('.qtinfo.hidden-lg.hidden-md.hidden-sm').text(); - const reg = /文章来源:(.*?)\|/g; + const reg = /文章来源:(.*?)\|/; item.title = $('p.wztitle').text().trim(); item.author = reg.exec(author)[1].toString().trim(); diff --git a/lib/routes/chinaratings/credit-research.ts b/lib/routes/chinaratings/credit-research.ts index 7baab932be09..55259a9f6a76 100644 --- a/lib/routes/chinaratings/credit-research.ts +++ b/lib/routes/chinaratings/credit-research.ts @@ -59,7 +59,7 @@ export const handler = async (ctx: Context): Promise => { const metaStr: string = $$('div.newshead p span, div.title p span').text(); const pubDateStr: string | undefined = metaStr?.match(/(\d{4}-\d{2}-\d{2})/)?.[1]; - const authors: DataItem['author'] = metaStr?.match(/来源:(.*?)/)?.[1]; + const authors: DataItem['author'] = metaStr?.match(/来源:(.*)/)?.[1]; const upDatedStr: string | undefined = pubDateStr; let processedItem: DataItem = { diff --git a/lib/routes/chnmus/exhibition.tsx b/lib/routes/chnmus/exhibition.tsx index 96bf680ef668..8fae9bb87598 100644 --- a/lib/routes/chnmus/exhibition.tsx +++ b/lib/routes/chnmus/exhibition.tsx @@ -17,7 +17,7 @@ const extractDates = (durationStr: string) => { return { startDate, endDate }; } - const parts = durationStr.split(/——|-|—|~/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency + const parts = durationStr.split(/——|[-—~]/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency const startStr = parts[0]; const endStr = parts[1]; diff --git a/lib/routes/cih-index/report.ts b/lib/routes/cih-index/report.ts index b3f6a0263a49..a61197bdbdfe 100644 --- a/lib/routes/cih-index/report.ts +++ b/lib/routes/cih-index/report.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*({.*?});/)?.[1] || '{}' + .match(/window\.__INITIAL_STATE__\s*=\s*(\{.*?\});/)?.[1] || '{}' ); const { dataResult, indNavLists, secondNameFilter, tagList, param } = initialState.data; diff --git a/lib/routes/cisia/index.ts b/lib/routes/cisia/index.ts index c1e5832b935f..df9098622297 100644 --- a/lib/routes/cisia/index.ts +++ b/lib/routes/cisia/index.ts @@ -35,7 +35,7 @@ export const handler = async (ctx) => { items = await Promise.all( items.map((item) => cache.tryGet(item.link, async () => { - if (!/^https?:\/\/www\.cisia\.org(\/[^\s]*)?$/.test(item.link)) { + if (!/^https?:\/\/www\.cisia\.org(?:\/\S*)?$/.test(item.link)) { return item; } diff --git a/lib/routes/cline/blog.ts b/lib/routes/cline/blog.ts index 7455759c8df2..4ae95ce50cea 100644 --- a/lib/routes/cline/blog.ts +++ b/lib/routes/cline/blog.ts @@ -21,7 +21,7 @@ function extractArticlesFromDOM($: CheerioAPI): DataItem[] { // Extract date and author with single regex const metaText = element.find('.text-sm.text-slate-500').text().trim(); - const metaMatch = metaText.match(/^([^•]+)\s*•\s*([A-Za-z]+\s+\d{1,2},?\s+\d{4})/); + const metaMatch = metaText.match(/^([^•]+)•\s*([A-Z]+\s+\d{1,2},?\s+\d{4})/i); const author = metaMatch ? metaMatch[1].trim() : 'Cline Team'; const pubDate = metaMatch ? parseDate(metaMatch[2]) : undefined; diff --git a/lib/routes/cool18/index.ts b/lib/routes/cool18/index.ts index f9d9da1b1453..96b9f7b50aef 100644 --- a/lib/routes/cool18/index.ts +++ b/lib/routes/cool18/index.ts @@ -65,7 +65,7 @@ function buildUrl(rootUrl: string, type: PostType, keyword: string | undefined, function extractHomeList($: CheerioAPI, rootUrl: string, limit: number): DataItem[] { try { const scriptText = $('script:contains("_PageData")').text(); - const match = scriptText.match(/const\s+_PageData\s*=\s*(\[[\s\S]*?]);/); + const match = scriptText.match(/const\s+_PageData\s*=\s*(\[[\s\S]*?\]);/); if (!match?.[1]) { return []; diff --git a/lib/routes/ctinews/topic.ts b/lib/routes/ctinews/topic.ts index ec1d4b745feb..9560bcacbfb1 100644 --- a/lib/routes/ctinews/topic.ts +++ b/lib/routes/ctinews/topic.ts @@ -88,7 +88,7 @@ async function handler(ctx) { const $ = load(response); if (item.link?.includes('/videos/')) { const ldJson = JSON.parse($('script[type="application/ld+json"]:contains("VideoObject")').text()); - const videoId = ldJson.embedUrl.match(/embed\/([a-zA-Z0-9_-]+)/)?.[1]; + const videoId = ldJson.embedUrl.match(/embed\/([\w-]+)/)?.[1]; item.description = `
` + diff --git a/lib/routes/dailypush/utils.ts b/lib/routes/dailypush/utils.ts index 1fd24ec3ad85..8a55e7305ea0 100644 --- a/lib/routes/dailypush/utils.ts +++ b/lib/routes/dailypush/utils.ts @@ -143,7 +143,7 @@ function extractCategories(article: ReturnType, $: CheerioAPI): stri const tagText = tagElement.text().trim(); // Skip summary/stats links and navigation - if (tagHref && tagText && !tagHref.includes('article/') && !tagHref.includes('Summary') && tagText.length < 50 && !/^(Summary|stats|About|Tags|Toggle|Trending|Latest|Previous|Next)$/i.test(tagText)) { + if (tagHref && tagText && !tagHref.includes('article/') && !tagHref.includes('Summary') && tagText.length < 50 && !/^(?:Summary|stats|About|Tags|Toggle|Trending|Latest|Previous|Next)$/i.test(tagText)) { return tagText; } return null; diff --git a/lib/routes/daum/potplayer.ts b/lib/routes/daum/potplayer.ts index 919afa2166b1..960c98389b6d 100644 --- a/lib/routes/daum/potplayer.ts +++ b/lib/routes/daum/potplayer.ts @@ -20,14 +20,14 @@ export const handler = async (ctx: Context): Promise => { // Group 3: Trailing hyphens (unused, but for context) // Group 4: Update content // Uses global and multiline flags for all matches and line start/end anchors - const updateRegex = /^(-+)\s*\n(.*?)\s*\n(-+)\s*\n([\s\S]*?)(?=\n-{2,}|<\/p>)/gm; + const updateRegex = /^-+[^\S\n]*\n(.*)\r?\n-+[^\S\n]*\n([\s\S]*?)(?=\n-{2}|<\/p>)/gm; const items: DataItem[] = []; let match: RegExpExecArray | null; while ((match = updateRegex.exec(response)) !== null && items.length < limit) { - const headerLine: string | undefined = match[2].trim(); - const description: string | undefined = match[4].trim()?.replaceAll(/(\s[+-])/g, '
$1'); + const headerLine: string | undefined = match[1].trim(); + const description: string | undefined = match[2].trim()?.replaceAll(/(\s[+-])/g, '
$1'); let version = 'N/A'; let pubDateStr: string | undefined = undefined; diff --git a/lib/routes/dayanzai/index.ts b/lib/routes/dayanzai/index.ts index abb37cb3ab5c..11db2149fedd 100644 --- a/lib/routes/dayanzai/index.ts +++ b/lib/routes/dayanzai/index.ts @@ -41,7 +41,7 @@ async function handler(ctx) { const response = await got.get(currentUrl); const $ = load(response.data); const lists = $('div.c-box > div > div.c-zx-list > ul > li'); - const reg = /日期:(.*?(\s\(.*?\))?)\s/; + const reg = /日期:(.*?(?:\s\(.*?\))?)\s/; const list = lists.toArray().map((item) => { item = $(item).find('div'); let date = reg.exec(item.find('div.r > p.other').text())[1]; diff --git a/lib/routes/dcard/utils.ts b/lib/routes/dcard/utils.ts index 083c736d6dd6..35bdc8594370 100644 --- a/lib/routes/dcard/utils.ts +++ b/lib/routes/dcard/utils.ts @@ -28,7 +28,7 @@ const ProcessFeed = async (items, cookies, browser, limit, cache) => { const data = JSON.parse(response); let body = data.content; body = body.replaceAll(/(?=https?:\/\/).*?(?<=\.(jpe?g|gif|png))/gi, (m) => ``); - body = body.replaceAll(/(?=https?:\/\/).*(??)$/gim, (m) => `${m}`); + body = body.replaceAll(/(?=https?:\/\/).+(??)$/gim, (m) => `${m}`); body = body.replaceAll('\n', '
'); return body; diff --git a/lib/routes/dealstreetasia/home.ts b/lib/routes/dealstreetasia/home.ts index e0959e5b0ef0..177bbf62e7ce 100644 --- a/lib/routes/dealstreetasia/home.ts +++ b/lib/routes/dealstreetasia/home.ts @@ -60,7 +60,7 @@ async function fetchPage() { link: item.post_url || item.link || '', description: item.post_excerpt || item.excerpt || '', pubDate: item.post_date ? new Date(item.post_date).toUTCString() : item.date ? new Date(item.date).toUTCString() : '', - category: item.category_link ? item.category_link.replaceAll(/(<([^>]+)>)/gi, '') : '', // Clean HTML if category_link exists + category: item.category_link ? item.category_link.replaceAll(/(<([^>]+)>)/g, '') : '', // Clean HTML if category_link exists image: item.image_url ? item.image_url.replace(/\?.*$/, '') : '', // Remove query parameters if image_url exists })); diff --git a/lib/routes/dedao/knowledge.tsx b/lib/routes/dedao/knowledge.tsx index 076b8e9a7ba4..d5eb7c28c293 100644 --- a/lib/routes/dedao/knowledge.tsx +++ b/lib/routes/dedao/knowledge.tsx @@ -5,7 +5,7 @@ import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -const mentionPattern = /<\u2267\u2746>{"name":"(.*?)","uid":"\d+","at":"1"}<\/\u2266\u2746>/g; +const mentionPattern = /<\u2267\u2746>\{"name":"(.*?)","uid":"\d+","at":"1"\}<\/\u2266\u2746>/g; const formatNoteText = (text = '') => text.replaceAll('\n\n', '

').replaceAll(mentionPattern, ' @$1'); diff --git a/lib/routes/dedao/user.tsx b/lib/routes/dedao/user.tsx index 164618039768..39ebb3772c8d 100644 --- a/lib/routes/dedao/user.tsx +++ b/lib/routes/dedao/user.tsx @@ -11,7 +11,7 @@ const types = { 12: '视频', }; -const mentionPattern = /<\u2267\u2746>{"name":"(.*?)","uid":"\d+","at":"1"}<\/\u2266\u2746>/g; +const mentionPattern = /<\u2267\u2746>\{"name":"(.*?)","uid":"\d+","at":"1"\}<\/\u2266\u2746>/g; const formatNoteText = (text = '') => text.replaceAll('\n\n', '

').replaceAll(mentionPattern, ' @$1'); diff --git a/lib/routes/dehenglaw/index.ts b/lib/routes/dehenglaw/index.ts index c5966d874ace..325ecf55d099 100644 --- a/lib/routes/dehenglaw/index.ts +++ b/lib/routes/dehenglaw/index.ts @@ -70,7 +70,7 @@ export const handler = async (ctx) => { return { title: $('title') .text() - .replace(/\|.*?$/, `| ${$('li.onthis').text()}`), + .replace(/\|.*$/, `| ${$('li.onthis').text()}`), description: $('meta[name="Description"]').prop('content'), link: currentUrl, item: items, diff --git a/lib/routes/dianping/user.ts b/lib/routes/dianping/user.ts index 6698f1aaeb0c..9f7cf14ab99f 100644 --- a/lib/routes/dianping/user.ts +++ b/lib/routes/dianping/user.ts @@ -75,7 +75,7 @@ async function handler(ctx) { headerGeneratorOptions: PRESETS.MODERN_IOS, }); - const nickNameReg = /window\.nickName = "(.*?)"/g; + const nickNameReg = /window\.nickName = "(.*?)"/; const nickName = nickNameReg.exec(pageResponse as string)?.[1]; const response = await ofetch(`https://m.dianping.com/member/ajax/NobleUserFeeds?userId=${id}`, { diff --git a/lib/routes/dlnews/category.tsx b/lib/routes/dlnews/category.tsx index a30277817a10..d813b90c0731 100644 --- a/lib/routes/dlnews/category.tsx +++ b/lib/routes/dlnews/category.tsx @@ -69,7 +69,7 @@ const extractArticle = (item) => const { data: response } = await got(item.link); const $ = load(response); const scriptTagContent = $('script#fusion-metadata').text(); - const jsonData = JSON.parse(scriptTagContent.match(/Fusion\.globalContent=({.*?});Fusion\.globalContentConfig/)[1]).content_elements; + const jsonData = JSON.parse(scriptTagContent.match(/Fusion\.globalContent=(\{.*?\});Fusion\.globalContentConfig/)[1]).content_elements; const filteredData = []; for (const v of jsonData) { if (v.type === 'header' && v.content.includes('What we’re reading')) { diff --git a/lib/routes/dnaindia/common.ts b/lib/routes/dnaindia/common.ts index e501d9b50378..180919d42009 100644 --- a/lib/routes/dnaindia/common.ts +++ b/lib/routes/dnaindia/common.ts @@ -44,7 +44,7 @@ export async function handler(ctx) { .map((item) => $(item).find('a').text()); // Process date const timeText = $('p.dna-update').text(); - const dateMatch = timeText.match(/Updated\s*:\s*([\w\s,:\d]+?)(?:\s*\||$)/); + const dateMatch = timeText.match(/Updated\s*:([\w\s,:]+)/); let time = dateMatch ? dateMatch[1].trim() : ''; time = time.replace(/\s+IST$/, ''); const pubDate = timezone(parseDate(time), +5.5); diff --git a/lib/routes/domp4/detail.ts b/lib/routes/domp4/detail.ts index ddeaf4515008..a8500872b358 100644 --- a/lib/routes/domp4/detail.ts +++ b/lib/routes/domp4/detail.ts @@ -30,7 +30,7 @@ function getDomList($, detailUrl) { export function getItemList($, detailUrl, second) { const encoded = $('.article script[type]') .text() - .match(/return p}\('(.*)',(\d+),(\d+),'(.*)'.split\(/); + .match(/return p\}\('(.*)',(\d+),(\d+),'(.*)'.split\(/); // 若 script 标签没有内容,直接解析 dom if (!encoded) { return getDomList($, detailUrl); diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index c927dd76828b..7d1b116ca7e0 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -145,7 +145,7 @@ const ProcessFeedType3 = (item, response) => { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*((?:\S.*?)??);\(/)[1] ); // filter out undefined item diff --git a/lib/routes/dora-world/article.ts b/lib/routes/dora-world/article.ts index c903ee1cce78..4c44d1392599 100644 --- a/lib/routes/dora-world/article.ts +++ b/lib/routes/dora-world/article.ts @@ -85,6 +85,6 @@ async function getContent(nextBuildId: string, contentId: string) { content .html() ?.replaceAll(rubyRegex, '$1($2)') - ?.replaceAll(/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '') ?? ''; + ?.replaceAll(/[^\t\n\r\u0020-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/g, '') ?? ''; return description; } diff --git a/lib/routes/douban/other/replied.ts b/lib/routes/douban/other/replied.ts index 23767e6ce856..c231d97ee642 100644 --- a/lib/routes/douban/other/replied.ts +++ b/lib/routes/douban/other/replied.ts @@ -56,7 +56,7 @@ async function handler(ctx) { method: 'get', url: item.link, }); - const match = detailResponse.data.match(/'comments':(.*)}],/); + const match = detailResponse.data.match(/'comments':(.*)\}\],/); if (match.length > 1) { const content = load(detailResponse.data); diff --git a/lib/routes/douban/other/replies.ts b/lib/routes/douban/other/replies.ts index 7d2515a6137a..f77c32e03a9f 100644 --- a/lib/routes/douban/other/replies.ts +++ b/lib/routes/douban/other/replies.ts @@ -55,7 +55,7 @@ async function handler(ctx) { url: item.link, }); - const comments = JSON.parse(detailResponse.data.match(/'comments':(.*)}],/)[1] + '}]'); + const comments = JSON.parse(detailResponse.data.match(/'comments':(.*)\}\],/)[1] + '}]'); for (const c of comments) { if (c.id === item.link.split('#')[1]) { diff --git a/lib/routes/dribbble/utils.tsx b/lib/routes/dribbble/utils.tsx index c7438b556977..c82870fce1b7 100644 --- a/lib/routes/dribbble/utils.tsx +++ b/lib/routes/dribbble/utils.tsx @@ -17,7 +17,7 @@ async function loadContent(link) { const shotData = JSON.parse( $('script') .text() - .match(/shotData:\s({.+?}),\n/)?.[1] ?? '{}' + .match(/shotData:\s(\{.+?\}),\n/)?.[1] ?? '{}' ); // Join multiple shots together by selecting elements with class 'media-shot' or 'main-shot' or 'block-media-wrapper' diff --git a/lib/routes/ehentai/ehapi.ts b/lib/routes/ehentai/ehapi.ts index ba9284beaa7c..271f2fd86f62 100644 --- a/lib/routes/ehentai/ehapi.ts +++ b/lib/routes/ehentai/ehapi.ts @@ -161,7 +161,7 @@ function getBittorrent(cache, bittorrent_page_url) { const match = onclick.match(/'(.*?)'/); if (match) { bittorrent_url = match[1]; - const match_p = bittorrent_url.match(/torrent\?p=(.*?)$/); + const match_p = bittorrent_url.match(/torrent\?p=(.*)$/); if (match_p) { p = match_p[1]; } diff --git a/lib/routes/fanqienovel/page.ts b/lib/routes/fanqienovel/page.ts index 58bc7e73e4da..11fc5bf9d131 100644 --- a/lib/routes/fanqienovel/page.ts +++ b/lib/routes/fanqienovel/page.ts @@ -76,7 +76,7 @@ async function handler(ctx: Context): Promise { const initialState = JSON.parse( $('script:contains("window.__INITIAL_STATE__")') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*);/)?.[1] ?? '{}' + .match(/window\.__INITIAL_STATE__\s*=\s*(\S.*);/)?.[1] ?? '{}' ); const page = initialState.page as Page; diff --git a/lib/routes/flashcat/blog.ts b/lib/routes/flashcat/blog.ts index 8c01e75106e9..0cb95e409253 100644 --- a/lib/routes/flashcat/blog.ts +++ b/lib/routes/flashcat/blog.ts @@ -29,34 +29,32 @@ export const route: Route = { }; async function handlerRoute(): Promise { - const response = await ofetch('https://flashcat.cloud/blog/'); + const baseUrl = 'https://flashcat.cloud'; + const link = `${baseUrl}/blog/`; + const response = await ofetch(link); const $ = load(response); - const items = $('.post-preview') + const items = $('.fc-content-card') .toArray() .map((elem) => { - const $elem = $(elem); + const $item = $(elem); + const [author, date] = $item + .find('.fc-content-card-meta') + .text() + .split('·') + .map((s) => s.trim()); return { - title: $elem.find('.post-title').text(), - description: $elem.find('.post-content-preview').text(), - link: $elem.find('a').attr('href'), - pubDate: parseDate( - $elem - .find('.post-meta') - .text() - .match(/on\s+(\w+,\s+\w+\s+\d{1,2},\s+\d{4})/)?.[1] || '' - ), - author: - $elem - .find('.post-meta') - .text() - .match(/by\s+(.+?)\s+on/)?.[1] || '', + title: $item.find('.fc-content-card-title').text(), + description: $item.find('.fc-content-card-summary').text().trim(), + link: new URL($item.find('.fc-content-card-link').attr('href')!, baseUrl).href, + pubDate: date ? parseDate(date, 'YYYY-MM-DD') : undefined, + author, }; }); return { title: 'Flashcat 快猫星云博客', - link: 'https://flashcat.cloud/blog/', + link, item: items, }; } diff --git a/lib/routes/gamer/ani/anime.ts b/lib/routes/gamer/ani/anime.ts index b73df201138a..0f8a527fac12 100644 --- a/lib/routes/gamer/ani/anime.ts +++ b/lib/routes/gamer/ani/anime.ts @@ -41,7 +41,7 @@ async function handler(ctx) { } const anime = response.data.anime; - const title = anime.title.replaceAll(/\[\d+?]$/g, '').trim(); + const title = anime.title.replaceAll(/\[\d+\]$/g, '').trim(); const items = anime.volumes[0] .map((item) => ({ diff --git a/lib/routes/getitfree/index.ts b/lib/routes/getitfree/index.ts index 47998a802eb7..988886abf8d6 100644 --- a/lib/routes/getitfree/index.ts +++ b/lib/routes/getitfree/index.ts @@ -31,7 +31,7 @@ async function handler(ctx) { const { data: response } = await got(apiUrl); - const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*])$/)[1])).slice(0, limit).map((item) => { + const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*\])$/)[1])).slice(0, limit).map((item) => { const terminologies = item._embedded['wp:term']; const content = load(item.content?.rendered ?? item.content); diff --git a/lib/routes/gigazine/en.ts b/lib/routes/gigazine/en.ts index bd9127f7a4dc..1f0f191b9f11 100644 --- a/lib/routes/gigazine/en.ts +++ b/lib/routes/gigazine/en.ts @@ -22,7 +22,7 @@ const getAbsoluteUrl = (path: string | undefined) => (path ? new URL(path, ROOT_ const getArticleAuthor = ($: ReturnType) => $('#article .items p') .text() - .match(/Posted by\s+(.+)$/)?.[1] + .match(/Posted by\s+(\S.*)$/)?.[1] ?.trim(); const getArticleCategories = ($: ReturnType) => [ ...new Set( diff --git a/lib/routes/globallawreview/index.ts b/lib/routes/globallawreview/index.ts index ea64e6d401a9..7fcf2210623b 100644 --- a/lib/routes/globallawreview/index.ts +++ b/lib/routes/globallawreview/index.ts @@ -50,7 +50,7 @@ async function handler(ctx) { item .find('p.p4') .text() - .match(/] (\d+\.\d+);/)[1], + .match(/\] (\d+\.\d+);/)[1], ], enclosure_url: link, enclosure_length: diff --git a/lib/routes/google/album.ts b/lib/routes/google/album.ts index 39f15f9a91ce..d62eae3dad9c 100644 --- a/lib/routes/google/album.ts +++ b/lib/routes/google/album.ts @@ -28,7 +28,7 @@ async function handler(ctx) { const real_url = response.request.options.url.href; - const info = JSON.parse(response.data.match(/AF_initDataCallback.*?data:(\[[\S\s]*])\s/m)[1]) || []; + const info = JSON.parse(response.data.match(/AF_initDataCallback.*?data:(\[[\s\S]*\])\s/)[1]) || []; const album_name = info[3][1]; const owner_name = info[3][5][2]; diff --git a/lib/routes/google/scholar.ts b/lib/routes/google/scholar.ts index 69465a1ad1e2..661518a0cd25 100644 --- a/lib/routes/google/scholar.ts +++ b/lib/routes/google/scholar.ts @@ -34,7 +34,7 @@ async function handler(ctx) { let description = `Google Scholar Monitor Query: ${query}`; if (params.includes('as_q=')) { - const reg = /as_q=(.*?)&/g; + const reg = /as_q=(.*?)&/; query = reg.exec(params)[1]; description = `Google Scholar Monitor Advanced Query: ${query}`; } else { diff --git a/lib/routes/gov/beijing/kw/index.ts b/lib/routes/gov/beijing/kw/index.ts index bab38a33d515..e1e06e6c5dcc 100644 --- a/lib/routes/gov/beijing/kw/index.ts +++ b/lib/routes/gov/beijing/kw/index.ts @@ -23,10 +23,10 @@ async function handler(ctx) { const title = $('a.bt_link').last().text().replace('>', ''); const dataJs = $('div.left.zhengce_right > script[language="javascript"]').html() || $('div.centent_width > script[language="javascript"]').html(); let items = dataJs - .match(/urls\[i]='(.*?)';headers\[i]="(.*?)";year\[i]='(\d+)';month\[i]='(\d+)';day\[i]='(\d+)';/g) + .match(/urls\[i\]='(.*?)';headers\[i\]="(.*?)";year\[i\]='(\d+)';month\[i\]='(\d+)';day\[i\]='(\d+)';/g) .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25) .map((item) => { - const result = item.match(/urls\[i]='(.*?)';headers\[i]="(.*?)";year\[i]='(\d+)';month\[i]='(\d+)';day\[i]='(\d+)';/); + const result = item.match(/urls\[i\]='(.*?)';headers\[i\]="(.*?)";year\[i\]='(\d+)';month\[i\]='(\d+)';day\[i\]='(\d+)';/); return { title: load(result[2])('a').attr('title') || result[2], link: new URL(result[1], rootUrl).href, diff --git a/lib/routes/gov/cac/index.ts b/lib/routes/gov/cac/index.ts index 1aec93ff592c..5ce00fb02fb3 100644 --- a/lib/routes/gov/cac/index.ts +++ b/lib/routes/gov/cac/index.ts @@ -29,7 +29,7 @@ async function handler(ctx) { .toArray() .map((item) => { const href = $(item).attr('href'); - if (href && /(?:http:)?\/\/www\.cac\.gov\.cn(.*?)\/(A.*?\.htm)/.test(href)) { + if (href && /(?:http:)?\/\/www\.cac\.gov\.cn.*?\/A.*?\.htm/.test(href)) { const matchArray = href.match(/(?:http:)?\/\/www\.cac\.gov\.cn(.*?)\/(A.*?\.htm)/); if (matchArray && matchArray.length > 2) { const path = matchArray[1]; diff --git a/lib/routes/gov/ccdi/utils.ts b/lib/routes/gov/ccdi/utils.ts index ff1366a581c2..39a394241994 100644 --- a/lib/routes/gov/ccdi/utils.ts +++ b/lib/routes/gov/ccdi/utils.ts @@ -10,7 +10,7 @@ const cookieJar = new CookieJar(); const owner = '中央纪委国家监委网站'; const rootUrl = 'https://www.ccdi.gov.cn'; -const regex = /(?[A-Z_]+)=(?(?:.*?(?=; max-age)|[\dA-Fa-f]+))/gm; +const regex = /(?[A-Z_]+)=(?.*?(?=; max-age)|[\dA-Fa-f]+)/g; const parseCookie = async (body) => { let m; diff --git a/lib/routes/gov/cn/news/index.ts b/lib/routes/gov/cn/news/index.ts index 326d19245287..3fa9e6252965 100644 --- a/lib/routes/gov/cn/news/index.ts +++ b/lib/routes/gov/cn/news/index.ts @@ -88,13 +88,13 @@ async function handler(ctx) { let pubDate; let author; let category; - if (/dysMiddleResultConItemTitle/g.test(item.html())) { + if (/dysMiddleResultConItemTitle/.test(item.html())) { if (contentUrl.includes('content')) { fullTextGet = await got.get(contentUrl); fullTextData = load(fullTextGet.data); fullTextData('.shuzi').remove(); // 移除videobg的图片 fullTextData('#myFlash').remove(); // 移除flash - description = /pages_content/g.test(fullTextData.html()) ? fullTextData('.pages_content').html() : fullTextData('#UCAP-CONTENT').html(); + description = /pages_content/.test(fullTextData.html()) ? fullTextData('.pages_content').html() : fullTextData('#UCAP-CONTENT').html(); } else { description = item.find('a').text(); // 忽略获取吹风会的全文 } @@ -105,13 +105,13 @@ async function handler(ctx) { pubDate = timezone(parseDate(fullTextData('meta[name="firstpublishedtime"]').attr('content'), 'YYYY-MM-DD HH:mm:ss'), 8); author = fullTextData('meta[name="author"]').attr('content'); category = fullTextData('meta[name="keywords"]').attr('content').split(/[,;]/); - if (/zhengceku/g.test(contentUrl)) { + if (/zhengceku/.test(contentUrl)) { // 政策文件库 description = fullTextData('.pages_content').html(); } else { fullTextData('.shuzi').remove(); // 移除videobg的图片 fullTextData('#myFlash').remove(); // 移除flash - description = /UCAP-CONTENT/g.test($1) ? fullTextData('#UCAP-CONTENT').html() : fullTextData('body').html(); + description = /UCAP-CONTENT/.test($1) ? fullTextData('#UCAP-CONTENT').html() : fullTextData('body').html(); } } else { description = item.find('a').text(); // 忽略获取吹风会的全文 diff --git a/lib/routes/gov/general/general.ts b/lib/routes/gov/general/general.ts index 7766acb8ab56..db03fe427ceb 100644 --- a/lib/routes/gov/general/general.ts +++ b/lib/routes/gov/general/general.ts @@ -186,7 +186,7 @@ const gdgov = async (info, ctx) => { title: data.art_title, description: renderZcjdpt(data), pubDate: timezone(parseDate(data.pub_time), +8), - author: /(本|本网|本站)/.test(data.pub_unite) ? authorisme : data.pub_unite, + author: /本/.test(data.pub_unite) ? authorisme : data.pub_unite, }; }); } else if (idlink.host === 'mp.weixin.qq.com') { @@ -217,7 +217,7 @@ const gdgov = async (info, ctx) => { title, description, pubDate: timezone(parseDate(pubDate, pubDate_format), +8), - author: /本|本网|本站/.test(author) ? authorisme : author, + author: /本/.test(author) ? authorisme : author, }; }); } diff --git a/lib/routes/gov/guangdong/tqyb/tfxtq.tsx b/lib/routes/gov/guangdong/tqyb/tfxtq.tsx index 3dde4b33aa3d..a2c09e3e2e0b 100644 --- a/lib/routes/gov/guangdong/tqyb/tfxtq.tsx +++ b/lib/routes/gov/guangdong/tqyb/tfxtq.tsx @@ -34,7 +34,7 @@ async function handler() { const tfxtqJsUrl = `${rootUrl}/data/gzWeather/weatherTips.js`; const response = await got.get(tfxtqJsUrl); - const data = JSON.parse(`[{${response.data.match(/Tips = {(.*?)}/)[1]}}]`); + const data = JSON.parse(`[{${response.data.match(/Tips = \{(.*?)\}/)[1]}}]`); const items = data.map((item) => ({ title: item.title, diff --git a/lib/routes/gov/miit/wjfb.ts b/lib/routes/gov/miit/wjfb.ts index 13c53ff141a6..0f4252db3ff6 100644 --- a/lib/routes/gov/miit/wjfb.ts +++ b/lib/routes/gov/miit/wjfb.ts @@ -72,7 +72,7 @@ async function handler(ctx) { item.description = content('#con_con') .html() - ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); + ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); return item; }) diff --git a/lib/routes/gov/miit/yjzj.ts b/lib/routes/gov/miit/yjzj.ts index 2b9deec1dd29..8138f23f6458 100644 --- a/lib/routes/gov/miit/yjzj.ts +++ b/lib/routes/gov/miit/yjzj.ts @@ -71,7 +71,7 @@ async function handler() { item.description = content('#con_con') .html() - ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); + ?.replaceAll(/()/g, '$1' + rootUrl + '$2$3'); return item; }) diff --git a/lib/routes/gov/mofcom/article.ts b/lib/routes/gov/mofcom/article.ts index 6fd06e75fc23..d861364e4c69 100644 --- a/lib/routes/gov/mofcom/article.ts +++ b/lib/routes/gov/mofcom/article.ts @@ -47,7 +47,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { let responses = await got(item.link); // xwfb/xwlxfbh || xwfb/xwztfbh - const redirect = responses.data.match(/_cofing1={href:"(.*)",type/) || responses.data.match(/window\.location\.href='(.*)'/); + const redirect = responses.data.match(/_cofing1=\{href:"(.*)",type/) || responses.data.match(/window\.location\.href='(.*)'/); if (redirect) { responses = await got(redirect[1], { headers: { diff --git a/lib/routes/gov/nrta/news.ts b/lib/routes/gov/nrta/news.ts index 8cd9d7ee0530..519f292405b2 100644 --- a/lib/routes/gov/nrta/news.ts +++ b/lib/routes/gov/nrta/news.ts @@ -45,7 +45,7 @@ async function handler(ctx) { url: currentUrl, }); - const regex = /(?=\s*<)/gi; + const regex = /(?=\s*<)/gi; const data = response.data.replaceAll(regex, '$1'); const $ = load(data, { diff --git a/lib/routes/gov/nsfc/index.ts b/lib/routes/gov/nsfc/index.ts index 92a4320430e3..124e308b1f6b 100644 --- a/lib/routes/gov/nsfc/index.ts +++ b/lib/routes/gov/nsfc/index.ts @@ -46,7 +46,7 @@ async function handler(ctx) { title: item.prop('title') ?? item.text(), link: new URL(item.prop('href'), rootUrl).href, guid: `nsfc-${item.prop('id')}`, - pubDate: parseDate(item.next().text().replace(/\[]/g, '', ['YYYY-MM-DD', 'YY-MM-DD'])), + pubDate: parseDate(item.next().text().replace(/\[\]/g, '', ['YYYY-MM-DD', 'YY-MM-DD'])), }; }); diff --git a/lib/routes/gov/stats/index.tsx b/lib/routes/gov/stats/index.tsx index c7d31461d507..b43af12be027 100644 --- a/lib/routes/gov/stats/index.tsx +++ b/lib/routes/gov/stats/index.tsx @@ -115,7 +115,7 @@ async function handler(ctx) { // articles from www.news.cn or www.gov.cn - if (/(news\.cn|www\.gov\.cn)/.test(item.link)) { + if (/news\.cn|www\.gov\.cn/.test(item.link)) { if (content('.year').text()) { item.pubDate = timezone(parseDate(`${content('.year').text()}/${content('.day').text()} ${content('.time').text()}`, 'YYYY/MM/DD HH:mm:ss'), +8); item.author = content('.source') diff --git a/lib/routes/gov/zhengce/govall.ts b/lib/routes/gov/zhengce/govall.ts index a1f13b5aa565..ca70133c1d3b 100644 --- a/lib/routes/gov/zhengce/govall.ts +++ b/lib/routes/gov/zhengce/govall.ts @@ -52,7 +52,7 @@ async function handler(ctx) { }); const query = `${params.toString()}&${advance}`; const res = await got.get(link, { - searchParams: query.replaceAll(/([\u4E00-\u9FA5])/g, (str) => encodeURIComponent(str)), + searchParams: query.replaceAll(/[\u4E00-\u9FA5]/g, (str) => encodeURIComponent(str)), }); const $ = load(res.data); diff --git a/lib/routes/gov/zj/ningbogzw-notice.ts b/lib/routes/gov/zj/ningbogzw-notice.ts index eda9789d0f27..61b26a86aafe 100644 --- a/lib/routes/gov/zj/ningbogzw-notice.ts +++ b/lib/routes/gov/zj/ningbogzw-notice.ts @@ -35,7 +35,7 @@ export const route: Route = { return { title: `宁波市国资委-${noticeCate}:${title.text()}`, link: `http://gzw.ningbo.gov.cn${title.attr('href')}`, - pubDate: parseDate($('p').text().replaceAll(/\[|]/g, '')), + pubDate: parseDate($('p').text().replaceAll(/\[|\]/g, '')), author: '宁波市国资委', description: title.text(), }; diff --git a/lib/routes/gov/zj/ningborsjnotice.ts b/lib/routes/gov/zj/ningborsjnotice.ts index e3f54a2b08fa..d9e06dad3f66 100644 --- a/lib/routes/gov/zj/ningborsjnotice.ts +++ b/lib/routes/gov/zj/ningborsjnotice.ts @@ -35,7 +35,7 @@ export const route: Route = { return { title: `宁波人社公告-${noticeCate}:${title.text()}`, link: `http://rsj.ningbo.gov.cn${title.attr('href')}`, - pubDate: parseDate($('.news_date').text().replaceAll(/\[|]/g, '')), + pubDate: parseDate($('.news_date').text().replaceAll(/\[|\]/g, '')), author: '宁波市人力资源和社会保障局', description: title.text(), }; diff --git a/lib/routes/guancha/personalpage.ts b/lib/routes/guancha/personalpage.ts index 74f482dde583..13f8118d93df 100644 --- a/lib/routes/guancha/personalpage.ts +++ b/lib/routes/guancha/personalpage.ts @@ -38,7 +38,7 @@ async function handler(ctx) { const minuteRelativeTime = /(\d+)\s*分钟前/; const hourRelativeTime = /(\d+)\s*小时前/; const yesterdayRelativeTime = /昨天\s*(\d+):(\d+)/; - const shortDate = /(\d+)-(\d+)\s*(\d+):(\d+)/; + const shortDate = /(\d+)-(\d+)\s+(\d+):(\d+)/; // offset to ADD for transforming China time to UTC const chinaToUtcOffset = -8 * 3600 * 1000; diff --git a/lib/routes/hk01/utils.tsx b/lib/routes/hk01/utils.tsx index 863be3af21b3..52ede4cd3bb4 100644 --- a/lib/routes/hk01/utils.tsx +++ b/lib/routes/hk01/utils.tsx @@ -110,7 +110,7 @@ const ProcessItems = (items, limit, tryGet) => url: item.link, }); - const content = JSON.parse(detailResponse.data.match(/"__NEXT_DATA__" type="application\/json">({"props":.*})<\/script>/)[1]); + const content = JSON.parse(detailResponse.data.match(/"__NEXT_DATA__" type="application\/json">(\{"props":.*\})<\/script>/)[1]); item.description = renderDescription({ image: content.props.initialProps.pageProps.article.originalImage.cdnUrl, diff --git a/lib/routes/hkej/index.tsx b/lib/routes/hkej/index.tsx index eeeacd849e3c..a1be1a98cc3b 100644 --- a/lib/routes/hkej/index.tsx +++ b/lib/routes/hkej/index.tsx @@ -158,7 +158,7 @@ async function handler(ctx) { .toArray() .map((e) => content(e).text().trim()); item.description = renderDesc(articleImg, content('div#article-content').html()); - item.pubDate = timezone(/(今|昨)/.test(pubDate) ? parseRelativeDate(pubDate) : parseDate(pubDate, 'YYYY M D'), +8); + item.pubDate = timezone(/今|昨/.test(pubDate) ? parseRelativeDate(pubDate) : parseDate(pubDate, 'YYYY M D'), +8); return item; }) diff --git a/lib/routes/hongkong/chp.ts b/lib/routes/hongkong/chp.ts index 04de3eac81e6..916cd5f1d9cc 100644 --- a/lib/routes/hongkong/chp.ts +++ b/lib/routes/hongkong/chp.ts @@ -70,7 +70,7 @@ async function handler(ctx) { url: apiUrl, }); - const list = JSON.parse(response.data.match(/"data":(\[{.*}])}/)[1]).map((item) => { + const list = JSON.parse(response.data.match(/"data":(\[\{.*\}\])\}/)[1]).map((item) => { let link: string; if (item.UrlPath_en) { diff --git a/lib/routes/hpoi/utils.ts b/lib/routes/hpoi/utils.ts index 3ca6700c4200..5168114c2e71 100644 --- a/lib/routes/hpoi/utils.ts +++ b/lib/routes/hpoi/utils.ts @@ -24,7 +24,7 @@ const MAPs = { }; const ProcessFeed = async (type, id, order) => { - let link = MAPs[type].url.replace(/{id}/, id).replace(/{order}/, order || 'add'); + let link = MAPs[type].url.replace(/\{id\}/, id).replace(/\{order\}/, order || 'add'); let response = await got({ method: 'get', url: link, @@ -35,7 +35,7 @@ const ProcessFeed = async (type, id, order) => { let $ = load(response.data); if (type === 'work') { - const overviewLink = MAPs.overview.url.replace(/{id}/, id); + const overviewLink = MAPs.overview.url.replace(/\{id\}/, id); const overviewResponse = await got({ method: 'get', url: overviewLink, diff --git a/lib/routes/hupu/utils.ts b/lib/routes/hupu/utils.ts index 2e6dba7106c5..7cce28d1530d 100644 --- a/lib/routes/hupu/utils.ts +++ b/lib/routes/hupu/utils.ts @@ -238,8 +238,8 @@ export function getEntryDetails(item: DataItem): Promise { // Possible formats: 10:21, 45分钟前, 09-15 19:57 const currentYear = new Date().getFullYear(); const currentDate = new Date(); - const monthDayTimePattern = /^(\d{2})-(\d{2}) (\d{2}):(\d{2})$/; - const timeOnlyPattern = /^(\d{1,2}):(\d{2})$/; + const monthDayTimePattern = /^\d{2}-\d{2} \d{2}:\d{2}$/; + const timeOnlyPattern = /^\d{1,2}:\d{2}$/; let processedDateString = pubDateString; if (monthDayTimePattern.test(pubDateString)) { diff --git a/lib/routes/hypergryph/arknights/arktca.ts b/lib/routes/hypergryph/arknights/arktca.ts index 2a53156a8d92..53a08ca48efa 100644 --- a/lib/routes/hypergryph/arknights/arktca.ts +++ b/lib/routes/hypergryph/arknights/arktca.ts @@ -48,7 +48,7 @@ async function handler() { allUrlList.map(async (item) => { const { data: response } = await got(item); const $$ = load(response); - const regVol = /(?<=Vol. )(\w+)/; + const regVol = /(?<=Vol. )\w+/; const match = regVol.exec($$('div.vp-page-title').find('h1').text()); const volume = match ? match[0] : ''; const links = $$('div.theme-hope-content > ul a') diff --git a/lib/routes/ifeng/feng.ts b/lib/routes/ifeng/feng.ts index edea32c3d7fe..3f5b86db2bb9 100644 --- a/lib/routes/ifeng/feng.ts +++ b/lib/routes/ifeng/feng.ts @@ -67,7 +67,7 @@ async function handler(ctx) { const _allData = JSON.parse( $('script') .text() - .match(/var allData = ({.*?});/)[1] + .match(/var allData = (\{.*?\});/)[1] ); if (type === 'doc') { item.description = extractDoc(_allData.docData.contentData.contentList); diff --git a/lib/routes/ifeng/news.tsx b/lib/routes/ifeng/news.tsx index b8b86ec9d35a..050b5119a27d 100644 --- a/lib/routes/ifeng/news.tsx +++ b/lib/routes/ifeng/news.tsx @@ -29,7 +29,7 @@ async function handler(ctx) { const $ = load(response.data); - const newsStream = JSON.parse(response.data.match(/"newsstream":(\[.*?]),"cooperation"/)[1]); + const newsStream = JSON.parse(response.data.match(/"newsstream":(\[.*?\]),"cooperation"/)[1]); let items = newsStream.slice(0, limit).map((item) => ({ title: item.title, @@ -47,9 +47,9 @@ async function handler(ctx) { }); item.author = detailResponse.data.match(/"editorName":"(.*?)",/)[1]; - item.category = detailResponse.data.match(/},"keywords":"(.*?)",/)[1].split(','); + item.category = detailResponse.data.match(/\},"keywords":"(.*?)",/)[1].split(','); const image = item.description; - const description = JSON.parse(detailResponse.data.match(/"contentList":(\[.*?]),/)[1]).map((content) => content.data); + const description = JSON.parse(detailResponse.data.match(/"contentList":(\[.*?\]),/)[1]).map((content) => content.data); item.description = renderToString( <> {image ? ( diff --git a/lib/routes/inewsweek/index.ts b/lib/routes/inewsweek/index.ts index 24233919e50a..0fc92c9ca186 100644 --- a/lib/routes/inewsweek/index.ts +++ b/lib/routes/inewsweek/index.ts @@ -62,7 +62,7 @@ async function handler(ctx) { parseDate( $('div.editor') .html() - .split(/(\s\s+)/)[2] + .split(/(\s{2,})/)[2] ), +8 ); diff --git a/lib/routes/iwara/utils.ts b/lib/routes/iwara/utils.ts index 573e3a1ef114..90f05e4a3371 100644 --- a/lib/routes/iwara/utils.ts +++ b/lib/routes/iwara/utils.ts @@ -19,7 +19,7 @@ export const parseThumbnail = (type: 'video' | 'image', item: any) => { } // regex borrowed from https://stackoverflow.com/a/3726073 - const match = /https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w-]*)(&(amp;)?[\w=?]*)?/.exec(item.embedUrl); + const match = /https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w-]*)(?:&(?:amp;)?[\w=?]*)?/.exec(item.embedUrl); if (match) { return ``; } diff --git a/lib/routes/ixigua/user-video.tsx b/lib/routes/ixigua/user-video.tsx index 6e87671c00b4..0b0ad9cead79 100644 --- a/lib/routes/ixigua/user-video.tsx +++ b/lib/routes/ixigua/user-video.tsx @@ -44,7 +44,7 @@ async function handler(ctx) { throw new Error('Failed to find SSR_HYDRATED_DATA'); } - const jsonData = JSON.parse(jsData.match(/var\s+data\s*=\s*({.*?});/s)?.[1].replaceAll('undefined', 'null') || '{}'); + const jsonData = JSON.parse(jsData.match(/var\s+data\s*=\s*(\{.*?\});/s)?.[1].replaceAll('undefined', 'null') || '{}'); const { AuthorVideoList: { videoList: videoInfos }, diff --git a/lib/routes/jandan/utils.ts b/lib/routes/jandan/utils.ts index 040d4ff00b5d..95af854819fa 100644 --- a/lib/routes/jandan/utils.ts +++ b/lib/routes/jandan/utils.ts @@ -20,7 +20,7 @@ export const extractPageId = async (url: string, referer: string): Promise { const content = $(script).html() || ''; - const match = content.match(/PAGE\s*=\s*{\s*id\s*:\s*(\d+)\s*}/); + const match = content.match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/); if (match) { pageId = match[1]; } diff --git a/lib/routes/javbus/index.tsx b/lib/routes/javbus/index.tsx index f5070c88b952..bef6d7791368 100644 --- a/lib/routes/javbus/index.tsx +++ b/lib/routes/javbus/index.tsx @@ -12,7 +12,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const toSize = (raw) => { - const matches = raw.match(/(\d+(\.\d+)?)(\w+)/); + const matches = raw.match(/(\d+(\.\d+)?)(\D\w*)/); return matches[3] === 'GB' ? matches[1] * 1024 : matches[1]; }; @@ -154,7 +154,7 @@ async function handler(ctx) { // To fetch magnets. try { - const matches = detailResponse.data.match(/var gid = (\d+);[\S\s]*var uc = (\d+);[\S\s]*var img = '(.*)';/); + const matches = detailResponse.data.match(/var gid = (\d+);[\s\S]*var uc = (\d+);[\s\S]*var img = '(.*)';/); const magnetResponse = await got({ method: 'get', diff --git a/lib/routes/jiemian/common.tsx b/lib/routes/jiemian/common.tsx index 1a226134008b..5ced6cb40a4f 100644 --- a/lib/routes/jiemian/common.tsx +++ b/lib/routes/jiemian/common.tsx @@ -34,7 +34,7 @@ export const handler = async (ctx): Promise => { const href = item.prop('href'); const link = href ? (href.startsWith('/') ? new URL(href, rootUrl).href : href) : undefined; - if (link && /\/(article|video)\/\w+\.html/.test(link)) { + if (link && /\/(?:article|video)\/\w+\.html/.test(link)) { items[link] = { title: item.text(), link, diff --git a/lib/routes/jike/user.ts b/lib/routes/jike/user.ts index be9308c43155..dafa0a0b0f1a 100644 --- a/lib/routes/jike/user.ts +++ b/lib/routes/jike/user.ts @@ -139,7 +139,7 @@ async function handler(ctx) { const single = { title: `${typeMap[item.type]}了: ${shortenTitle}`, - description: `${content}${linkTemplate}${imgTemplate}`.replace(/(
|\s)+$/, ''), + description: `${content}${linkTemplate}${imgTemplate}`.replace(/(?:
|\s)+$/, ''), pubDate: parseDate(item.createdAt), link: getLink(item.id, item.type), _extra: repostContent && { diff --git a/lib/routes/jike/utils.ts b/lib/routes/jike/utils.ts index 9fbe7b082de0..9fa9653a9567 100644 --- a/lib/routes/jike/utils.ts +++ b/lib/routes/jike/utils.ts @@ -117,7 +117,7 @@ const topicDataHanding = (data, ctx) => // default: // break; // } - const imgUrl = /\.[\da-z]+?\?imageMogr2/.test(pic.picUrl) ? pic.picUrl.split('?imageMogr2/')[0] : pic.picUrl.replace(/thumbnail\/.+/, ''); + const imgUrl = /\.[\da-z]+\?imageMogr2/.test(pic.picUrl) ? pic.picUrl.split('?imageMogr2/')[0] : pic.picUrl.replace(/thumbnail\/.+/, ''); description += `
`; // description += `
{ + imgTag.replaceAll(/\b(src|data-src)="(?!http|\/\/)([^"]*)"/g, (_, attrName, relativePath) => { const absoluteImageUrl = new URL(relativePath, baseUrl).href; return `${attrName}="${absoluteImageUrl}"`; }) diff --git a/lib/routes/kunchengblog/essay.ts b/lib/routes/kunchengblog/essay.ts index 3bb85f90f8e6..82ef34f8ed33 100644 --- a/lib/routes/kunchengblog/essay.ts +++ b/lib/routes/kunchengblog/essay.ts @@ -53,7 +53,7 @@ async function handler(ctx) { .map((item) => { const source = consumer.sourceContentFor(item).replaceAll(/\s\n/g, ''); - const processedSource = source.replaceAll(/(\w+)={+([^{}]+)}+/g, (match, key, value) => { + const processedSource = source.replaceAll(/(\w+)=\{+([^{}]+)\}+/g, (match, key, value) => { const processedValue = value.slice(1, -1).replaceAll('"', "'").trim(); return `${key}="${processedValue}"`; }); diff --git a/lib/routes/leetcode/dailyquestion-solution-cn.ts b/lib/routes/leetcode/dailyquestion-solution-cn.ts index eb19f5c5a5e9..bcc30f5b0677 100644 --- a/lib/routes/leetcode/dailyquestion-solution-cn.ts +++ b/lib/routes/leetcode/dailyquestion-solution-cn.ts @@ -186,7 +186,7 @@ async function handler() { const handleText = (s) => { // 处理多语言代码展示问题 - s = s.replaceAll(/(```)([\d#+A-Za-z-]+)\s*?(\[.*?])?\n/g, '\r\n###$2\r\n$1$2\r\n'); + s = s.replaceAll(/(```)([\d#+A-Z-]+)\s*?(\[.*?\])?\n/gi, '\r\n###$2\r\n$1$2\r\n'); return s; }; return { diff --git a/lib/routes/lfsyd/utils.tsx b/lib/routes/lfsyd/utils.tsx index 160de968bb64..0a5f05fe437a 100644 --- a/lib/routes/lfsyd/utils.tsx +++ b/lib/routes/lfsyd/utils.tsx @@ -46,7 +46,7 @@ const ProcessForm = (form, type) => { }; const cleanHtml = (htmlString) => { - const regex = /(

|

)(.*?)?(标准|狂野)日报投稿.*?<\/strong>(.*?)?(<\/p>|<\/div>)(.|\n)*$/; + const regex = /(

|

)(.*?)(标准|狂野)日报投稿.*?<\/strong>(.*?)(<\/p>|<\/div>)(.|\n)*$/; const $ = load(htmlString.replace(regex, '')); $('.yingdi-car,.bbspost,.deck-set').each((i, e) => { diff --git a/lib/routes/line/utils.ts b/lib/routes/line/utils.ts index 087ddc3655ab..190ab04615c1 100644 --- a/lib/routes/line/utils.ts +++ b/lib/routes/line/utils.ts @@ -20,7 +20,7 @@ const parseItems = (list) => Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const edition = item.link.match(/today\.line\.me\/(\w+?)\/v[23]\/.*$/)[1]; + const edition = item.link.match(/today\.line\.me\/(\w+)\/v[23]\/.*$/)[1]; let data; try { const response = await got(`${baseUrl}/webapi/portal/page/setting/article`, { diff --git a/lib/routes/lorientlejour/index.tsx b/lib/routes/lorientlejour/index.tsx index 0d54c8adfd16..f996e275d600 100644 --- a/lib/routes/lorientlejour/index.tsx +++ b/lib/routes/lorientlejour/index.tsx @@ -86,7 +86,7 @@ async function viewCategory(category: string) { } async function handler(ctx) { - const categoryId = (ctx.req.param('category') ?? '977-Lebanon').split('|').map((item) => item.match(/^(\d+)/i)[0] ?? item); + const categoryId = (ctx.req.param('category') ?? '977-Lebanon').split('|').map((item) => item.match(/^(\d+)/)[0] ?? item); const limit = ctx.req.query('limit') ?? 25; let token; diff --git a/lib/routes/luolei/index.tsx b/lib/routes/luolei/index.tsx index 49e7a0822c3f..50ce10c8b2fa 100644 --- a/lib/routes/luolei/index.tsx +++ b/lib/routes/luolei/index.tsx @@ -70,7 +70,7 @@ export const handler = async (ctx) => { const { data: themeResponse } = await got(themeUrl); let items = themeResponse - .match(/{"title":".*?"string":".*?"}}/g) + .match(/\{"title":".*?"string":".*?"\}\}/g) .slice(0, limit) .map((item) => { item = JSON.parse( diff --git a/lib/routes/magazinelib/latest-magazine.tsx b/lib/routes/magazinelib/latest-magazine.tsx index 2c101052cb56..ee1f4c4b1f07 100644 --- a/lib/routes/magazinelib/latest-magazine.tsx +++ b/lib/routes/magazinelib/latest-magazine.tsx @@ -46,7 +46,7 @@ async function handler(ctx) { if (subTitle === undefined) { subTitle = ''; } else { - subTitle = subTitle.replaceAll(/[^\dA-Za-z]+/g, ' ').toUpperCase(); + subTitle = subTitle.replaceAll(/[^\dA-Z]+/gi, ' ').toUpperCase(); subTitle = ` - ${subTitle}`; } diff --git a/lib/routes/mastodon/utils.ts b/lib/routes/mastodon/utils.ts index 0bebaab81c85..8b33870b7731 100644 --- a/lib/routes/mastodon/utils.ts +++ b/lib/routes/mastodon/utils.ts @@ -40,8 +40,8 @@ const parseStatuses = (data) => const accountRepostedBy = item.reblog ? item.account : null; item = item.reblog ?? item; - const content = item.content ? item.content.replaceAll(/|<\/span.*?>/gm, '') : ''; - const contentRemovedHtml = content.replaceAll(/<(?:.|\n)*?>/gm, '\n'); + const content = item.content ? item.content.replaceAll(/|<\/span.*?>/g, '') : ''; + const contentRemovedHtml = content.replaceAll(/<(?:.|\n)*?>/g, '\n'); const author = `${item.account.display_name} (@${item.account.acct})`; const link = item.url; diff --git a/lib/routes/maven/central.ts b/lib/routes/maven/central.ts index c6a1a6634824..dee6acf2c8b8 100644 --- a/lib/routes/maven/central.ts +++ b/lib/routes/maven/central.ts @@ -45,7 +45,7 @@ export const route: Route = { * Handles cases without delimiters: 5.0.0beta2, 7.0.0canary * Handles secondary versions: 1.0.0-M6.1 */ -const UNSTABLE_VERSION_REGEX = /[-_.]?(rc|m|snapshot|alpha|beta|preview|canary)[.\d]*$/i; +const UNSTABLE_VERSION_REGEX = /[-_.]?(?:rc|m|snapshot|alpha|beta|preview|canary)[.\d]*$/i; /** * Regex to extract date in the format YYYY-MM-DD HH:mm (e.g., 2024-09-22 04:19) diff --git a/lib/routes/metacritic/index.tsx b/lib/routes/metacritic/index.tsx index 9f402f8ced1a..4c867dab650e 100644 --- a/lib/routes/metacritic/index.tsx +++ b/lib/routes/metacritic/index.tsx @@ -81,7 +81,7 @@ async function handler(ctx) { if (platforms.length || networks.length) { const labels = {}; - const labelPattern = String.raw`{label:"([^"]+)",value:(\d+),href:a,meta:{mcDisplayWeight`; + const labelPattern = String.raw`\{label:"([^"]+)",value:(\d+),href:a,meta:\{mcDisplayWeight`; for (const m of currentResponse.match(new RegExp(labelPattern, 'g'))) { const matches = m.match(new RegExp(labelPattern)); diff --git a/lib/routes/meteor/utils.ts b/lib/routes/meteor/utils.ts index 8b7de2293589..124f57a07b26 100644 --- a/lib/routes/meteor/utils.ts +++ b/lib/routes/meteor/utils.ts @@ -25,7 +25,7 @@ const getBoards = (tryGet) => }); const renderDesc = (desc) => { - const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-]+)&?/g; + const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be.*(?:v=|v\/|\/)([\w-]+)&?/g; const matchYouTube = desc.match(youTube); const matchImgur = desc.match(/https:\/\/i.imgur.com\/\w*.(jpg|png|gif|jpeg)/g); const matchVideo = desc.match(/(https:\/\/storage\.meteor\.today\/video\/[\da-f]{24}\.)(mp4|mov|avi|flv|wmv|mpeg|mkv)/gi); diff --git a/lib/routes/mirror/index.ts b/lib/routes/mirror/index.ts index 658715621d53..4a0b4754af5b 100644 --- a/lib/routes/mirror/index.ts +++ b/lib/routes/mirror/index.ts @@ -39,7 +39,7 @@ async function handler(ctx) { const response = await got(currentUrl); - const data = JSON.parse(response.data.match(/"__NEXT_DATA__" type="application\/json">({"props":.*})<\/script>/)[1]); + const data = JSON.parse(response.data.match(/"__NEXT_DATA__" type="application\/json">(\{"props":.*\})<\/script>/)[1]); const items = Object.keys(data.props.pageProps.__APOLLO_STATE__) .filter((key) => key.startsWith('entry:')) diff --git a/lib/routes/modelscope/community.tsx b/lib/routes/modelscope/community.tsx index 341202c55389..4a7a5314a8ce 100644 --- a/lib/routes/modelscope/community.tsx +++ b/lib/routes/modelscope/community.tsx @@ -72,7 +72,7 @@ async function handler(ctx) { const initialData = JSON.parse( $('script') .text() - .match(/window\.__INITIAL_STATE__\s*=\s*({.*?});/)[1] + .match(/window\.__INITIAL_STATE__\s*=\s*(\{.*?\});/)[1] ); item.description = renderDescription(item.thumb, item.description, initialData.pageData.detail.ext.content); diff --git a/lib/routes/mrinalxdev/blog.ts b/lib/routes/mrinalxdev/blog.ts index 5b2ff9c3d25e..e9485d133041 100644 --- a/lib/routes/mrinalxdev/blog.ts +++ b/lib/routes/mrinalxdev/blog.ts @@ -41,7 +41,7 @@ async function handler() { const text = $el.text().trim(); // Extract date from link text (e.g., "2nd October, 2025 Redis 101 : From a Beginners POV") - const dateMatch = text.match(/^(\d{1,2}(?:st|nd|rd|th)\s+\w+,\s+\d{4})\s+(.+)$/); + const dateMatch = text.match(/^(\d{1,2}(?:st|nd|rd|th)\s+\w+,\s+\d{4})\s+(\S.*)$/); let date: string | undefined; let title: string; diff --git a/lib/routes/mydrivers/index.tsx b/lib/routes/mydrivers/index.tsx index 9cabfc0395a3..b9754e5e1853 100644 --- a/lib/routes/mydrivers/index.tsx +++ b/lib/routes/mydrivers/index.tsx @@ -53,7 +53,7 @@ async function handler(ctx) { let newTitle = ''; - if (!/^(\w+\/\w+)$/.test(category)) { + if (!/^\w+\/\w+$/.test(category)) { newTitle = `${title} - ${Object.hasOwn(categories, category) ? categories[category] : categories[Object.keys(categories)[0]]}`; category = `ac/${category}`; } diff --git a/lib/routes/mydrivers/rank.ts b/lib/routes/mydrivers/rank.ts index 7f5f115ccedd..88da71f1fe2f 100644 --- a/lib/routes/mydrivers/rank.ts +++ b/lib/routes/mydrivers/rank.ts @@ -47,7 +47,7 @@ async function handler(ctx) { let items = $('a') .toArray() - .filter((item) => /\/(\d+)\.html?/.test($(item).prop('href'))) + .filter((item) => /\/\d+\.html?/.test($(item).prop('href'))) .slice(0, limit) .map((item) => { item = $(item); diff --git a/lib/routes/natgeo/dailyphoto.tsx b/lib/routes/natgeo/dailyphoto.tsx index 347b66384799..9357400224a1 100644 --- a/lib/routes/natgeo/dailyphoto.tsx +++ b/lib/routes/natgeo/dailyphoto.tsx @@ -51,7 +51,7 @@ async function handler() { const response = await cache.tryGet(apiUrl, async () => (await got(apiUrl)).data, config.cache.contentExpire, false); const $ = load(response); - const natgeo = JSON.parse($.html().match(/window\['__natgeo__']=(.*);/)[1]); + const natgeo = JSON.parse($.html().match(/window\['__natgeo__'\]=(.*);/)[1]); const media = natgeo.page.content.mediaspotlight.frms[0].mods[0].edgs[1].media; const items = media.map((item) => ({ diff --git a/lib/routes/nationalgeographic/latest-stories.tsx b/lib/routes/nationalgeographic/latest-stories.tsx index afda5cbc60cb..c3ba067ce433 100644 --- a/lib/routes/nationalgeographic/latest-stories.tsx +++ b/lib/routes/nationalgeographic/latest-stories.tsx @@ -11,7 +11,7 @@ const findNatgeo = ($) => JSON.parse( $('script') .text() - .match(/\['__natgeo__']=({.*?});/)[1] + .match(/\['__natgeo__'\]=(\{.*?\});/)[1] ); type StoryMedia = { diff --git a/lib/routes/nature/utils.ts b/lib/routes/nature/utils.ts index bebefabc07c2..f30ada7aab34 100644 --- a/lib/routes/nature/utils.ts +++ b/lib/routes/nature/utils.ts @@ -105,7 +105,7 @@ const getDataLayer = (html) => JSON.parse( html('script[data-test=dataLayer]') .text() - .match(/window\.dataLayer = \[(.*)];/s)[1] + .match(/window\.dataLayer = \[(.*)\];/s)[1] ); const cookieJar = new CookieJar(); diff --git a/lib/routes/ncpssd/newlist.ts b/lib/routes/ncpssd/newlist.ts index 8201212a2b5a..61421915ce69 100644 --- a/lib/routes/ncpssd/newlist.ts +++ b/lib/routes/ncpssd/newlist.ts @@ -37,7 +37,7 @@ async function handler() { const title = $(p) .find('a') .text() - .replaceAll(/(\r\n|\n|\r)/gm, '') + .replaceAll(/(\r\n|\n|\r)/g, '') .trim(); const articleUrl = baseUrl + diff --git a/lib/routes/neu/yz.ts b/lib/routes/neu/yz.ts index 1b8ae5844b38..8e195936112a 100644 --- a/lib/routes/neu/yz.ts +++ b/lib/routes/neu/yz.ts @@ -40,7 +40,7 @@ const parsePage = async (items, type) => { })(), author: type === DOWNLOAD_ID ? DOWNLOAD_AUTHOR : '', }; - if (type === DOWNLOAD_ID && /\.(pdf|docx?|xlsx?|zip|rar|7z)$/i.test(url)) { + if (type === DOWNLOAD_ID && /\.(?:pdf|docx?|xlsx?|zip|rar|7z)$/i.test(url)) { resultItem.description = `

${title}


点击进入下载地址传送门~ diff --git a/lib/routes/nga/forum.ts b/lib/routes/nga/forum.ts index 920454dc74a5..b7768df13848 100644 --- a/lib/routes/nga/forum.ts +++ b/lib/routes/nga/forum.ts @@ -35,11 +35,11 @@ async function handler(ctx) { } const formatContent = (content) => content - .replaceAll(/\[img](.+?)\[\/img]/g, (match, p1) => { + .replaceAll(/\[img\](.+?)\[\/img\]/g, (match, p1) => { const src = p1.replaceAll(/\?.*/g, ''); return ``; }) - .replaceAll(/\[url](.+?)\[\/url]/g, '$1'); + .replaceAll(/\[url\](.+?)\[\/url\]/g, '$1'); const homePage = await got.post('https://ngabbs.com/app_api.php?__lib=subject&__act=list', { headers: { 'X-User-Agent': X_UA, diff --git a/lib/routes/nga/post.ts b/lib/routes/nga/post.ts index 6f0a7782f287..a13284a4e482 100644 --- a/lib/routes/nga/post.ts +++ b/lib/routes/nga/post.ts @@ -48,7 +48,7 @@ async function handler(ctx) { const getLastPageId = async (tid, authorId) => { const $ = await getPage(tid, authorId); const nav = $('#pagebtop'); - const match = nav.html().match(/{0:'\/read\.php\?tid=(\d+).*?',1:(\d+),.*?}/); + const match = nav.html().match(/\{0:'\/read\.php\?tid=(\d)[^']*',1:(\d+),[^}]*\}/); return match ? match[2] : 1; }; @@ -62,33 +62,33 @@ async function handler(ctx) { const formatContent = (str) => { // 简单样式 - str = deepReplace(str, /\[(b|u|i|del|code|sub|sup)](.+?)\[\/\1]/g, '<$1>$2'); + str = deepReplace(str, /\[([bui]|del|code|sub|sup)\](.+?)\[\/\1\]/g, '<$1>$2'); str = str - .replaceAll(/\[dice](.+?)\[\/dice]/g, 'ROLL : $1') - .replaceAll(/\[color=(.+?)](.+?)\[\/color]/g, '$2') - .replaceAll(/\[font=(.+?)](.+?)\[\/font]/g, '$2') - .replaceAll(/\[size=(.+?)](.+?)\[\/size]/g, '$2') - .replaceAll(/\[align=(.+?)](.+?)\[\/align]/g, '$2'); + .replaceAll(/\[dice\](.+?)\[\/dice\]/g, 'ROLL : $1') + .replaceAll(/\[color=([^\]]+)\](.+?)\[\/color\]/g, '$2') + .replaceAll(/\[font=([^\]]+)\](.+?)\[\/font\]/g, '$2') + .replaceAll(/\[size=([^\]]+)\](.+?)\[\/size\]/g, '$2') + .replaceAll(/\[align=([^\]]+)\](.+?)\[\/align\]/g, '$2'); // 列表 - str = deepReplace(str, /\[\*](.+?)(?=\[\*]|\[\/list])/g, '
  • $1
  • '); - str = deepReplace(str, /\[list](.+?)\[\/list]/g, '
      $1
    '); + str = deepReplace(str, /\[\*\](.+?)(?=\[\*\]|\[\/list\])/g, '
  • $1
  • '); + str = deepReplace(str, /\[list\](.+?)\[\/list\]/g, '
      $1
    '); // 图片 - str = str.replaceAll(/\[img](.+?)\[\/img]/g, (m, src) => ``); + str = str.replaceAll(/\[img\](.+?)\[\/img\]/g, (m, src) => ``); // 折叠 - str = deepReplace(str, /\[collapse(?:=(.+?))?](.+?)\[\/collapse]/g, '
    $1$2
    '); + str = deepReplace(str, /\[collapse(?:=([^\]]+))?\](.+?)\[\/collapse\]/g, '
    $1$2
    '); // 引用 - str = deepReplace(str, /\[quote](.+?)\[\/quote]/g, '
    $1
    ') - .replaceAll(/\[@(.+?)]/g, '@$1') - .replaceAll(/\[uid=(\d+)](.+?)\[\/uid]/g, '@$2') - .replaceAll(/\[tid=(\d+)](.+?)\[\/tid]/g, '$2') - .replaceAll(/\[pid=(\d+),(\d+),(\d+)](.+?)\[\/pid]/g, (m, pid, tid, page, str) => { + str = deepReplace(str, /\[quote\](.+?)\[\/quote\]/g, '
    $1
    ') + .replaceAll(/\[@(.+?)\]/g, '@$1') + .replaceAll(/\[uid=(\d+)\](.+?)\[\/uid\]/g, '@$2') + .replaceAll(/\[tid=(\d+)\](.+?)\[\/tid\]/g, '$2') + .replaceAll(/\[pid=(\d+),(\d+),(\d+)\](.+?)\[\/pid\]/g, (m, pid, tid, page, str) => { const url = `https://nga.178.com/read.php?tid=${tid}&page=${page}#pid${pid}Anchor`; return `${str}`; }); // 链接 - str = str.replaceAll(/\[url=(.+?)](.+?)\[\/url]/g, '$2'); + str = str.replaceAll(/\[url=([^\]]+)\](.+?)\[\/url\]/g, '$2'); // 分割线 - str = str.replaceAll(/\[h](.+?)\[\/h]/g, '

    $1

    '); + str = str.replaceAll(/\[h\](.+?)\[\/h\]/g, '

    $1

    '); return str; }; diff --git a/lib/routes/nhentai/util.tsx b/lib/routes/nhentai/util.tsx index aa953f25bea3..416e9b6a0eb5 100644 --- a/lib/routes/nhentai/util.tsx +++ b/lib/routes/nhentai/util.tsx @@ -148,7 +148,7 @@ const getDetail = async (simple) => { const galleryImgs = $('.gallerythumb img') .toArray() .map((ele) => new URL($(ele).attr('data-src'), baseUrl).href) - .map((src) => src.replace(/(.+)(\d+)t\.(.+)/, (_, p1, p2, p3) => `${p1}${p2}.${p3}`)) // thumb to high-quality + .map((src) => src.replace(/(.+)(\d)t\.(.+)/, (_, p1, p2, p3) => `${p1}${p2}.${p3}`)) // thumb to high-quality .map((src) => src.replace(/t(\d+)\.nhentai\.net/, 'i$1.nhentai.net')) .map((src) => src.replace(/\.(jpg|png|gif)\.webp$/, '.$1')) // 移除重複的.webp後綴 .map((src) => src.replace(/\.webp\.webp$/, '.webp')); // 處理.webp.webp的情況 diff --git a/lib/routes/nikkei/cn/index.ts b/lib/routes/nikkei/cn/index.ts index 191a4e5105f9..4441eaea30d5 100644 --- a/lib/routes/nikkei/cn/index.ts +++ b/lib/routes/nikkei/cn/index.ts @@ -66,7 +66,7 @@ async function handler(ctx) { let language: string; let path = getSubPath(ctx); - if (/^\/cn\/(cn|zh)/.test(path)) { + if (/^\/cn\/(?:cn|zh)/.test(path)) { language = path.match(/^\/cn\/(cn|zh)/)[1]; path = path.match(new RegExp(String.raw`\/cn\/` + language + '(.*)'))[1]; } else { diff --git a/lib/routes/nintendo/eshop-hk.ts b/lib/routes/nintendo/eshop-hk.ts index 4b8607752c8c..4b4fd80f9e91 100644 --- a/lib/routes/nintendo/eshop-hk.ts +++ b/lib/routes/nintendo/eshop-hk.ts @@ -50,7 +50,7 @@ async function handler(ctx) { const gallery = JSON.parse( $('[type=text/x-magento-init]') .text() - .match(/{\n\s+"\[data-gal{2}ery-role=gal{2}ery-placeholder]": {\n\s+"mage(?:\/gal{2}ery){2}".*?}{4}(?:\s+}\n){3}/s) + .match(/\{\n\s+"\[data-gal{2}ery-role=gal{2}ery-placeholder\]": \{\n\s+"mage(?:\/gal{2}ery){2}".*?\}{4}(?:\s+\}\n){3}/s) ); description = renderEshopHkDescription({ @@ -60,7 +60,7 @@ async function handler(ctx) { host: 'store.nintendo.com.hk', }); } else if (item.link.startsWith('https://ec.nintendo.com/')) { - const jsonData = JSON.parse(response.match(/NXSTORE\.titleDetail\.jsonData = ({.*?});/)[1]); + const jsonData = JSON.parse(response.match(/NXSTORE\.titleDetail\.jsonData = (\{.*?\});/)[1]); const { data: priceData } = await got('https://ec.nintendo.com/api/HK/zh/guest_prices', { searchParams: { ns_uids: jsonData.id, diff --git a/lib/routes/nintendo/system-update.ts b/lib/routes/nintendo/system-update.ts index c4fdb27667ea..149b470b7e27 100644 --- a/lib/routes/nintendo/system-update.ts +++ b/lib/routes/nintendo/system-update.ts @@ -47,7 +47,7 @@ async function handler() { .toArray() .map((element) => $(element).html()) .join('\n'); - const matched_version = /(\d\.)+\d/.exec(heading); + const matched_version = /(?:\d\.)+\d/.exec(heading); return { title: heading, diff --git a/lib/routes/nowcoder/discuss.ts b/lib/routes/nowcoder/discuss.ts index 458af092671f..2044b149a167 100644 --- a/lib/routes/nowcoder/discuss.ts +++ b/lib/routes/nowcoder/discuss.ts @@ -53,7 +53,7 @@ async function handler(ctx) { const out = await Promise.all( list.map((info) => { const title = info.title || 'tzgg'; - const itemUrl = new URL(info.link, host).href.replace(/^(.*)\?(.*)$/, '$1'); + const itemUrl = new URL(info.link, host).href.replace(/^([^\n\r\u2028\u2029]*)\?[^\n\r?\u2028\u2029]*$/, '$1'); return cache.tryGet(itemUrl, async () => { const response = await got.get(itemUrl); diff --git a/lib/routes/odaily/activity.ts b/lib/routes/odaily/activity.ts index 554b90bfa24f..f401d4f60cab 100644 --- a/lib/routes/odaily/activity.ts +++ b/lib/routes/odaily/activity.ts @@ -54,7 +54,7 @@ async function handler(ctx) { url: item.link, }); - const content = load(detailResponse.data.match(/"content":"(.*)"}},"secondaryList":/)[1]); + const content = load(detailResponse.data.match(/"content":"(.*)"\}\},"secondaryList":/)[1]); content('img').each((_, el) => { content(el).attr( diff --git a/lib/routes/odaily/post.ts b/lib/routes/odaily/post.ts index c2cef7b46ce3..2de881e3c61c 100644 --- a/lib/routes/odaily/post.ts +++ b/lib/routes/odaily/post.ts @@ -68,7 +68,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { const detailResponse = await got(item.link); - const ssr = JSON.parse(`{${detailResponse.data.match(/window\.__INITIAL_STATE__ = {(.*)}/)[1]}}`); + const ssr = JSON.parse(`{${detailResponse.data.match(/window\.__INITIAL_STATE__ = \{(.*)\}/)[1]}}`); const content = load(ssr.post.detail.content, null, false); content('img').each((_, img) => { diff --git a/lib/routes/oeeee/utils.ts b/lib/routes/oeeee/utils.ts index 706dc335f70b..9f8217b060eb 100644 --- a/lib/routes/oeeee/utils.ts +++ b/lib/routes/oeeee/utils.ts @@ -20,7 +20,7 @@ const parseArticle = (item, tryGet) => item.description += content('.post-cont') .html() - .replaceAll(/data:image\S*=="\s*\n*\s*original="/g, '') ?? ''; + .replaceAll(/data:image\S*=="\s*original="/g, '') ?? ''; if (!item.pubDate) { item.pubDate = timezone(parseDate(content('.introduce').text().split()), +8); } diff --git a/lib/routes/outagereport/index.ts b/lib/routes/outagereport/index.ts index d155a1876523..59a2ee124dc0 100644 --- a/lib/routes/outagereport/index.ts +++ b/lib/routes/outagereport/index.ts @@ -35,8 +35,8 @@ async function handler(ctx) { // use RegExp because of irregular class name const gaugeRegexp = /class="Gauge__Count.*?>(\d+)<\/text>/; // Core Pattern - const gaugeTextRegexp = /class="Gauge__MessageWrapper.*?class="Gauge__Message.*?>(.*?)<\/span>/; // Core Pattern - const rssDescribeRegexp = /

    ]*>(.*?)<\/p>/; // data to be shown on RSS feed and RSS items const gaugeCount = Number(html.match(gaugeRegexp)[1]); diff --git a/lib/routes/papers/category.ts b/lib/routes/papers/category.ts index 60aa2f2450aa..7249f3fab2d9 100644 --- a/lib/routes/papers/category.ts +++ b/lib/routes/papers/category.ts @@ -81,7 +81,7 @@ export const handler = async (ctx: Context): Promise => { const description: string = renderDescription({ pdfUrl: enclosureUrl, - kimiUrl: `${targetUrl.replace(/[a-zA-Z0-9.]+$/, 'kimi')}?paper=${doi}`, + kimiUrl: `${targetUrl.replace(/[a-z0-9.]+$/i, 'kimi')}?paper=${doi}`, authors, summary: $el.find('p.summary').text(), }); diff --git a/lib/routes/parliament/section77.ts b/lib/routes/parliament/section77.ts index ff84df3a1006..1eaa8a6b3a6d 100644 --- a/lib/routes/parliament/section77.ts +++ b/lib/routes/parliament/section77.ts @@ -136,7 +136,7 @@ async function handler(ctx) { ]; const voteText = $('.row.bg-status .col-md-4.text-right').text().trim(); - const voteRegex = /^ผู้แสดงความคิดเห็น\s*(\d+)\s*คน\s*(\d+(?:\.\d+)?)%\s*(\d+(?:\.\d+)?)%/g.exec(voteText); + const voteRegex = /^ผู้แสดงความคิดเห็น\s*(\d+)\s*คน\s*(\d+(?:\.\d+)?)%\s*\d+(?:\.\d+)?%/.exec(voteText); if (voteRegex) { const voteTotal = Number.parseInt(voteRegex[0]); @@ -148,7 +148,7 @@ async function handler(ctx) { } const dateText = $('.banner-detail .banner-detail-caption .blockquote p:last-child').text(); - const dateRegex = /^รับฟังตั้งแต่วันที่\s(\d{1,2})\s*([\u0E00-\u0E7F]+)\s*(\d{4})/g.exec(dateText); + const dateRegex = /^รับฟังตั้งแต่วันที่\s(\d{1,2})\s*([\u0E00-\u0E7F]+)\s*(\d{4})/.exec(dateText); if (dateRegex) { item.pubDate = timezone( diff --git a/lib/routes/patreon/feed.tsx b/lib/routes/patreon/feed.tsx index 388c8043aa6a..3e09492ce611 100644 --- a/lib/routes/patreon/feed.tsx +++ b/lib/routes/patreon/feed.tsx @@ -121,7 +121,7 @@ async function handler(ctx) { const ogUrl = $('meta[property="og:url"]').attr('content'); if (ogUrl?.startsWith(`${baseUrl}/cw/`)) { const ogImage = $('meta[property="og:image"]').attr('content'); - const creatorId = decodeURIComponent(ogImage || '').match(/card-teaser-image\/creator\/(\d+?)\?/)?.[1]; + const creatorId = decodeURIComponent(ogImage || '').match(/card-teaser-image\/creator\/(\d+)\?/)?.[1]; if (creatorId) { const creator = await ofetch(`${baseUrl}/api/campaigns/${creatorId}`); return { diff --git a/lib/routes/pixiv/novel-api/content/utils.ts b/lib/routes/pixiv/novel-api/content/utils.ts index ef5ec5629f1f..9e1a68cd8676 100644 --- a/lib/routes/pixiv/novel-api/content/utils.ts +++ b/lib/routes/pixiv/novel-api/content/utils.ts @@ -93,10 +93,10 @@ export async function parseNovelContent(content: string, images: Record){2,}/g, '

    ') // ruby 標籤(為日文漢字標註讀音) // ruby tags (for Japanese kanji readings) - .replaceAll(/\[\[rb:(.*?)>(.*?)\]\]/g, '$1$2') + .replaceAll(/\[\[rb:([^>\n\r\u2028\u2029]*)>(.*?)\]\]/g, '$1$2') // 外部連結 // external links - .replaceAll(/\[\[jumpuri:(.*?)>(.*?)\]\]/g, '$1') + .replaceAll(/\[\[jumpuri:([^>\n\r\u2028\u2029]*)>(.*?)\]\]/g, '$1') // 頁面跳轉,但由於 [newpage] 使用 hr 分隔,沒有頁數,沒必要跳轉,所以只顯示文字 // Page jumps, but since [newpage] uses hr separators, without the page numbers, jumping isn't needed, so just display text .replaceAll(/\[jump:(\d+)\]/g, 'Jump to page $1') diff --git a/lib/routes/playno1/av.ts b/lib/routes/playno1/av.ts index 04956e769cc6..e0de6cf58cd1 100644 --- a/lib/routes/playno1/av.ts +++ b/lib/routes/playno1/av.ts @@ -56,7 +56,7 @@ async function handler(ctx) { author: item .find('.fire_right') .text() - .match(/作者:(.*)\s*\|/)[1] + .match(/作者:([^|]*)\|/)[1] .trim(), }; }); diff --git a/lib/routes/qingting/channel.ts b/lib/routes/qingting/channel.ts index 6a9e47dfda9d..c1afb06fb42a 100644 --- a/lib/routes/qingting/channel.ts +++ b/lib/routes/qingting/channel.ts @@ -42,7 +42,7 @@ async function handler(ctx) { items.map((item) => cache.tryGet(item.link, async () => { response = await ofetch(item.link); - const data = JSON.parse(response.match(/},"program":(.*?),"plist":/)[1]); + const data = JSON.parse(response.match(/\},"program":(.*?),"plist":/)[1]); item.description = data.richtext; return item; }) diff --git a/lib/routes/qingting/podcast.ts b/lib/routes/qingting/podcast.ts index b99513f3497b..bbde582e2d3e 100644 --- a/lib/routes/qingting/podcast.ts +++ b/lib/routes/qingting/podcast.ts @@ -83,7 +83,7 @@ async function handler(ctx) { }, }); - const detail = JSON.parse(detailRes.match(/},"program":(.*?),"plist":/)[1]); + const detail = JSON.parse(detailRes.match(/\},"program":(.*?),"plist":/)[1]); const rssItem = { title: item.title, diff --git a/lib/routes/quantamagazine/archive.ts b/lib/routes/quantamagazine/archive.ts index c9bf893db74f..a383a60d6108 100644 --- a/lib/routes/quantamagazine/archive.ts +++ b/lib/routes/quantamagazine/archive.ts @@ -13,16 +13,16 @@ const processArticleContent = (html: string | null, articleLink?: string): strin } // Handle LaTeX formulas - let processed = html.replaceAll(/\$latex([\S\s]+?)\$/g, ''); + let processed = html.replaceAll(/\$latex([\s\S]+?)\$/g, ''); // Handle embedded images with captions - processed = processed.replaceAll(/

    ?/g, (_match, src, cap) => { + processed = processed.replaceAll(/
    ?/g, (_match, src, cap) => { const imgUrl = src.replaceAll(/\\([^nu])/g, '$1'); const img = ``; const noBS = cap.replaceAll(/\\([^nu])/g, '$1'); const removeNL = noBS.replaceAll(String.raw`\n`, ''); - const caption = removeNL.replaceAll(/\\u(\d{1,3}[a-z]\d?|\d{4}?)/g, (_omit, s) => String.fromCodePoint(Number.parseInt(s, 16))); + const caption = removeNL.replaceAll(/\\u(\d{1,3}[a-z]\d?|\d{4})/g, (_omit, s) => String.fromCodePoint(Number.parseInt(s, 16))); return `
    ${img}
    ${caption}
    `; }); diff --git a/lib/routes/rawkuma/manga.tsx b/lib/routes/rawkuma/manga.tsx index cbf47ff3bb8c..4c3783909664 100644 --- a/lib/routes/rawkuma/manga.tsx +++ b/lib/routes/rawkuma/manga.tsx @@ -70,7 +70,7 @@ async function handler(ctx) { const content = load(detailResponse); - const imageMatches = detailResponse.match(/"images":(\[.*?])}],"lazyload"/); + const imageMatches = detailResponse.match(/"images":(\[.*?\])\}\],"lazyload"/); const images = imageMatches ? JSON.parse(imageMatches[1]) : []; diff --git a/lib/routes/readhub/index.ts b/lib/routes/readhub/index.ts index c5138ee4f608..689d162e27c4 100644 --- a/lib/routes/readhub/index.ts +++ b/lib/routes/readhub/index.ts @@ -37,7 +37,7 @@ async function handler(ctx) { const { data: currentResponse } = await got(currentUrl); - const type = currentResponse.match(/\[\\"type\\",\\"(\d+)\\",\\"d\\"]/)?.[1] ?? '1'; + const type = currentResponse.match(/\[\\"type\\",\\"(\d+)\\",\\"d\\"\]/)?.[1] ?? '1'; const { data: response } = await got(apiTopicUrl, { searchParams: { diff --git a/lib/routes/readhub/util.ts b/lib/routes/readhub/util.ts index 2bb28b445f60..825214c66fc5 100644 --- a/lib/routes/readhub/util.ts +++ b/lib/routes/readhub/util.ts @@ -26,7 +26,7 @@ const processItems = async (items, tryGet) => const { data: detailResponse } = await got(item.link); - const data = JSON.parse(detailResponse.match(/{\\"topic\\":(.*?)}]\\n"]\)<\/script>/)[1].replaceAll(String.raw`\"`, '"')); + const data = JSON.parse(detailResponse.match(/\{\\"topic\\":(.*?)\}\]\\n"\]\)<\/script>/)[1].replaceAll(String.raw`\"`, '"')); item.title = data.title; item.link = data.url ?? new URL(`topic/${data.uid}`, rootUrl).href; diff --git a/lib/routes/reuters/common.tsx b/lib/routes/reuters/common.tsx index f440cf2a011c..1c8decff6586 100644 --- a/lib/routes/reuters/common.tsx +++ b/lib/routes/reuters/common.tsx @@ -288,7 +288,7 @@ async function handler(ctx) { const matches = content('script#fusion-metadata') .text() - .match(/Fusion.globalContent=({[\S\s]*?});/); + .match(/Fusion.globalContent=(\{[\s\S]*?\});/); if (matches) { const data = JSON.parse(matches[1]); @@ -310,7 +310,7 @@ async function handler(ctx) { item.title = content('meta[property="og:title"]').attr('content'); item.pubDate = parseDate(detailResponse.data.match(/"datePublished":"(.*?)","dateModified/)[1]); item.author = detailResponse.data - .match(/{"@type":"Person","name":"(.*?)"}/g) + .match(/\{"@type":"Person","name":"(.*?)"\}/g) .map((p) => p.match(/"name":"(.*?)"/)[1]) .join(', '); item.description = content('article').html(); diff --git a/lib/routes/runyeah/posts.ts b/lib/routes/runyeah/posts.ts index 5e241ff79f27..a21a477cdc83 100644 --- a/lib/routes/runyeah/posts.ts +++ b/lib/routes/runyeah/posts.ts @@ -29,7 +29,7 @@ async function handler(ctx) { let data = response; if (typeof response !== 'object') { // remove php warnings before JSON - data = JSON.parse(response.match(/\[(.*)]/)[0]); + data = JSON.parse(response.match(/\[(.*)\]/)[0]); } const items = data.map((item) => ({ diff --git a/lib/routes/shisu/en.ts b/lib/routes/shisu/en.ts index 62438ea48cb4..5845a98a1fa3 100644 --- a/lib/routes/shisu/en.ts +++ b/lib/routes/shisu/en.ts @@ -49,8 +49,8 @@ async function process(baseUrl: string, section: any) { const $ = load(r); j.description = $('.details-con') .html()! - .replaceAll(/[\S\s]*?<\/o:p>/g, '') - .replaceAll(/(]*> <\/p>\s*)+/gm, '

     

    '); + .replaceAll(/[\s\S]*?<\/o:p>/g, '') + .replaceAll(/(]*> <\/p>\s*)+/g, '

     

    '); return j; }) ) diff --git a/lib/routes/sina/utils.tsx b/lib/routes/sina/utils.tsx index e1a7c302596a..68c44e813e93 100644 --- a/lib/routes/sina/utils.tsx +++ b/lib/routes/sina/utils.tsx @@ -48,7 +48,7 @@ const parseArticle = (item, tryGet) => const slideData = JSON.parse( $('script') .text() - .match(/var slide_data = ({.*?})\s/)[1] + .match(/var slide_data = (\{.*?\})\s/)[1] ); item.description = renderToString( <> diff --git a/lib/routes/sis001/common.ts b/lib/routes/sis001/common.ts index 1dbcb72911e3..89b8a12d7b32 100644 --- a/lib/routes/sis001/common.ts +++ b/lib/routes/sis001/common.ts @@ -53,7 +53,7 @@ async function getThread(cookie: string, item: DataItem) { $('.postinfo') .eq(0) .text() - .match(/发表于 (.*)\s*只看该作者/)[1], + .match(/发表于 (.*)(?:[\n\r\u2028\u2029]\s*)?只看该作者/)[1], 'YYYY-M-D HH:mm' ), 8 @@ -65,7 +65,7 @@ async function getThread(cookie: string, item: DataItem) { .html() ?.replaceAll('\n', '') .replaceAll(/\u3000{2}.+?(((?:
    ){2})|( ))/g, (str) => `

    ${str.replaceAll('
    ', '')}

    `) - .replaceAll(/

    \u3000{6,}(.+?)<\/p>/g, '

    $1

    ') + .replaceAll(/

    \u3000{6,}([^\u3000\n\r\u2028\u2029].*?|\u3000)<\/p>/g, '

    $1

    ') .replaceAll(' ', '') .replace(/

    +

    /, '') + ($('.defaultpost .postattachlist').html() ?? ''); return item; diff --git a/lib/routes/smartlink/index.ts b/lib/routes/smartlink/index.ts index e0d04bebbb23..4fd221133adf 100644 --- a/lib/routes/smartlink/index.ts +++ b/lib/routes/smartlink/index.ts @@ -15,7 +15,7 @@ function parseTitle(smartlinkUrl: string): string { let titleSlug = dateIndex !== -1 && dateIndex < pathSegments.length - 1 ? pathSegments[dateIndex + 1] : pathSegments.at(-1) || ''; // Remove .html/.htm extension if present - titleSlug = titleSlug.replace(/\.(html?|htm)$/i, ''); + titleSlug = titleSlug.replace(/\.(html?)$/i, ''); // Convert hyphens to spaces and capitalize each word return toTitleCase(titleSlug.replaceAll('-', ' ')); diff --git a/lib/routes/sohu/mobile.ts b/lib/routes/sohu/mobile.ts index 5fc65c0322d7..2d40aab9d6fc 100644 --- a/lib/routes/sohu/mobile.ts +++ b/lib/routes/sohu/mobile.ts @@ -36,7 +36,7 @@ async function handler() { // 从HTML中提取JSON数据 const $ = cheerio.load(response); const jsonScript = $('script:contains("WapHomeRenderData")').text(); - const jsonMatch = jsonScript?.match(/window\.WapHomeRenderData\s*=\s*({.*})/s); + const jsonMatch = jsonScript?.match(/window\.WapHomeRenderData\s*=\s*(\{.*\})/s); if (!jsonMatch?.[1]) { throw new Error('WapHomeRenderData 数据未找到'); } diff --git a/lib/routes/sohu/mp.tsx b/lib/routes/sohu/mp.tsx index 1b9a3a07eb4f..8b1cfdf4353a 100644 --- a/lib/routes/sohu/mp.tsx +++ b/lib/routes/sohu/mp.tsx @@ -140,7 +140,7 @@ async function handler(ctx) { const blockRenderData = JSON.parse( $('script:contains("column_2_text")') .text() - .match(/({.*})/)?.[1] + .match(/(\{.*\})/)?.[1] ); const renderData = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('FeedSlideloadAuthor'))]; const briefIntroductionCard = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('BriefIntroductionCard'))].param.data.list[0]; diff --git a/lib/routes/solidot/_article.ts b/lib/routes/solidot/_article.ts index 827570f18240..1dd70b9de099 100644 --- a/lib/routes/solidot/_article.ts +++ b/lib/routes/solidot/_article.ts @@ -18,7 +18,7 @@ export default async function get_article(url) { const $ = load(data); const date_raw = $('div.talk_time').clone().children().remove().end().text(); - const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(.*分)[^`]*$/g, '$1'); // use [^`] to match \n + const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(?=(.*分))\1[^`]*$/g, '$1'); // use [^`] to match \n const date_str = date_str_zh .replaceAll(/[年月]/g, '-') .replaceAll('时', ':') diff --git a/lib/routes/sony/downloads.ts b/lib/routes/sony/downloads.ts index 0e6f89f42a14..f8cd5f114652 100644 --- a/lib/routes/sony/downloads.ts +++ b/lib/routes/sony/downloads.ts @@ -42,7 +42,7 @@ async function handler(ctx) { const $ = load(data); const contents = $('script:contains("window.__PRELOADED_STATE__.downloads")').text(); - const regex = /window\.__PRELOADED_STATE__\.downloads\s*=\s*({.*?});\s*window\.__PRELOADED_STATE__/s; + const regex = /window\.__PRELOADED_STATE__\.downloads\s*=\s*(\{.*?\});\s*window\.__PRELOADED_STATE__/s; const match = contents.match(regex); let results = {}; diff --git a/lib/routes/steam/curator.tsx b/lib/routes/steam/curator.tsx index 7dd9d2e4ece7..03c969dd9834 100644 --- a/lib/routes/steam/curator.tsx +++ b/lib/routes/steam/curator.tsx @@ -62,7 +62,7 @@ Examples: const reviewContent = el.find('.recommendation_desc').text().trim(); const reviewDateText = el.find('.curator_review_date').text().trim(); - const notCurrentYearPattern = /,\s\b\d{4}\b$/; + const notCurrentYearPattern = /,\s\d{4}$/; const reviewPubDate = notCurrentYearPattern.test(reviewDateText) ? parseDate(reviewDateText) : parseDate(`${reviewDateText}, ${new Date().getFullYear()}`); const description = renderToString(); diff --git a/lib/routes/steam/news.ts b/lib/routes/steam/news.ts index ccc7c3970a9c..ed6894e806bb 100644 --- a/lib/routes/steam/news.ts +++ b/lib/routes/steam/news.ts @@ -179,12 +179,12 @@ const linebreakRenderer = (tree: BBobCoreTagNodeTree) => const plainUrlRenderer = (tree: BBobCoreTagNodeTree) => tree.walk((node) => { - if (typeof node === 'string' && /https?:\/\/[^\s]+/.test(node)) { + if (typeof node === 'string' && /https?:\/\/\S+/.test(node)) { let lastIndex = 0; let match: RegExpExecArray | null; const content: NodeContent[] = []; - const urlRe = /https?:\/\/[^\s]+/g; + const urlRe = /https?:\/\/\S+/g; while ((match = urlRe.exec(node)) !== null) { if (match.index > lastIndex) { content.push(node.slice(lastIndex, match.index)); @@ -250,7 +250,7 @@ const customPreset: PresetFactory = presetHTML5.extend((tags) => ({ previewyoutube: (node) => ({ tag: 'iframe', attrs: { - src: `https://www.youtube-nocookie.com/embed/${(getUniqAttr(node.attrs) as string).match(/[A-Za-z0-9_-]+/)?.[0]}`, + src: `https://www.youtube-nocookie.com/embed/${(getUniqAttr(node.attrs) as string).match(/[\w-]+/)?.[0]}`, title: 'YouTube video player', frameborder: '0', allowFullScreen: '1', diff --git a/lib/routes/steam/workshop-search.tsx b/lib/routes/steam/workshop-search.tsx index ef49063c4722..cadf710862b8 100644 --- a/lib/routes/steam/workshop-search.tsx +++ b/lib/routes/steam/workshop-search.tsx @@ -66,7 +66,7 @@ Language Parameter: // const script_tag = item.next('script'); // console.log(`script_tag:${script_tag.text()}`); const hoverContent = item.next('script').text(); - const regex = /SharedFileBindMouseHover\(\s*"sharedfile_\d+",\s*(?:true|false),\s*({.*?})\s*\);/; + const regex = /SharedFileBindMouseHover\(\s*"sharedfile_\d+",\s*(?:true|false),\s*(\{.*?\})\s*\);/; const match = hoverContent.match(regex); let entryDescription = ''; diff --git a/lib/routes/supchina/index.ts b/lib/routes/supchina/index.ts index a2965c0975cf..984ac98d5e04 100644 --- a/lib/routes/supchina/index.ts +++ b/lib/routes/supchina/index.ts @@ -43,7 +43,7 @@ async function handler(ctx) { author: item .find(String.raw`dc\:creator`) .html() - .match(/CDATA\[(.*?)]/)[1], + .match(/CDATA\[(.*?)\]/)[1], category: item .find('category') .toArray() @@ -51,7 +51,7 @@ async function handler(ctx) { (c) => $(c) .html() - .match(/CDATA\[(.*?)]/)[1] + .match(/CDATA\[(.*?)\]/)[1] ), pubDate: parseDate(item.find('pubDate').text()), }; diff --git a/lib/routes/swjtu/gsee/yjs.ts b/lib/routes/swjtu/gsee/yjs.ts index 5e2bd3feab6f..83515ddadfd4 100644 --- a/lib/routes/swjtu/gsee/yjs.ts +++ b/lib/routes/swjtu/gsee/yjs.ts @@ -13,7 +13,7 @@ const getItem = (item) => { const newsDate = item .find('dd') .text() - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)[0]; + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)[0]; const infoTitle = newsInfo.text(); const link = rootURL + newsInfo.find('a').last().attr('href').slice(2); diff --git a/lib/routes/swjtu/scai.ts b/lib/routes/swjtu/scai.ts index a9dcf8798327..45056316b18b 100644 --- a/lib/routes/swjtu/scai.ts +++ b/lib/routes/swjtu/scai.ts @@ -67,7 +67,7 @@ const getItem = (item, cache) => { // 'date' may be undefined. and 'parseDate' will return current time. // 转其他院的通知,获取不到具体时间,先从列表页获取具体信息 if (dateText) { - const dateMatch = dateText.match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/); + const dateMatch = dateText.match(/\d{4}(.)\d{1,2}\1\d{1,2}/); if (!dateMatch || !dateMatch[0]) { return null; } diff --git a/lib/routes/swjtu/sports.ts b/lib/routes/swjtu/sports.ts index edaaa63d52fa..c3967bc62e3b 100644 --- a/lib/routes/swjtu/sports.ts +++ b/lib/routes/swjtu/sports.ts @@ -44,7 +44,7 @@ const getItem = (item, cache) => { $('div.info span:nth-of-type(3)') .text() .slice(3) - .match(/\d{4}(-|\/|.)\d{1,2}\1\d{1,2}/)?.[0] + .match(/\d{4}(.)\d{1,2}\1\d{1,2}/)?.[0] ); const description = $('div.detail-wrap').html(); return { diff --git a/lib/routes/swpu/utils.ts b/lib/routes/swpu/utils.ts index 11cb95227443..4ae3ccdd1d08 100644 --- a/lib/routes/swpu/utils.ts +++ b/lib/routes/swpu/utils.ts @@ -1,5 +1,5 @@ function isCompleteUrl(url) { - return /^\w+?:\/\/.*?\//.test(url); + return /^\w+:\/\/.*?\//.test(url); } function joinUrl(url1, url2) { diff --git a/lib/routes/szse/disclosure/listed-notice.ts b/lib/routes/szse/disclosure/listed-notice.ts index 37dba801088b..075aec9875bd 100644 --- a/lib/routes/szse/disclosure/listed-notice.ts +++ b/lib/routes/szse/disclosure/listed-notice.ts @@ -10,7 +10,7 @@ import timezone from '@/utils/timezone'; function isValidDate(dateString: string): boolean { // 正则表达式检查格式:YYYY-MM-DD - const regex = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/; + const regex = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/; if (!regex.test(dateString)) { return false; } diff --git a/lib/routes/tass/news.ts b/lib/routes/tass/news.ts index 8463e762ea9c..429298022372 100644 --- a/lib/routes/tass/news.ts +++ b/lib/routes/tass/news.ts @@ -40,7 +40,7 @@ async function handler(ctx) { const sectionId = $('.container .section-page') .attr('ng-init') - .match(/sectionId\s*=\s*(\d+?);/); + .match(/sectionId\s*=\s*(\d+);/); const { data: response } = await got.post('https://tass.com/userApi/categoryNewsList', { json: { diff --git a/lib/routes/tencent/news/author.tsx b/lib/routes/tencent/news/author.tsx index 23de40840dc5..02c6da3bcfaf 100644 --- a/lib/routes/tencent/news/author.tsx +++ b/lib/routes/tencent/news/author.tsx @@ -68,7 +68,7 @@ async function handler(ctx): Promise { const data = JSON.parse( $('script:contains("window.DATA")') .text() - .match(/window\.DATA = ({.+});/)[1] + .match(/window\.DATA = (\{.+\});/)[1] ); const $data = load(data.originContent?.text || '', null, false); if ($data) { diff --git a/lib/routes/tesla/cx.ts b/lib/routes/tesla/cx.ts index 8ea59460c1e3..116fead80aa3 100644 --- a/lib/routes/tesla/cx.ts +++ b/lib/routes/tesla/cx.ts @@ -145,7 +145,7 @@ async function handler(ctx) { alt: item.venueName ?? item.title, } : undefined, - description: item.description?.replaceAll(/\["|"]/g, '') ?? undefined, + description: item.description?.replaceAll(/\["|"\]/g, '') ?? undefined, data: item.parkingLocationId ? { title: item.venueName ?? item.title, diff --git a/lib/routes/threads/utils.ts b/lib/routes/threads/utils.ts index d26a88751456..2570f653fa28 100644 --- a/lib/routes/threads/utils.ts +++ b/lib/routes/threads/utils.ts @@ -31,7 +31,7 @@ const extractTokens = async (user): Promise<{ lsd: string }> => { const $ = load(response); const data = $('script:contains("LSD"):first').text(); - const lsd = data.match(/"LSD",\[],{"token":"([\w@-]+)"},/)?.[1]; + const lsd = data.match(/"LSD",\[\],\{"token":"([\w@-]+)"\},/)?.[1]; if (!lsd) { throw new NotFoundError('LSD token not found'); diff --git a/lib/routes/transcriptforest/index.ts b/lib/routes/transcriptforest/index.ts index 254f5492c5d9..072bc3506d37 100644 --- a/lib/routes/transcriptforest/index.ts +++ b/lib/routes/transcriptforest/index.ts @@ -41,7 +41,7 @@ async function handler(ctx) { const { data: firstResponse } = await got(rootUrl); - const data = JSON.parse(firstResponse.match(/({"props".*"scriptLoader":\[]})<\/script>/)?.[1]); + const data = JSON.parse(firstResponse.match(/(\{"props".*"scriptLoader":\[\]\})<\/script>/)?.[1]); const buildId = data.buildId; const defaultLocale = data.defaultLocale; diff --git a/lib/routes/twitter/api/web-api/gql-id-resolver.ts b/lib/routes/twitter/api/web-api/gql-id-resolver.ts index aac492bf1106..91c304c90040 100644 --- a/lib/routes/twitter/api/web-api/gql-id-resolver.ts +++ b/lib/routes/twitter/api/web-api/gql-id-resolver.ts @@ -30,7 +30,7 @@ async function fetchTwitterPage(): Promise { function extractQueryIds(scriptContent: string): Record { const ids: Record = {}; - const matches = scriptContent.matchAll(/queryId:"([^"]+?)".+?operationName:"([^"]+?)"/g); + const matches = scriptContent.matchAll(/queryId:"([^"]+)".+?operationName:"([^"]+)"/g); for (const match of matches) { const [, queryId, operationName] = match; if (operationNames.includes(operationName)) { diff --git a/lib/routes/twitter/utils.ts b/lib/routes/twitter/utils.ts index afe8532f08ba..cadd107c0d70 100644 --- a/lib/routes/twitter/utils.ts +++ b/lib/routes/twitter/utils.ts @@ -11,7 +11,7 @@ const getOriginalImg = (url) => { format = 'jpg'; } return `${m[1]}?format=${format}&name=orig`; - } else if ((m = url.match(/^(https?:\/\/\w+\.twimg\.com\/.+)(\?.+)$/i))) { + } else if ((m = url.match(/^(https?:\/\/\w+\.twimg\.com\/[^?]+)(\?.+)$/i))) { const pars = getQueryParams(url); if (!pars.format || !pars.name) { return url; diff --git a/lib/routes/txrjy/fornumtopic.tsx b/lib/routes/txrjy/fornumtopic.tsx index 18ecdd6896bc..d3dcf242227e 100644 --- a/lib/routes/txrjy/fornumtopic.tsx +++ b/lib/routes/txrjy/fornumtopic.tsx @@ -70,12 +70,12 @@ async function handler(ctx) { .remove() .end() .html() - ?.replaceAll(/()/g, '$1$2') + ?.replaceAll(/()/g, '$1$2') .replaceAll(/()/g, '$1src$2'); const pattlHtml = content(item) .find('div.pattl') .html() - ?.replaceAll(/()/g, '$1$2') + ?.replaceAll(/()/g, '$1$2') .replaceAll(/()/g, '$1src$2'); const author = content(item).find('a.xw1').text().trim(); diff --git a/lib/routes/udn/breaking-news.tsx b/lib/routes/udn/breaking-news.tsx index 4fa9fe943a1e..85099d70fb86 100644 --- a/lib/routes/udn/breaking-news.tsx +++ b/lib/routes/udn/breaking-news.tsx @@ -62,7 +62,7 @@ async function handler(ctx) { .eq(0) .text() .trim() - .replaceAll(/[\b\t\n]/g, ''); + .replaceAll(/[\t\n]/g, ''); const data = metadata.startsWith('[') ? JSON.parse(metadata)[0] : JSON.parse(metadata); // e.g. https://udn.com/news/story/7331/6576320 const content = $('.article-content__editor'); @@ -90,7 +90,7 @@ async function handler(ctx) { // 轉角24小時 description = $('.story_body_content') .html() - .split(//g) + .split(//g) .slice(1, -1) .join(''); } diff --git a/lib/routes/upc/jwc.ts b/lib/routes/upc/jwc.ts index b0c67c28dfc6..a69e5da01ec8 100644 --- a/lib/routes/upc/jwc.ts +++ b/lib/routes/upc/jwc.ts @@ -48,7 +48,7 @@ const handler = async (ctx) => { const scriptContent = $('body script').first().html(); let dataObj = null; if (scriptContent) { - const match = scriptContent.match(/data\s*:\s*function\s*\(\)\s*{\s*return\s*{[^}]*data\s*:\s*({[\s\S]*?})/); + const match = scriptContent.match(/data\s*:\s*function\s*\(\)\s*\{\s*return\s*\{[^}]*data\s*:\s*(\{[\s\S]*?\})/); if (match && match[1]) { const dataStr = match[1]; dataObj = JSON.parse(dataStr); diff --git a/lib/routes/ups/track.ts b/lib/routes/ups/track.ts index 5421c6d30dde..e7646f54dd0c 100644 --- a/lib/routes/ups/track.ts +++ b/lib/routes/ups/track.ts @@ -68,7 +68,7 @@ async function handler(ctx) { const dateTimeStr = dateTimeRaw .trim() - .replace(/(\d{1,}\/\d{1,}\/\d{4})(\d{1,}:\d{1,}\s[AP]\.?M\.?)/, '$1 $2') + .replace(/(\d+\/\d+\/\d{4})(\d+:\d+\s[AP]\.?M\.?)/, '$1 $2') .replaceAll('P.M.', 'PM') .replaceAll('A.M.', 'AM'); @@ -78,7 +78,7 @@ async function handler(ctx) { .find(`#stApp_milestoneActivityLocation${i}`) .text() .trim() - .replaceAll(/\s*\n+\s*/g, '\n'); + .replaceAll(/\s*\n\s*/g, '\n'); const lines = activityCellText .split('\n') diff --git a/lib/routes/uptimerobot/rss.tsx b/lib/routes/uptimerobot/rss.tsx index 6b1afae07eb9..a5fdbdd8e912 100644 --- a/lib/routes/uptimerobot/rss.tsx +++ b/lib/routes/uptimerobot/rss.tsx @@ -6,7 +6,7 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { fallback, queryToBoolean } from '@/utils/readable-social'; -const titleRegex = /(.+)\s+is\s+([A-Z]+)\s+\((.+)\)/; +const titleRegex = /(.*\S)\s+is\s+([A-Z]+)\s+\((.+)\)/; const formatTime = (s) => { const duration = dayjs.duration(s - 0, 'seconds'); diff --git a/lib/routes/vcb-s/category.ts b/lib/routes/vcb-s/category.ts index bfd8880b927c..890a15845153 100644 --- a/lib/routes/vcb-s/category.ts +++ b/lib/routes/vcb-s/category.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const items = data.map((item) => { const description = renderDescription({ - post: item.content.rendered.replaceAll(/
    ]*>(.*?)<\/pre>/gs, '
    $1
    ').replaceAll(/]+>(.*?)<\/div>/gs, '
    $1
    '), medias: item._embedded['wp:featuredmedia'], }); diff --git a/lib/routes/vcb-s/index.ts b/lib/routes/vcb-s/index.ts index df8ec6c9f6aa..1895f826d270 100644 --- a/lib/routes/vcb-s/index.ts +++ b/lib/routes/vcb-s/index.ts @@ -33,7 +33,7 @@ async function handler(ctx) { const items = data.map((item) => { const description = renderDescription({ - post: item.content.rendered.replaceAll(/
    ]*>(.*?)<\/pre>/gs, '
    $1
    ').replaceAll(/]+>(.*?)<\/div>/gs, '
    $1
    '), medias: item._embedded['wp:featuredmedia'], }); diff --git a/lib/routes/weibo/utils.ts b/lib/routes/weibo/utils.ts index c4df2ea2fd19..67511437dcb4 100644 --- a/lib/routes/weibo/utils.ts +++ b/lib/routes/weibo/utils.ts @@ -26,11 +26,11 @@ const formatDescriptionText = (html, { showEmojiInDescription, showLinkIconInDes let formattedHtml = html; if (!showEmojiInDescription) { - formattedHtml = formattedHtml.replaceAll(/]*?alt=["']?([^>]+?)["']?\s[^>]*?\/><\/span>/g, '$1'); + formattedHtml = formattedHtml.replaceAll(/]*?alt=["']?([^>\s"']+)["']?\s[^>]*?\/><\/span>/g, '$1'); } if (!showLinkIconInDescription) { - formattedHtml = formattedHtml.replaceAll(/(]*>)]*><\/span>[^<>]*?([^<>]*?)<\/span><\/a>/g, '$1$2'); + formattedHtml = formattedHtml.replaceAll(/(]*>)]*><\/span>[^<>]*([^<>]*)<\/span><\/a>/g, '$1$2'); } return formattedHtml; @@ -141,7 +141,7 @@ const weiboUtils = { })(), formatTitle: (html) => html - .replaceAll(/]*?alt=["']?([^>]+?)["']?\s[^>]*?\/?><\/span>/g, '$1') // 表情转换 + .replaceAll(/]*?alt=["']?([^>\s"']+)["']?\s[^>]*?\/?><\/span>/g, '$1') // 表情转换 .replaceAll(/(]*>)<\/span>/g, '') // 去掉所有图标 .replaceAll(//g, '[图片]') // impossible to have inline script in weibo posts, but CodeQL complains about it diff --git a/lib/routes/wenku8/volume.ts b/lib/routes/wenku8/volume.ts index ff0381de503a..addd9ea65c91 100644 --- a/lib/routes/wenku8/volume.ts +++ b/lib/routes/wenku8/volume.ts @@ -42,7 +42,7 @@ async function handler(ctx) { title: `轻小说文库 ${$('#title').text()} 最新卷`, link, item: await cache.tryGet(volumeUrl, async () => - [...(await get(volumeUrl)).matchAll(/\s{2}(\S.*)\r?\n([\S\s]+?)\r?\n\r?\n/g)] + [...(await get(volumeUrl)).matchAll(/\s{2}(\S.*)\r?\n([\s\S]+?)\r?\n\r?\n/g)] .map((chapter, index) => ({ title: chapter[1], description: chapter[2] diff --git a/lib/routes/wfu/news.ts b/lib/routes/wfu/news.ts index c1945c2a7073..613e796ed73a 100644 --- a/lib/routes/wfu/news.ts +++ b/lib/routes/wfu/news.ts @@ -24,7 +24,7 @@ async function loadContent(link) { let response; // 如果不是 大学的站点, 直接返回简单的标题即可 // 判断 是否外站链接,如果是 则直接返回页面 不做单独的解析 - const https_reg = /^https:\/\/www.wfu.edu.cn(.*)/; + const https_reg = /^https:\/\/www\.wfu\.edu\.cn\/.*/; if (!https_reg.test(link)) { return { description }; } diff --git a/lib/routes/wikipedia/current-events.ts b/lib/routes/wikipedia/current-events.ts index 8ae433d945ec..82cd50125781 100644 --- a/lib/routes/wikipedia/current-events.ts +++ b/lib/routes/wikipedia/current-events.ts @@ -51,12 +51,12 @@ function parseCurrentEventsTemplate(wikitext: string): string | null { // Look for {{Current events|content=...}} template // The closing }} is always at the end of wikitext - const contentMatch = wikitext.match(/\{\{Current events\s*\|[\s\S]*?content\s*=\s*([\s\S]*)\}\}$/); + const contentMatch = wikitext.match(/\{\{Current events\s*\|[\s\S]*?content(?=(\s*=))\1\s*((?:\S[\s\S]*)?)\}\}$/); if (!contentMatch) { return null; } - let content = contentMatch[1].trim(); + let content = contentMatch[2].trim(); // Strip comments to detect empty content content = stripComments(content); @@ -84,7 +84,7 @@ function convertWikiLinks(html: string): string { function convertExternalLinks(html: string): string { // Convert external links [URL Text] or [URL] - html = html.replaceAll(/\[([^\s\]]+)\s+([^\]]+)\]/g, '$2'); + html = html.replaceAll(/\[([^\s\]]+)\s+([^\s\]][^\]]*|\s)\]/g, '$2'); html = html.replaceAll(/\[([^\s\]]+)\]/g, '$1'); return html; } @@ -186,7 +186,7 @@ function processListsAndLines(html: string): string { } // Check for bullet points - const bulletMatch = trimmedLine.match(/^(\*+)\s*(.*)$/); + const bulletMatch = trimmedLine.match(/^(\*+)(?!\*)\s*((?:\S.*)?)$/); if (bulletMatch) { const depth = bulletMatch[1].length; const content = bulletMatch[2]; diff --git a/lib/routes/wmc-bj/publish.tsx b/lib/routes/wmc-bj/publish.tsx index e16afd07207e..822fa2653b67 100644 --- a/lib/routes/wmc-bj/publish.tsx +++ b/lib/routes/wmc-bj/publish.tsx @@ -46,7 +46,7 @@ async function handler(ctx) { ), category: categories, guid: `${currentUrl}#${datetime}`, - pubDate: timezone(parseDate(/^[A-Za-z]{3}/.test(datetime) ? datetime.replace(/^\w+/, '') : datetime, ['DD MMM HH:mm', 'MM/DD HH:mm']), +0), + pubDate: timezone(parseDate(/^[A-Z]{3}/i.test(datetime) ? datetime.replace(/^\w+/, '') : datetime, ['DD MMM HH:mm', 'MM/DD HH:mm']), +0), }, ]; diff --git a/lib/routes/wnacg/common.tsx b/lib/routes/wnacg/common.tsx index 16cf18bfbce6..5c1edf68e0fb 100644 --- a/lib/routes/wnacg/common.tsx +++ b/lib/routes/wnacg/common.tsx @@ -85,7 +85,7 @@ export async function handler(ctx) { const imgListMatch = $('script') .text() - .match(/var imglist = (\[.*]);"\);/)[1]; + .match(/var imglist = (\[.*\]);"\);/)[1]; const imgList = JSON.parse(imgListMatch.replaceAll('url:', '"url":').replaceAll('caption:', '"caption":').replaceAll('fast_img_host+\\', '').replaceAll('\\', '')); diff --git a/lib/routes/wordpress/index.ts b/lib/routes/wordpress/index.ts index a0558f897558..225f0d44d4f0 100644 --- a/lib/routes/wordpress/index.ts +++ b/lib/routes/wordpress/index.ts @@ -17,7 +17,7 @@ async function handler(ctx) { throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } - if (!/^(https?):\/\/[^\s#$./?].\S*$/i.test(url)) { + if (!/^https?:\/\/[^\s#$./?].\S*$/i.test(url)) { throw new Error('Invalid URL'); } @@ -39,7 +39,7 @@ async function handler(ctx) { try { const { data: response } = await got(apiUrl); - const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*])$/)[1])).slice(0, limit).map((item) => { + const items = (Array.isArray(response) ? response : JSON.parse(response.match(/(\[.*\])$/)[1])).slice(0, limit).map((item) => { const terminologies = item._embedded['wp:term']; const guid = item.guid?.rendered ?? item.guid; diff --git a/lib/routes/wsj/news.ts b/lib/routes/wsj/news.ts index c9c575f0e119..034cc4016e7b 100644 --- a/lib/routes/wsj/news.ts +++ b/lib/routes/wsj/news.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const $ = load(response.data); const contents = $('script:contains("window.__STATE__")').text(); - const data = JSON.parse(contents.match(/{.*}/)[0]).data; + const data = JSON.parse(contents.match(/\{.*\}/)[0]).data; const filteredKeys = Object.entries(data) .filter(([key, value]) => { if (!key.startsWith('article')) { diff --git a/lib/routes/xaufe/jiaowu.ts b/lib/routes/xaufe/jiaowu.ts index 6f8a568aa059..43cac6d1cf95 100644 --- a/lib/routes/xaufe/jiaowu.ts +++ b/lib/routes/xaufe/jiaowu.ts @@ -80,7 +80,7 @@ async function handler(ctx) { url: item.link, }); const $ = load(response.body); - item.author = /作者:(\S*)\s{4}/g.exec($('p', '.main_contit').text())[1]; + item.author = /作者:(\S*)\s{4}/.exec($('p', '.main_contit').text())[1]; item.description = $('#vsb_content').html(); return item; }) diff --git a/lib/routes/xhamster/index.ts b/lib/routes/xhamster/index.ts index c92562d5a00c..144b6d36444a 100644 --- a/lib/routes/xhamster/index.ts +++ b/lib/routes/xhamster/index.ts @@ -60,7 +60,7 @@ interface Initials { } function extractInitials(scriptContent: string): Initials { - const match = scriptContent.match(/window\.initials\s*=\s*([\s\S]*?);?$/); + const match = scriptContent.match(/window\.initials\s*=\s*(\S[\s\S]*?);?$/); if (!match) { throw new Error('initials not found'); } diff --git a/lib/routes/xinpianchang/index.ts b/lib/routes/xinpianchang/index.ts index 55a662320595..3509d86e8312 100644 --- a/lib/routes/xinpianchang/index.ts +++ b/lib/routes/xinpianchang/index.ts @@ -34,7 +34,7 @@ async function handler(ctx) { const { data, response } = await getData(currentUrl, cache.tryGet); - let items = JSON.parse(response.match(/"list":(\[.*?]),"total"/)[1]); + let items = JSON.parse(response.match(/"list":(\[.*?\]),"total"/)[1]); items = await processItems(items.slice(0, limit), cache.tryGet); diff --git a/lib/routes/xueqiu/snb.ts b/lib/routes/xueqiu/snb.ts index 141f971b2657..32ac6e1abf62 100644 --- a/lib/routes/xueqiu/snb.ts +++ b/lib/routes/xueqiu/snb.ts @@ -35,7 +35,7 @@ async function handler(ctx) { }); const data = response.data; - const pattern = /SNB.cubeInfo = {(.+)}/; + const pattern = /SNB.cubeInfo = \{(.+)\}/; const info = pattern.exec(data); const obj = JSON.parse('{' + info[1] + '}'); const rebalancing_histories = obj.sell_rebalancing.rebalancing_histories; diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index 5b111f133908..641d81f902f7 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -92,7 +92,7 @@ async function handler(ctx) { const content = await mainPage.evaluate(() => { const articleContent = document.querySelector('.article__bd')?.innerHTML || ''; - const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?});/); + const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?\});/); return { articleContent, statusData: statusMatch ? statusMatch[1] : null, diff --git a/lib/routes/xys/new.tsx b/lib/routes/xys/new.tsx index 921714e76537..c50e50dfdff5 100644 --- a/lib/routes/xys/new.tsx +++ b/lib/routes/xys/new.tsx @@ -67,7 +67,7 @@ async function handler(ctx) { .filter((item) => !item.link.endsWith('.zip')) .map((item) => cache.tryGet(item.link, async () => { - const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-]+)&?/g; + const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be.*(?:v=|v\/|\/)([\w-]+)&?/g; const matchYoutube = item.link.match(youTube); if (matchYoutube) { diff --git a/lib/routes/yamibo/utils.ts b/lib/routes/yamibo/utils.ts index 437d5d14a114..1fcdb07f4900 100644 --- a/lib/routes/yamibo/utils.ts +++ b/lib/routes/yamibo/utils.ts @@ -45,12 +45,12 @@ export async function fetchThread( // sometimes may trigger anti-crawling measures if (data.startsWith(''; - -describe('/obsidian', () => { - it('builds the plugins feed from the community search API sorted by creation time', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.get('https://community.obsidian.md/search', ({ request }) => { - const url = new URL(request.url); - - expect(url.searchParams.get('type')).toBe('plugin'); - expect(url.searchParams.get('sort')).toBe('created'); - - return HttpResponse.html(searchConfigHtml); - }), - http.get('https://community.obsidian.md/api/search/collections/entries/documents/search', ({ request }) => { - const url = new URL(request.url); - - expect(request.headers.get('x-typesense-api-key')).toBe('test-search-key'); - expect(url.searchParams.get('q')).toBe('*'); - expect(url.searchParams.get('query_by')).toBe('name,authors,short_desc'); - expect(url.searchParams.get('filter_by')).toBe('type:=plugin'); - expect(url.searchParams.get('sort_by')).toBe('github_created_at:desc'); - expect(url.searchParams.get('per_page')).toBe('54'); - - return HttpResponse.json({ - hits: [ - { - document: { - authors: ['slaymish'], - downloads: 2, - github_created_at: '2026-05-14T05:08:50Z', - id: '5757', - name: 'Synod', - short_desc: 'Run a council of LLM value agents over your journal.', - slug: 'synod', - tags: ['ai', 'import'], - type: 'plugin', - }, - }, - ], - }); - }) - ); - - const feed = await pluginsRoute.handler(); - - expect(feed.title).toBe('Obsidian Plugins'); - expect(feed.link).toBe('https://community.obsidian.md/search?type=plugin&sort=created'); - expect(feed.item).toHaveLength(1); - expect(feed.item[0]).toMatchObject({ - title: 'Synod', - description: 'Run a council of LLM value agents over your journal.', - link: 'https://community.obsidian.md/plugins/synod', - guid: 'plugin:5757', - author: 'slaymish', - category: ['ai', 'import'], - }); - expect(feed.item[0].pubDate?.toISOString()).toBe('2026-05-14T05:08:50.000Z'); - }); - - it('builds the themes feed from the community search API sorted by creation time', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.get('https://community.obsidian.md/search', ({ request }) => { - const url = new URL(request.url); - - expect(url.searchParams.get('type')).toBe('theme'); - expect(url.searchParams.get('sort')).toBe('created'); - - return HttpResponse.html(searchConfigHtml); - }), - http.get('https://community.obsidian.md/api/search/collections/entries/documents/search', ({ request }) => { - const url = new URL(request.url); - - expect(request.headers.get('x-typesense-api-key')).toBe('test-search-key'); - expect(url.searchParams.get('filter_by')).toBe('type:=theme'); - expect(url.searchParams.get('sort_by')).toBe('github_created_at:desc'); - - return HttpResponse.json({ - hits: [ - { - document: { - authors: ['jshuntley'], - downloads: 0, - github_created_at: '2026-05-14T00:28:26Z', - id: '5749', - name: 'Fjord', - short_desc: 'Fjord colorscheme for Obsidian.', - slug: 'fjord', - tags: [], - type: 'theme', - }, - }, - ], - }); - }) - ); - - const feed = await themesRoute.handler(); - - expect(feed.title).toBe('Obsidian Themes'); - expect(feed.link).toBe('https://community.obsidian.md/search?type=theme&sort=created'); - expect(feed.item).toHaveLength(1); - expect(feed.item[0]).toMatchObject({ - title: 'Fjord', - description: 'Fjord colorscheme for Obsidian.', - link: 'https://community.obsidian.md/themes/fjord', - guid: 'theme:5749', - author: 'jshuntley', - category: [], - }); - expect(feed.item[0].pubDate?.toISOString()).toBe('2026-05-14T00:28:26.000Z'); - }); -}); diff --git a/lib/registry.dynamic.test.ts b/lib/registry.dynamic.test.ts deleted file mode 100644 index 0d2b4e090663..000000000000 --- a/lib/registry.dynamic.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import type fs from 'node:fs'; -import path from 'node:path'; - -import { Hono } from 'hono'; -import { describe, expect, it, vi } from 'vitest'; - -// The dev registry lists lib/routes at startup; expose the fixture directory names there -const fakeTopDirectories = vi.hoisted(() => ({ names: [] as string[] })); - -vi.mock('node:fs', async (importOriginal) => { - const actual = await importOriginal(); - const readdirSync = ((target: unknown, options: unknown) => { - if (fakeTopDirectories.names.length > 0 && String(target).endsWith(path.join('lib', 'routes'))) { - return fakeTopDirectories.names.map((name) => ({ name, isDirectory: () => true })); - } - return actual.readdirSync(target as never, options as never); - }) as typeof actual.readdirSync; - return { ...actual, readdirSync, default: { ...actual, readdirSync } }; -}); - -const perDirectoryMock = (fakeDirectories: Record>) => { - fakeTopDirectories.names = Object.keys(fakeDirectories); - return vi.fn(({ targetDirectoryPath }: { targetDirectoryPath: string }) => { - const name = targetDirectoryPath.split(/[/\\]/).findLast(Boolean) as string; - return Promise.resolve(fakeDirectories[name]); - }); -}; - -const wrap = (registry: Hono) => { - const app = new Hono(); - app.use(async (ctx, next) => { - const response = await next(); - const apiData = ctx.get('apiData'); - if (apiData) { - return ctx.json(apiData); - } - const data = ctx.get('data'); - if (data) { - return ctx.json(data); - } - return response; - }); - app.route('/', registry); - return app; -}; - -describe('registry dynamic loading', () => { - it('loads production namespaces from build', async () => { - const originalEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'production'; - vi.resetModules(); - - const { namespaces } = await import('@/registry'); - expect(Object.keys(namespaces).length).toBeGreaterThan(0); - - process.env.NODE_ENV = originalEnv; - }); - - it('lazily builds namespaces from directory import on first request', async () => { - const originalEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - - const directoryImportMock = perDirectoryMock({ - nsRoute: { - '/route-array.ts': { - route: { - path: ['/a', '/b'], - name: 'Array', - handler: () => ({ title: 'array', link: 'https://example.com', item: [], allowEmpty: true }), - }, - }, - '/route-single.ts': { - route: { - path: '/single', - name: 'Single', - handler: () => ({ title: 'ok', link: 'https://example.com', item: [], allowEmpty: true }), - }, - }, - }, - nsModule: { - '/route-module.ts': { - route: { - path: '/module', - name: 'Module', - module: () => - Promise.resolve({ - route: { - handler: () => new Response('module'), - }, - }), - }, - }, - }, - nsApi: { - '/api-single.ts': { - apiRoute: { - path: '/single', - name: 'ApiSingle', - handler: () => ({ ok: true }), - }, - }, - '/api-module.ts': { - apiRoute: { - path: '/module', - name: 'ApiModule', - module: () => - Promise.resolve({ - apiRoute: { - handler: () => ({ ok: true }), - }, - }), - }, - }, - }, - test: { - '/api-index.ts': { - apiRoute: { - path: '/', - name: 'ApiIndex', - }, - }, - }, - }); - vi.doMock('@/utils/directory-import', () => ({ - directoryImport: directoryImportMock, - })); - vi.resetModules(); - const { namespaces, default: registry } = await import('@/registry'); - - // The cold-start guarantee: importing the registry imports no route modules - expect(directoryImportMock).not.toHaveBeenCalled(); - - const app = wrap(registry); - - const routeResponse = await app.request('/nsModule/module'); - expect(await routeResponse.text()).toBe('module'); - expect(directoryImportMock).toHaveBeenCalledTimes(1); - - const singleResponse = await app.request('/nsRoute/single'); - const singleBody = await singleResponse.json(); - expect(singleBody.title).toBe('ok'); - expect(namespaces.nsRoute.routes['/single']).toBeDefined(); - expect(namespaces.nsRoute.routes['/a']).toBeDefined(); - - const apiResponse = await app.request('/api/nsApi/module'); - const apiBody = await apiResponse.json(); - expect(apiBody).toEqual({ ok: true }); - expect(namespaces.nsApi.apiRoutes['/single']).toBeDefined(); - - // Handler resolution from disk in test env (real file lib/routes/test/api-index.ts) - process.env.NODE_ENV = 'test'; - const apiTestResponse = await app.request('/api/test'); - expect(await apiTestResponse.json()).toEqual({ code: 0 }); - - process.env.NODE_ENV = originalEnv; - vi.doUnmock('@/utils/directory-import'); - }); - - // https://github.com/DIYgod/RSSHub/pull/18002 - it('prioritizes literal segments over parameter segments in route matching', async () => { - const originalEnv = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - - const directoryImportMock = perDirectoryMock({ - specificity: { - '/param-route.ts': { - route: { - path: '/:category', - name: 'ParamRoute', - handler: () => ({ title: 'param', link: 'https://example.com', item: [], allowEmpty: true }), - }, - }, - '/literal-route.ts': { - route: { - path: '/news/:channel', - name: 'LiteralRoute', - handler: () => ({ title: 'literal', link: 'https://example.com', item: [], allowEmpty: true }), - }, - }, - '/news-route.ts': { - route: { - path: '/news', - name: 'NewsRoute', - handler: () => ({ title: 'news', link: 'https://example.com', item: [], allowEmpty: true }), - }, - }, - }, - }); - vi.doMock('@/utils/directory-import', () => ({ - directoryImport: directoryImportMock, - })); - vi.resetModules(); - const { default: registry } = await import('@/registry'); - - const app = wrap(registry); - - // /news/sports should match /news/:channel (literal "news" wins over :category) - const literalResponse = await app.request('/specificity/news/sports'); - expect((await literalResponse.json()).title).toBe('literal'); - - // /news should match /news (literal wins over :category) - const newsResponse = await app.request('/specificity/news'); - expect((await newsResponse.json()).title).toBe('news'); - - // /products should match /:category - const paramResponse = await app.request('/specificity/products'); - expect((await paramResponse.json()).title).toBe('param'); - - process.env.NODE_ENV = originalEnv; - vi.doUnmock('@/utils/directory-import'); - }); -}); diff --git a/lib/registry.test.ts b/lib/registry.test.ts index 27d691911a15..da75e1933702 100644 --- a/lib/registry.test.ts +++ b/lib/registry.test.ts @@ -1,10 +1,28 @@ -import { describe, expect, it, vi } from 'vitest'; +import type fs from 'node:fs'; +import path from 'node:path'; + +import { Hono } from 'hono'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import app from '@/app'; import { config } from '@/config'; import registryApp, { collectNamespaceRoots, namespaces, resolveModuleNamespace, sortRoutes } from '@/registry'; import type { Route } from '@/types'; +// The dev registry lists lib/routes at startup; expose the fixture directory names there +const fakeTopDirectories = vi.hoisted(() => ({ names: [] as string[] })); + +vi.mock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + const readdirSync = ((target: unknown, options: unknown) => { + if (fakeTopDirectories.names.length > 0 && String(target).endsWith(path.join('lib', 'routes'))) { + return fakeTopDirectories.names.map((name) => ({ name, isDirectory: () => true })); + } + return actual.readdirSync(target as never, options as never); + }) as typeof actual.readdirSync; + return { ...actual, readdirSync, default: { ...actual, readdirSync } }; +}); + describe('registry', () => { // root it('/', async () => { @@ -137,3 +155,200 @@ describe('nested namespace mounting', () => { expect(sorted.map(([path]) => path)).toEqual(['/static', '/:id{[0-9]+}', '/:category?']); }); }); + +const perDirectoryMock = (fakeDirectories: Record>) => { + fakeTopDirectories.names = Object.keys(fakeDirectories); + return vi.fn(({ targetDirectoryPath }: { targetDirectoryPath: string }) => { + const name = targetDirectoryPath.split(/[/\\]/).findLast(Boolean) as string; + return Promise.resolve(fakeDirectories[name]); + }); +}; + +const wrap = (registry: Hono) => { + const app = new Hono(); + app.use(async (ctx, next) => { + const response = await next(); + const apiData = ctx.get('apiData'); + if (apiData) { + return ctx.json(apiData); + } + const data = ctx.get('data'); + if (data) { + return ctx.json(data); + } + return response; + }); + app.route('/', registry); + return app; +}; + +describe('registry dynamic loading', () => { + afterEach(() => { + fakeTopDirectories.names = []; + }); + + it('loads production namespaces from build', async () => { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + vi.resetModules(); + + const { namespaces } = await import('@/registry'); + expect(Object.keys(namespaces).length).toBeGreaterThan(0); + + process.env.NODE_ENV = originalEnv; + }); + + it('lazily builds namespaces from directory import on first request', async () => { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + const directoryImportMock = perDirectoryMock({ + nsRoute: { + '/route-array.ts': { + route: { + path: ['/a', '/b'], + name: 'Array', + handler: () => ({ title: 'array', link: 'https://example.com', item: [], allowEmpty: true }), + }, + }, + '/route-single.ts': { + route: { + path: '/single', + name: 'Single', + handler: () => ({ title: 'ok', link: 'https://example.com', item: [], allowEmpty: true }), + }, + }, + }, + nsModule: { + '/route-module.ts': { + route: { + path: '/module', + name: 'Module', + module: () => + Promise.resolve({ + route: { + handler: () => new Response('module'), + }, + }), + }, + }, + }, + nsApi: { + '/api-single.ts': { + apiRoute: { + path: '/single', + name: 'ApiSingle', + handler: () => ({ ok: true }), + }, + }, + '/api-module.ts': { + apiRoute: { + path: '/module', + name: 'ApiModule', + module: () => + Promise.resolve({ + apiRoute: { + handler: () => ({ ok: true }), + }, + }), + }, + }, + }, + test: { + '/api-index.ts': { + apiRoute: { + path: '/', + name: 'ApiIndex', + }, + }, + }, + }); + vi.doMock('@/utils/directory-import', () => ({ + directoryImport: directoryImportMock, + })); + vi.resetModules(); + const { namespaces, default: registry } = await import('@/registry'); + + // The cold-start guarantee: importing the registry imports no route modules + expect(directoryImportMock).not.toHaveBeenCalled(); + + const app = wrap(registry); + + const routeResponse = await app.request('/nsModule/module'); + expect(await routeResponse.text()).toBe('module'); + expect(directoryImportMock).toHaveBeenCalledTimes(1); + + const singleResponse = await app.request('/nsRoute/single'); + const singleBody = await singleResponse.json(); + expect(singleBody.title).toBe('ok'); + expect(namespaces.nsRoute.routes['/single']).toBeDefined(); + expect(namespaces.nsRoute.routes['/a']).toBeDefined(); + + const apiResponse = await app.request('/api/nsApi/module'); + const apiBody = await apiResponse.json(); + expect(apiBody).toEqual({ ok: true }); + expect(namespaces.nsApi.apiRoutes['/single']).toBeDefined(); + + // Handler resolution from disk in test env (real file lib/routes/test/api-index.ts) + process.env.NODE_ENV = 'test'; + const apiTestResponse = await app.request('/api/test'); + expect(await apiTestResponse.json()).toEqual({ code: 0 }); + + process.env.NODE_ENV = originalEnv; + vi.doUnmock('@/utils/directory-import'); + }); + + // https://github.com/DIYgod/RSSHub/pull/18002 + it('prioritizes literal segments over parameter segments in route matching', async () => { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + const directoryImportMock = perDirectoryMock({ + specificity: { + '/param-route.ts': { + route: { + path: '/:category', + name: 'ParamRoute', + handler: () => ({ title: 'param', link: 'https://example.com', item: [], allowEmpty: true }), + }, + }, + '/literal-route.ts': { + route: { + path: '/news/:channel', + name: 'LiteralRoute', + handler: () => ({ title: 'literal', link: 'https://example.com', item: [], allowEmpty: true }), + }, + }, + '/news-route.ts': { + route: { + path: '/news', + name: 'NewsRoute', + handler: () => ({ title: 'news', link: 'https://example.com', item: [], allowEmpty: true }), + }, + }, + }, + }); + vi.doMock('@/utils/directory-import', () => ({ + directoryImport: directoryImportMock, + })); + vi.resetModules(); + const { default: registry } = await import('@/registry'); + + const app = wrap(registry); + + // /news/sports should match /news/:channel (literal "news" wins over :category) + const literalResponse = await app.request('/specificity/news/sports'); + expect((await literalResponse.json()).title).toBe('literal'); + + // /news should match /news (literal wins over :category) + const newsResponse = await app.request('/specificity/news'); + expect((await newsResponse.json()).title).toBe('news'); + + // /products should match /:category + const paramResponse = await app.request('/specificity/products'); + expect((await paramResponse.json()).title).toBe('param'); + + process.env.NODE_ENV = originalEnv; + vi.doUnmock('@/utils/directory-import'); + }); +}); diff --git a/lib/routes.test.ts b/lib/routes.test.ts deleted file mode 100644 index f1da8dc8dd2e..000000000000 --- a/lib/routes.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import Parser from 'rss-parser'; -import { describe, expect, it } from 'vitest'; - -import app from '@/app'; -import { config } from '@/config'; - -const parser = new Parser(); - -process.env.ALLOW_USER_SUPPLY_UNSAFE_DOMAIN = 'true'; - -const routes = { - '/test/:id': '/test/1', -}; -if (process.env.FULL_ROUTES_TEST) { - const { namespaces } = await import('@/registry'); - for (const namespace in namespaces) { - for (const route in namespaces[namespace].routes) { - const requireConfig = namespaces[namespace].routes[route].features?.requireConfig; - let configs; - if (typeof requireConfig !== 'boolean') { - configs = requireConfig - ?.filter((config) => !config.optional) - .map((config) => config.name) - .filter((name) => name !== 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN'); - } - if (namespaces[namespace].routes[route].example && !configs?.length) { - routes[`/${namespace}${route}`] = namespaces[namespace].routes[route].example; - } - } - } -} - -async function checkRSS(response) { - const checkDate = (date) => { - expect(date).toEqual(expect.any(String)); - expect(Date.parse(date)).toEqual(expect.any(Number)); - expect(Date.now() - +new Date(date)).toBeGreaterThan(-1000 * 60 * 60 * 24 * 5); - expect(Date.now() - +new Date(date)).toBeLessThan(1000 * 60 * 60 * 24 * 30 * 12 * 10); - }; - - const parsed = await parser.parseString(await response.text()); - - expect(parsed).toEqual(expect.any(Object)); - expect(parsed.title).toEqual(expect.any(String)); - expect(parsed.title).not.toBe('RSSHub'); - expect(parsed.description).toEqual(expect.any(String)); - expect(parsed.link).toEqual(expect.any(String)); - expect(parsed.lastBuildDate).toEqual(expect.any(String)); - expect(parsed.ttl).toEqual(Math.trunc(config.cache.routeExpire / 60) + ''); - expect(parsed.items).toEqual(expect.any(Array)); - checkDate(parsed.lastBuildDate); - - // check items - const guids: Array = []; - for (const item of parsed.items) { - expect(item).toEqual(expect.any(Object)); - expect(item.title).toEqual(expect.any(String)); - expect(item.link).toEqual(expect.any(String)); - expect(item.content).toEqual(expect.any(String)); - expect(item.guid).toEqual(expect.any(String)); - if (item.pubDate) { - expect(item.pubDate).toEqual(expect.any(String)); - checkDate(item.pubDate); - } - - // guid must be unique - expect(guids).not.toContain(item.guid); - guids.push(item.guid); - } -} - -describe('routes', () => { - for (const route in routes) { - it.concurrent( - route, - { - timeout: 60000, - }, - async () => { - const response = await app.request(routes[route]); - expect(response.status).toBe(200); - await checkRSS(response); - } - ); - } -}); diff --git a/lib/entrypoints.test.ts b/lib/server.test.ts similarity index 53% rename from lib/entrypoints.test.ts rename to lib/server.test.ts index ede7c38ca2f3..188aef750a08 100644 --- a/lib/entrypoints.test.ts +++ b/lib/server.test.ts @@ -1,11 +1,6 @@ import { describe, expect, it } from 'vitest'; -describe('entrypoints', () => { - it('exports app entrypoint', async () => { - const app = (await import('@/app')).default; - expect(typeof app.request).toBe('function'); - }); - +describe('server', () => { it('exports server entrypoint', async () => { const server = (await import('@/server')).default; expect(typeof server.request).toBe('function'); diff --git a/lib/techflowpost.test.ts b/lib/techflowpost.test.ts deleted file mode 100644 index 57edf708964d..000000000000 --- a/lib/techflowpost.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { http, HttpResponse } from 'msw'; -import { describe, expect, it } from 'vitest'; - -import { route as indexRoute } from './routes/techflowpost'; -import { route as expressRoute } from './routes/techflowpost/express'; -import { route as featuredRoute } from './routes/techflowpost/featured'; - -const challengeArg = '7FC0A515A247CA56A8EE791EF40FF1CAEB93AAA6'; -const expectedCookie = 'acw_sc__v2=69fef11a2a690097fa41a9b697a798108d4fb906'; - -const challenge = ``; - -function createCtx({ category, limit = '1' }: { category?: string; limit?: string } = {}) { - return { - req: { - param: (name: string) => (name === 'category' ? category : undefined), - query: (name: string) => (name === 'limit' ? limit : undefined), - }, - }; -} - -describe('/techflowpost', () => { - it('builds the article feed from the current client API after acw challenge', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.post('https://www.techflowpost.com/ashx/index.ashx', () => HttpResponse.json({ message: 'gone' }, { status: 410 })), - http.get('https://www.techflowpost.com/api/client/articles', ({ request }) => { - const url = new URL(request.url); - expect(url.searchParams.get('page')).toBe('1'); - expect(url.searchParams.get('page_size')).toBe('1'); - - if (!request.headers.get('cookie')?.includes(expectedCookie)) { - return HttpResponse.html(challenge); - } - - return HttpResponse.json({ - data: [ - { - id: 31495, - title: 'Article title', - abstract: 'Article abstract', - picture: '/upload/images/article.png', - author: { name: 'TechFlow' }, - category: { name: 'Trading' }, - labels: [{ label: 'AI' }], - created_at: '2026-05-09T06:40:11.209Z', - updated_at: '2026-05-09T08:31:01.272Z', - }, - ], - }); - }), - http.get('https://www.techflowpost.com/api/client/articles/31495', ({ request }) => { - expect(request.headers.get('cookie')).toContain(expectedCookie); - - return HttpResponse.json({ - article: { - content: '

    Full article content

    ', - }, - }); - }) - ); - - const feed = await indexRoute.handler(createCtx()); - expect(feed.item).toHaveLength(1); - expect(feed.item[0].title).toBe('Article title'); - expect(feed.item[0].link).toBe('https://www.techflowpost.com/zh-CN/article/31495'); - expect(feed.item[0].description).toContain('Full article content'); - expect(feed.item[0].category).toEqual(['Trading', 'AI']); - }); - - it('passes category_id for featured article feeds', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.post('https://www.techflowpost.com/ashx/index.ashx', () => HttpResponse.json({ message: 'gone' }, { status: 410 })), - http.get('https://www.techflowpost.com/api/client/articles', ({ request }) => { - const url = new URL(request.url); - expect(url.searchParams.get('category_id')).toBe('2043'); - - if (!request.headers.get('cookie')?.includes(expectedCookie)) { - return HttpResponse.html(challenge); - } - - return HttpResponse.json({ - data: [ - { - id: 31496, - title: 'Featured article', - abstract: 'Featured abstract', - author: { name: 'TechFlow' }, - category: { name: 'Trading' }, - labels: [], - created_at: '2026-05-09T07:27:22.240Z', - updated_at: '2026-05-09T08:32:09.789Z', - }, - ], - }); - }), - http.get('https://www.techflowpost.com/api/client/articles/31496', () => - HttpResponse.json({ - article: { - content: '

    Featured content

    ', - }, - }) - ) - ); - - const feed = await featuredRoute.handler(createCtx({ category: '2043' })); - expect(feed.item[0].title).toBe('Featured article'); - expect(feed.item[0].description).toContain('Featured content'); - }); - - it('builds the express feed from the current newsflash API', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.post('https://www.techflowpost.com/ashx/newflash_index.ashx', () => HttpResponse.json({ message: 'gone' }, { status: 410 })), - http.get('https://www.techflowpost.com/api/client/newsflashes', ({ request }) => { - const url = new URL(request.url); - expect(url.searchParams.get('page')).toBe('1'); - expect(url.searchParams.get('page_size')).toBe('1'); - - if (!request.headers.get('cookie')?.includes(expectedCookie)) { - return HttpResponse.html(challenge); - } - - return HttpResponse.json({ - data: [ - { - id: 122059, - title: 'Newsflash title', - abstract: 'Newsflash abstract', - url: 'https://example.com/source', - created_at: '2026-05-09T08:21:45.537Z', - updated_at: '2026-05-09T08:33:21.834Z', - }, - ], - }); - }), - http.get('https://www.techflowpost.com/api/client/newsflashes/122059', ({ request }) => { - expect(request.headers.get('cookie')).toContain(expectedCookie); - - return HttpResponse.json({ - content: '

    Newsflash content

    ', - }); - }) - ); - - const feed = await expressRoute.handler(createCtx()); - expect(feed.item).toHaveLength(1); - expect(feed.item[0].title).toBe('Newsflash title'); - expect(feed.item[0].link).toBe('https://www.techflowpost.com/zh-CN/newsletter/122059'); - expect(feed.item[0].description).toContain('Newsflash content'); - }); -}); diff --git a/lib/utils/cache.test.ts b/lib/utils/cache/index.test.ts similarity index 100% rename from lib/utils/cache.test.ts rename to lib/utils/cache/index.test.ts diff --git a/lib/utils/cache/index.worker.test.ts b/lib/utils/cache/index.worker.test.ts new file mode 100644 index 000000000000..3e65db09c8f2 --- /dev/null +++ b/lib/utils/cache/index.worker.test.ts @@ -0,0 +1,77 @@ +/// +import { env } from 'cloudflare:test'; +import { describe, expect, it, vi } from 'vitest'; + +import cache, { setKVNamespace } from '@/utils/cache/index.worker'; +import kv from '@/utils/cache/kv'; + +describe('worker cache before KV binding', () => { + it('is unavailable and always calls the value function', async () => { + expect(cache.status.available).toBe(false); + + const getValue = vi.fn().mockResolvedValue({ fresh: 1 }); + const first = await cache.tryGet('worker:fallback', getValue); + const second = await cache.tryGet('worker:fallback', getValue); + + expect(first).toEqual({ fresh: 1 }); + expect(second).toEqual({ fresh: 1 }); + expect(getValue).toHaveBeenCalledTimes(2); + expect(await cache.globalCache.get('worker:fallback')).toBeNull(); + }); +}); + +// The binding is module-global state, so these run after the unavailable tests above +describe('worker cache with KV binding', () => { + it('becomes available once the KV namespace is bound', () => { + setKVNamespace(env.CACHE); + expect(cache.status.available).toBe(true); + }); + + it('round-trips strings and serializes objects', async () => { + await kv.set('worker:string', 'value'); + expect(await kv.get('worker:string')).toBe('value'); + + await kv.set('worker:object', { a: 1 }); + expect(await kv.get('worker:object')).toBe('{"a":1}'); + expect(await kv.has('worker:object')).toBe(true); + expect(await kv.has('worker:missing')).toBe(false); + }); + + it('tryGet caches the computed value and parses JSON on the next hit', async () => { + const getValue = vi.fn().mockResolvedValue({ cached: true }); + + const miss = await cache.tryGet('worker:tryget', getValue); + expect(miss).toEqual({ cached: true }); + + // tryGet writes the cache without awaiting the KV put + await vi.waitFor(async () => { + expect(await kv.has('worker:tryget')).toBe(true); + }); + + const hit = await cache.tryGet('worker:tryget', getValue); + expect(hit).toEqual({ cached: true }); + expect(getValue).toHaveBeenCalledTimes(1); + }); + + it('stores a ttl sidecar key for non-default expiry', async () => { + await kv.set('worker:custom-ttl', 'v', 120); + expect(await env.CACHE.get('rsshub:cacheTtl:worker:custom-ttl')).toBe('120'); + + await kv.set('worker:default-ttl', 'v'); + expect(await env.CACHE.get('rsshub:cacheTtl:worker:default-ttl')).toBeNull(); + }); + + it('rejects keys using the reserved ttl prefix', async () => { + await expect(kv.get('rsshub:cacheTtl:foo')).rejects.toThrow('reserved'); + }); + + it('claims a key only once', async () => { + expect(await cache.globalCache.claim('worker:claim', 60)).toBe(true); + expect(await cache.globalCache.claim('worker:claim', 60)).toBe(false); + }); + + it('exposes get/set through globalCache', async () => { + await cache.globalCache.set('worker:global', { b: 2 }, 60); + expect(await cache.globalCache.get('worker:global')).toBe('{"b":2}'); + }); +}); diff --git a/lib/utils/common-config.charset.test.ts b/lib/utils/common-config.charset.test.ts deleted file mode 100644 index 008141d66cab..000000000000 --- a/lib/utils/common-config.charset.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -const html = `
    -
      -
    • - 1 -
      RSSHub1
      -
      2025-01-01
      -
    • -
    -
    `; - -const rawSpy = vi.fn(() => - Promise.resolve({ - headers: new Headers({ - 'content-type': 'text/html; charset=gbk', - }), - _data: html, - }) -); -const ofetchSpy = vi.fn(() => Promise.resolve(Buffer.from(html))); - -vi.mock('@/utils/ofetch', () => ({ - default: Object.assign(ofetchSpy, { raw: rawSpy }), -})); - -describe('common-config charset', () => { - it('parses charset from content-type', async () => { - const buildData = (await import('@/utils/common-config')).default; - const data = await buildData({ - link: 'http://rsshub.test/buildData', - url: 'http://rsshub.test/buildData', - title: '%title%', - params: { - title: 'buildData', - }, - item: { - item: '.content li', - title: `$('a').text() + ' - %title%'`, - link: `$('a').attr('href')`, - description: `$('.description').html()`, - pubDate: `timezone(parseDate($('.date').text(), 'YYYY-MM-DD'), 0)`, - }, - }); - - expect(data.title).toBe('buildData'); - expect(data.item[0].title).toBe('1 - buildData'); - }); -}); diff --git a/lib/utils/common-config.test.ts b/lib/utils/common-config.test.ts index 8acb8ebd855a..6eee18d5f04e 100644 --- a/lib/utils/common-config.test.ts +++ b/lib/utils/common-config.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import configUtils, { getProp, replaceParams, transElemText } from '@/utils/common-config'; @@ -78,3 +78,59 @@ describe('index', () => { }); }); }); + +describe('charset', () => { + const html = `
    +
      +
    • + 1 +
      RSSHub1
      +
      2025-01-01
      +
    • +
    +
    `; + + const rawSpy = vi.fn(() => + Promise.resolve({ + headers: new Headers({ + 'content-type': 'text/html; charset=gbk', + }), + _data: html, + }) + ); + const ofetchSpy = vi.fn(() => Promise.resolve(Buffer.from(html))); + + beforeAll(() => { + vi.doMock('@/utils/ofetch', () => ({ + default: Object.assign(ofetchSpy, { raw: rawSpy }), + })); + vi.resetModules(); + }); + + afterAll(() => { + vi.doUnmock('@/utils/ofetch'); + vi.resetModules(); + }); + + it('parses charset from content-type', async () => { + const buildData = (await import('@/utils/common-config')).default; + const data = await buildData({ + link: 'http://rsshub.test/buildData', + url: 'http://rsshub.test/buildData', + title: '%title%', + params: { + title: 'buildData', + }, + item: { + item: '.content li', + title: `$('a').text() + ' - %title%'`, + link: `$('a').attr('href')`, + description: `$('.description').html()`, + pubDate: `timezone(parseDate($('.date').text(), 'YYYY-MM-DD'), 0)`, + }, + }); + + expect(data.title).toBe('buildData'); + expect(data.item[0].title).toBe('1 - buildData'); + }); +}); diff --git a/lib/utils/header-generator.mock.test.ts b/lib/utils/header-generator.mock.test.ts deleted file mode 100644 index ec554191c08e..000000000000 --- a/lib/utils/header-generator.mock.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; - -afterEach(() => { - vi.resetModules(); - vi.clearAllMocks(); - vi.unmock('header-generator'); -}); - -describe('header-generator (mocked)', () => { - it('retries invalid safari user agents', async () => { - const headersQueue = [{ 'user-agent': 'Mozilla/5.0 Applebot Safari' }, { 'user-agent': 'Mozilla/5.0 Safari' }]; - - vi.doMock('header-generator', () => ({ - HeaderGenerator: class { - getHeaders() { - return headersQueue.shift() ?? { 'user-agent': 'Mozilla/5.0 Safari' }; - } - }, - PRESETS: { - MODERN_MACOS_CHROME: { mock: true }, - }, - })); - - const { generateHeaders } = await import('@/utils/header-generator'); - const headers = generateHeaders({ preset: 'safari' } as any); - - expect(headers['user-agent']).toContain('Safari'); - expect(headersQueue.length).toBe(0); - }); - - it('accepts firefox user agents', async () => { - const headersQueue = [{ 'user-agent': 'Mozilla/5.0 Firefox' }]; - - vi.doMock('header-generator', () => ({ - HeaderGenerator: class { - getHeaders() { - return headersQueue.shift() ?? { 'user-agent': 'Mozilla/5.0 Firefox' }; - } - }, - PRESETS: { - MODERN_MACOS_CHROME: { mock: true }, - }, - })); - - const { generateHeaders } = await import('@/utils/header-generator'); - const headers = generateHeaders(); - - expect(headers['user-agent']).toContain('Firefox'); - }); -}); diff --git a/lib/utils/header-generator.test.ts b/lib/utils/header-generator.test.ts index 035b005cca03..044d7105c7e5 100644 --- a/lib/utils/header-generator.test.ts +++ b/lib/utils/header-generator.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { generateHeaders, PRESETS } from '@/utils/header-generator'; import ofetch from '@/utils/ofetch'; @@ -62,3 +62,54 @@ describe('header-generator', () => { expect(headers['user-agent']).toMatch(/Chrome/); }); }); + +describe('header-generator (mocked)', () => { + afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + vi.unmock('header-generator'); + }); + + it('retries invalid safari user agents', async () => { + const headersQueue = [{ 'user-agent': 'Mozilla/5.0 Applebot Safari' }, { 'user-agent': 'Mozilla/5.0 Safari' }]; + + vi.doMock('header-generator', () => ({ + HeaderGenerator: class { + getHeaders() { + return headersQueue.shift() ?? { 'user-agent': 'Mozilla/5.0 Safari' }; + } + }, + PRESETS: { + MODERN_MACOS_CHROME: { mock: true }, + }, + })); + + vi.resetModules(); + const { generateHeaders: generateMockedHeaders } = await import('@/utils/header-generator'); + const headers = generateMockedHeaders({ preset: 'safari' } as any); + + expect(headers['user-agent']).toContain('Safari'); + expect(headersQueue.length).toBe(0); + }); + + it('accepts firefox user agents', async () => { + const headersQueue = [{ 'user-agent': 'Mozilla/5.0 Firefox' }]; + + vi.doMock('header-generator', () => ({ + HeaderGenerator: class { + getHeaders() { + return headersQueue.shift() ?? { 'user-agent': 'Mozilla/5.0 Firefox' }; + } + }, + PRESETS: { + MODERN_MACOS_CHROME: { mock: true }, + }, + })); + + vi.resetModules(); + const { generateHeaders: generateMockedHeaders } = await import('@/utils/header-generator'); + const headers = generateMockedHeaders(); + + expect(headers['user-agent']).toContain('Firefox'); + }); +}); diff --git a/lib/utils/proxy/pac-proxy-error.test.ts b/lib/utils/proxy/pac-proxy-error.test.ts deleted file mode 100644 index 51224fe0a7b6..000000000000 --- a/lib/utils/proxy/pac-proxy-error.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -const errorSpy = vi.fn(); -const warnSpy = vi.fn(); -const infoSpy = vi.fn(); - -vi.mock('@/utils/logger', () => ({ - default: { - error: errorSpy, - warn: warnSpy, - info: infoSpy, - }, -})); - -describe('pac-proxy', () => { - it('logs error when PAC_SCRIPT is not a string', async () => { - const pacProxy = (await import('@/utils/proxy/pac-proxy')).default; - pacProxy(undefined, { invalid: true } as any, {}); - - expect(errorSpy).toHaveBeenCalledWith('Invalid PAC_SCRIPT, use PAC_URI instead'); - }); -}); diff --git a/lib/utils/proxy/pac-proxy.test.ts b/lib/utils/proxy/pac-proxy.test.ts index 97a9b52417ae..2e5c8d417630 100644 --- a/lib/utils/proxy/pac-proxy.test.ts +++ b/lib/utils/proxy/pac-proxy.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import pacProxy from '@/utils/proxy/pac-proxy'; @@ -79,3 +79,30 @@ describe('pac-proxy', () => { effectiveExpect(pacProxy(httpsAuthUri, '', httpsAuthObj), httpsAuthUri, httpsObj); }); }); + +describe('pac-proxy error handling', () => { + const errorSpy = vi.fn(); + + beforeAll(() => { + vi.doMock('@/utils/logger', () => ({ + default: { + error: errorSpy, + warn: vi.fn(), + info: vi.fn(), + }, + })); + vi.resetModules(); + }); + + afterAll(() => { + vi.doUnmock('@/utils/logger'); + vi.resetModules(); + }); + + it('logs error when PAC_SCRIPT is not a string', async () => { + const freshPacProxy = (await import('@/utils/proxy/pac-proxy')).default; + freshPacProxy(undefined, { invalid: true } as any, {}); + + expect(errorSpy).toHaveBeenCalledWith('Invalid PAC_SCRIPT, use PAC_URI instead'); + }); +}); diff --git a/lib/utils/render.test.ts b/lib/utils/render.test.ts new file mode 100644 index 000000000000..cdb36c869a4c --- /dev/null +++ b/lib/utils/render.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; + +import { Atom as RenderAtom, json as renderJson, RSS as RenderRSS, rss3 as renderRss3 } from '@/utils/render'; +import Atom from '@/views/atom'; +import jsonView from '@/views/json'; +import RSS from '@/views/rss'; +import rss3View from '@/views/rss3'; + +describe('view exports', () => { + it('re-exports view helpers from render', () => { + expect(RenderAtom).toBe(Atom); + expect(RenderRSS).toBe(RSS); + expect(renderJson).toBe(jsonView); + expect(renderRss3).toBe(rss3View); + }); +}); diff --git a/lib/utils/request-rewriter/fetch-retry.test.ts b/lib/utils/request-rewriter/fetch-retry.test.ts deleted file mode 100644 index fcd8c382a695..000000000000 --- a/lib/utils/request-rewriter/fetch-retry.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import undici from 'undici'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -const buildProxyState = () => [ - { - uri: 'http://proxy1.test', - isActive: true, - failureCount: 0, - urlHandler: new URL('http://proxy1.test'), - }, - { - uri: 'http://proxy2.test', - isActive: true, - failureCount: 0, - urlHandler: new URL('http://proxy2.test'), - }, -]; - -const loadWrappedFetch = async (proxyMock: any) => { - vi.resetModules(); - vi.doMock('@/utils/logger', () => ({ - default: { - debug: vi.fn(), - warn: vi.fn(), - info: vi.fn(), - error: vi.fn(), - http: vi.fn(), - }, - })); - vi.doMock('@/utils/proxy', () => ({ - default: proxyMock, - })); - - return (await import('@/utils/request-rewriter/fetch')).default; -}; - -afterEach(() => { - vi.restoreAllMocks(); - vi.resetModules(); - vi.unmock('@/utils/logger'); - vi.unmock('@/utils/proxy'); -}); - -describe('request-rewriter fetch retry', () => { - it('retries with the next proxy when prefer-proxy header is set', async () => { - const proxies = buildProxyState(); - let index = 0; - const proxyMock = { - proxyObj: { - strategy: 'on_retry', - url_regex: 'example.com', - }, - proxyUrlHandler: null, - multiProxy: { - allProxies: proxies, - }, - getCurrentProxy: vi.fn(() => proxies[index]), - markProxyFailed: vi.fn(() => { - index = 1; - }), - getDispatcherForProxy: vi.fn((proxyState) => ({ - proxy: proxyState.uri, - })), - }; - - const wrappedFetch = await loadWrappedFetch(proxyMock); - const fetchSpy = vi.spyOn(undici, 'fetch'); - fetchSpy.mockRejectedValueOnce(new Error('boom')); - fetchSpy.mockResolvedValueOnce(new Response('ok')); - - const response = await wrappedFetch('http://example.com/resource', { - headers: new Headers({ - 'x-prefer-proxy': '1', - }), - }); - - expect(response).toBeInstanceOf(Response); - expect(fetchSpy).toHaveBeenCalledTimes(2); - expect(proxyMock.markProxyFailed).toHaveBeenCalledWith('http://proxy1.test'); - expect(proxyMock.getDispatcherForProxy).toHaveBeenCalledWith(proxies[1]); - - const requestArg = fetchSpy.mock.calls[0][0] as Request; - expect(requestArg.headers.get('x-prefer-proxy')).toBeNull(); - }); - - it('drops dispatcher when no next proxy is available', async () => { - const proxies = buildProxyState(); - const proxyMock = { - proxyObj: { - strategy: 'on_retry', - url_regex: 'example.com', - }, - proxyUrlHandler: null, - multiProxy: { - allProxies: proxies, - }, - getCurrentProxy: vi.fn(() => proxies[0]), - markProxyFailed: vi.fn(), - getDispatcherForProxy: vi.fn((proxyState) => ({ - proxy: proxyState.uri, - })), - }; - - const wrappedFetch = await loadWrappedFetch(proxyMock); - const fetchSpy = vi.spyOn(undici, 'fetch'); - fetchSpy.mockRejectedValueOnce(new Error('boom')); - fetchSpy.mockResolvedValueOnce(new Response('ok')); - - await wrappedFetch('http://example.com/resource', { - headers: { - 'x-prefer-proxy': '1', - }, - }); - - expect(fetchSpy).toHaveBeenCalledTimes(2); - expect(fetchSpy.mock.calls[1][1]?.dispatcher).toBeUndefined(); - }); -}); diff --git a/lib/utils/request-rewriter/fetch.test.ts b/lib/utils/request-rewriter/fetch.test.ts index 7108e80d03c4..d0dae90617a1 100644 --- a/lib/utils/request-rewriter/fetch.test.ts +++ b/lib/utils/request-rewriter/fetch.test.ts @@ -105,3 +105,119 @@ describe('wrappedFetch', () => { fetchSpy.mockRestore(); }); }); + +describe('request-rewriter fetch retry', () => { + const buildProxyState = () => [ + { + uri: 'http://proxy1.test', + isActive: true, + failureCount: 0, + urlHandler: new URL('http://proxy1.test'), + }, + { + uri: 'http://proxy2.test', + isActive: true, + failureCount: 0, + urlHandler: new URL('http://proxy2.test'), + }, + ]; + + const loadWrappedFetch = async (proxyMock: any) => { + vi.resetModules(); + vi.doMock('@/utils/logger', () => ({ + default: { + debug: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + error: vi.fn(), + http: vi.fn(), + }, + })); + vi.doMock('@/utils/proxy', () => ({ + default: proxyMock, + })); + + return (await import('@/utils/request-rewriter/fetch')).default; + }; + + afterEach(() => { + vi.restoreAllMocks(); + vi.resetModules(); + vi.unmock('@/utils/logger'); + vi.unmock('@/utils/proxy'); + }); + + test('retries with the next proxy when prefer-proxy header is set', async () => { + const proxies = buildProxyState(); + let index = 0; + const proxyMock = { + proxyObj: { + strategy: 'on_retry', + url_regex: 'example.com', + }, + proxyUrlHandler: null, + multiProxy: { + allProxies: proxies, + }, + getCurrentProxy: vi.fn(() => proxies[index]), + markProxyFailed: vi.fn(() => { + index = 1; + }), + getDispatcherForProxy: vi.fn((proxyState) => ({ + proxy: proxyState.uri, + })), + }; + + const wrappedFetch = await loadWrappedFetch(proxyMock); + const fetchSpy = vi.spyOn(undici, 'fetch'); + fetchSpy.mockRejectedValueOnce(new Error('boom')); + fetchSpy.mockResolvedValueOnce(new Response('ok')); + + const response = await wrappedFetch('http://example.com/resource', { + headers: new Headers({ + 'x-prefer-proxy': '1', + }), + }); + + expect(response).toBeInstanceOf(Response); + expect(fetchSpy).toHaveBeenCalledTimes(2); + expect(proxyMock.markProxyFailed).toHaveBeenCalledWith('http://proxy1.test'); + expect(proxyMock.getDispatcherForProxy).toHaveBeenCalledWith(proxies[1]); + + const requestArg = fetchSpy.mock.calls[0][0] as Request; + expect(requestArg.headers.get('x-prefer-proxy')).toBeNull(); + }); + + test('drops dispatcher when no next proxy is available', async () => { + const proxies = buildProxyState(); + const proxyMock = { + proxyObj: { + strategy: 'on_retry', + url_regex: 'example.com', + }, + proxyUrlHandler: null, + multiProxy: { + allProxies: proxies, + }, + getCurrentProxy: vi.fn(() => proxies[0]), + markProxyFailed: vi.fn(), + getDispatcherForProxy: vi.fn((proxyState) => ({ + proxy: proxyState.uri, + })), + }; + + const wrappedFetch = await loadWrappedFetch(proxyMock); + const fetchSpy = vi.spyOn(undici, 'fetch'); + fetchSpy.mockRejectedValueOnce(new Error('boom')); + fetchSpy.mockResolvedValueOnce(new Response('ok')); + + await wrappedFetch('http://example.com/resource', { + headers: { + 'x-prefer-proxy': '1', + }, + }); + + expect(fetchSpy).toHaveBeenCalledTimes(2); + expect(fetchSpy.mock.calls[1][1]?.dispatcher).toBeUndefined(); + }); +}); diff --git a/lib/utils/request-rewriter/fetch.worker.test.ts b/lib/utils/request-rewriter/fetch.worker.test.ts new file mode 100644 index 000000000000..33f104fcec4f --- /dev/null +++ b/lib/utils/request-rewriter/fetch.worker.test.ts @@ -0,0 +1,72 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +const fetchMock = vi.fn(() => Promise.resolve(new Response('ok'))); + +// The module captures the global fetch at import time, so stub before importing +const loadWrappedFetch = async () => { + vi.stubGlobal('fetch', fetchMock); + vi.resetModules(); + return (await import('@/utils/request-rewriter/fetch.worker')).default; +}; + +const lastRequest = () => fetchMock.mock.lastCall?.[0] as unknown as Request; + +afterEach(() => { + vi.unstubAllGlobals(); + vi.clearAllMocks(); +}); + +describe('worker fetch wrapper', () => { + it('applies a browser fingerprint when no user-agent is set', async () => { + const wrappedFetch = await loadWrappedFetch(); + await wrappedFetch('https://example.com/page'); + + const request = lastRequest(); + expect(request.headers.get('user-agent')).toContain('Chrome'); + expect(request.headers.get('sec-ch-ua-platform')).toBe('"macOS"'); + expect(request.headers.get('accept-language')).toBe('en-US,en;q=0.9'); + }); + + it('keeps a caller-provided user-agent and headers', async () => { + const wrappedFetch = await loadWrappedFetch(); + await wrappedFetch('https://example.com/page', { + headers: { + 'user-agent': 'custom-ua', + 'sec-fetch-site': 'same-origin', + }, + }); + + const request = lastRequest(); + expect(request.headers.get('user-agent')).toBe('custom-ua'); + expect(request.headers.get('sec-fetch-site')).toBe('same-origin'); + }); + + it('sets the referer to the request origin when absent', async () => { + const wrappedFetch = await loadWrappedFetch(); + await wrappedFetch('https://example.com/deep/page?q=1'); + + expect(lastRequest().headers.get('referer')).toBe('https://example.com'); + }); + + it('keeps a caller-provided referer', async () => { + const wrappedFetch = await loadWrappedFetch(); + await wrappedFetch('https://example.com/page', { + headers: { + referer: 'https://referrer.test/', + }, + }); + + expect(lastRequest().headers.get('referer')).toBe('https://referrer.test/'); + }); + + it('strips the x-prefer-proxy header', async () => { + const wrappedFetch = await loadWrappedFetch(); + await wrappedFetch('https://example.com/page', { + headers: { + 'x-prefer-proxy': '1', + }, + }); + + expect(lastRequest().headers.has('x-prefer-proxy')).toBe(false); + }); +}); diff --git a/lib/utils/request-rewriter.test.ts b/lib/utils/request-rewriter/index.test.ts similarity index 100% rename from lib/utils/request-rewriter.test.ts rename to lib/utils/request-rewriter/index.test.ts diff --git a/lib/utils/request-rewriter/index.worker.test.ts b/lib/utils/request-rewriter/index.worker.test.ts new file mode 100644 index 000000000000..d16b57a5a092 --- /dev/null +++ b/lib/utils/request-rewriter/index.worker.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from 'vitest'; + +describe('worker request-rewriter', () => { + it('replaces globalThis.fetch with the wrapped fetch', async () => { + const { default: wrappedFetch } = await import('@/utils/request-rewriter/fetch.worker'); + await import('@/utils/request-rewriter/index.worker'); + + expect(fetch).toBe(wrappedFetch); + }); +}); diff --git a/lib/views/atom.test.tsx b/lib/views/atom.test.tsx new file mode 100644 index 000000000000..8010a8cec630 --- /dev/null +++ b/lib/views/atom.test.tsx @@ -0,0 +1,57 @@ +import { renderToString } from 'hono/jsx/dom/server'; +import { describe, expect, it } from 'vitest'; + +import Atom from '@/views/atom'; + +describe('Atom view', () => { + it('renders optional fields and media extensions', () => { + const html = renderToString( + + ); + + expect(html).toContain('https://example.com/icon.png'); + expect(html).toContain('https://example.com/logo.png'); + expect(html).toContain('media:content'); + expect(html).toContain('term="News"'); + expect(html).toContain('term="Tech"'); + expect(html).toContain('rsshub:upvotes'); + expect(html).toContain('rsshub:downvotes'); + expect(html).toContain('rsshub:comments'); + }); +}); diff --git a/lib/views/index.test.tsx b/lib/views/index.test.tsx new file mode 100644 index 000000000000..4951760bb488 --- /dev/null +++ b/lib/views/index.test.tsx @@ -0,0 +1,74 @@ +import { renderToString } from 'hono/jsx/dom/server'; +import { afterEach, describe, expect, it, vi } from 'vitest'; + +afterEach(() => { + vi.resetModules(); + vi.unmock('@/config'); + vi.unmock('@/utils/debug-info'); + vi.unmock('@/utils/git-hash'); +}); + +describe('Index view', () => { + const renderIndex = async (debugInfo: string | undefined, debugQuery: string | undefined) => { + const debugData = { + hitCache: 2, + request: 10, + etag: 3, + error: 1, + routes: { + '/foo': 5, + '/bar': 2, + }, + paths: { + '/foo?x=1': 4, + '/bar?x=2': 1, + }, + errorRoutes: { + '/error': 2, + '/fail': 1, + }, + errorPaths: { + '/error?x=1': 1, + '/fail?x=2': 1, + }, + }; + + vi.doMock('@/config', () => ({ + config: { + debugInfo, + disallowRobot: true, + nodeName: 'TestNode', + cache: { + routeExpire: 120, + }, + }, + })); + vi.doMock('@/utils/debug-info', () => ({ + getDebugInfo: () => debugData, + })); + vi.doMock('@/utils/git-hash', () => ({ + gitHash: 'abc123', + gitDate: new Date('2020-01-01T00:00:00Z'), + })); + + const { default: Index } = await import('@/views/index'); + + return renderToString(); + }; + + it('shows debug info when enabled', async () => { + const html = await renderIndex('secret', 'secret'); + + expect(html).toContain('Debug Info'); + expect(html).toContain('TestNode'); + expect(html).toContain('abc123'); + expect(html).toContain('5 /foo'); + expect(html).toContain('2 /error'); + }); + + it('hides debug info when disabled', async () => { + const html = await renderIndex('false', 'secret'); + + expect(html).not.toContain('Debug Info'); + }); +}); diff --git a/lib/views/json.test.ts b/lib/views/json.test.ts new file mode 100644 index 000000000000..f18046ca44bc --- /dev/null +++ b/lib/views/json.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from 'vitest'; + +import jsonView from '@/views/json'; + +describe('JSON view', () => { + it('renders summary, authors, tags, attachments, and extras', () => { + const jsonOutput = jsonView({ + title: 'JSON Feed', + link: 'https://example.com', + feedLink: 'https://example.com/feed.json', + description: 'JSON Description', + language: 'en', + author: 'Feed Author', + image: 'https://example.com/icon.png', + item: [ + { + title: 'Item One', + link: 'https://example.com/one', + description: 'Entry One', + guid: 'guid-1', + content: { + html: '

    hello

    ', + text: 'hello', + }, + image: 'https://example.com/image.jpg', + banner: 'https://example.com/banner.jpg', + pubDate: '2024-01-01T00:00:00Z', + updated: '2024-01-02T00:00:00Z', + author: 'Item Author', + category: ['Tech', 'AI'], + enclosure_url: 'https://example.com/audio.mp3', + enclosure_type: 'audio/mpeg', + enclosure_title: 'Audio Title', + enclosure_length: 123, + itunes_duration: 321, + _extra: { foo: 'bar' }, + }, + ], + }); + + const feed = JSON.parse(jsonOutput); + + expect(feed.version).toBe('https://jsonfeed.org/version/1.1'); + expect(feed.title).toBe('JSON Feed'); + expect(feed.home_page_url).toBe('https://example.com'); + expect(feed.feed_url).toBe('https://example.com/feed.json'); + expect(feed.description).toBe('JSON Description - Powered by RSSHub'); + expect(feed.authors).toEqual([{ name: 'Feed Author' }]); + expect(feed.items).toHaveLength(1); + expect(feed.items[0]).toMatchObject({ + id: 'guid-1', + url: 'https://example.com/one', + title: 'Item One', + content_html: '

    hello

    ', + content_text: 'hello', + summary: 'Entry One', + image: 'https://example.com/image.jpg', + banner_image: 'https://example.com/banner.jpg', + date_published: '2024-01-01T00:00:00Z', + date_modified: '2024-01-02T00:00:00Z', + authors: [{ name: 'Item Author' }], + tags: ['Tech', 'AI'], + attachments: [ + { + url: 'https://example.com/audio.mp3', + mime_type: 'audio/mpeg', + title: 'Audio Title', + size_in_bytes: 123, + duration_in_seconds: 321, + }, + ], + }); + expect(feed.items[0]._extra).toEqual({ foo: 'bar' }); + }); +}); diff --git a/lib/views/rss.test.tsx b/lib/views/rss.test.tsx new file mode 100644 index 000000000000..479082a48143 --- /dev/null +++ b/lib/views/rss.test.tsx @@ -0,0 +1,67 @@ +import { renderToString } from 'hono/jsx/dom/server'; +import { describe, expect, it } from 'vitest'; + +import RSS from '@/views/rss'; + +describe('RSS view', () => { + it('renders itunes, media, and telegram image variants', () => { + const html = renderToString( + + ); + + expect(html).toContain('xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"'); + expect(html).toContain('xmlns:media="http://search.yahoo.com/mrss/"'); + expect(html).toContain('Podcast Author'); + expect(html).toContain('itunes:category text="Tech"'); + expect(html).toContain('true'); + expect(html).toContain('31'); + expect(html).toContain('88'); + expect(html).toContain('media:content'); + expect(html).toContain('Podcast'); + expect(html).toContain('News'); + }); +}); diff --git a/lib/views/views.test.tsx b/lib/views/views.test.tsx deleted file mode 100644 index 911d807d812d..000000000000 --- a/lib/views/views.test.tsx +++ /dev/null @@ -1,277 +0,0 @@ -import { renderToString } from 'hono/jsx/dom/server'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -import { Atom as RenderAtom, json as renderJson, RSS as RenderRSS, rss3 as renderRss3 } from '@/utils/render'; -import Atom from '@/views/atom'; -import jsonView from '@/views/json'; -import RSS from '@/views/rss'; -import rss3View from '@/views/rss3'; - -afterEach(() => { - vi.resetModules(); - vi.unmock('@/config'); - vi.unmock('@/utils/debug-info'); - vi.unmock('@/utils/git-hash'); -}); - -describe('view exports', () => { - it('re-exports view helpers from render', () => { - expect(RenderAtom).toBe(Atom); - expect(RenderRSS).toBe(RSS); - expect(renderJson).toBe(jsonView); - expect(renderRss3).toBe(rss3View); - }); -}); - -describe('Atom view', () => { - it('renders optional fields and media extensions', () => { - const html = renderToString( - - ); - - expect(html).toContain('https://example.com/icon.png'); - expect(html).toContain('https://example.com/logo.png'); - expect(html).toContain('media:content'); - expect(html).toContain('term="News"'); - expect(html).toContain('term="Tech"'); - expect(html).toContain('rsshub:upvotes'); - expect(html).toContain('rsshub:downvotes'); - expect(html).toContain('rsshub:comments'); - }); -}); - -describe('RSS view', () => { - it('renders itunes, media, and telegram image variants', () => { - const html = renderToString( - - ); - - expect(html).toContain('xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"'); - expect(html).toContain('xmlns:media="http://search.yahoo.com/mrss/"'); - expect(html).toContain('Podcast Author'); - expect(html).toContain('itunes:category text="Tech"'); - expect(html).toContain('true'); - expect(html).toContain('31'); - expect(html).toContain('88'); - expect(html).toContain('media:content'); - expect(html).toContain('Podcast'); - expect(html).toContain('News'); - }); -}); - -describe('JSON view', () => { - it('renders summary, authors, tags, attachments, and extras', () => { - const jsonOutput = jsonView({ - title: 'JSON Feed', - link: 'https://example.com', - feedLink: 'https://example.com/feed.json', - description: 'JSON Description', - language: 'en', - author: 'Feed Author', - image: 'https://example.com/icon.png', - item: [ - { - title: 'Item One', - link: 'https://example.com/one', - description: 'Entry One', - guid: 'guid-1', - content: { - html: '

    hello

    ', - text: 'hello', - }, - image: 'https://example.com/image.jpg', - banner: 'https://example.com/banner.jpg', - pubDate: '2024-01-01T00:00:00Z', - updated: '2024-01-02T00:00:00Z', - author: 'Item Author', - category: ['Tech', 'AI'], - enclosure_url: 'https://example.com/audio.mp3', - enclosure_type: 'audio/mpeg', - enclosure_title: 'Audio Title', - enclosure_length: 123, - itunes_duration: 321, - _extra: { foo: 'bar' }, - }, - ], - }); - - const feed = JSON.parse(jsonOutput); - - expect(feed.version).toBe('https://jsonfeed.org/version/1.1'); - expect(feed.title).toBe('JSON Feed'); - expect(feed.home_page_url).toBe('https://example.com'); - expect(feed.feed_url).toBe('https://example.com/feed.json'); - expect(feed.description).toBe('JSON Description - Powered by RSSHub'); - expect(feed.authors).toEqual([{ name: 'Feed Author' }]); - expect(feed.items).toHaveLength(1); - expect(feed.items[0]).toMatchObject({ - id: 'guid-1', - url: 'https://example.com/one', - title: 'Item One', - content_html: '

    hello

    ', - content_text: 'hello', - summary: 'Entry One', - image: 'https://example.com/image.jpg', - banner_image: 'https://example.com/banner.jpg', - date_published: '2024-01-01T00:00:00Z', - date_modified: '2024-01-02T00:00:00Z', - authors: [{ name: 'Item Author' }], - tags: ['Tech', 'AI'], - attachments: [ - { - url: 'https://example.com/audio.mp3', - mime_type: 'audio/mpeg', - title: 'Audio Title', - size_in_bytes: 123, - duration_in_seconds: 321, - }, - ], - }); - expect(feed.items[0]._extra).toEqual({ foo: 'bar' }); - }); -}); - -describe('Index view', () => { - const renderIndex = async (debugInfo: string | undefined, debugQuery: string | undefined) => { - const debugData = { - hitCache: 2, - request: 10, - etag: 3, - error: 1, - routes: { - '/foo': 5, - '/bar': 2, - }, - paths: { - '/foo?x=1': 4, - '/bar?x=2': 1, - }, - errorRoutes: { - '/error': 2, - '/fail': 1, - }, - errorPaths: { - '/error?x=1': 1, - '/fail?x=2': 1, - }, - }; - - vi.doMock('@/config', () => ({ - config: { - debugInfo, - disallowRobot: true, - nodeName: 'TestNode', - cache: { - routeExpire: 120, - }, - }, - })); - vi.doMock('@/utils/debug-info', () => ({ - getDebugInfo: () => debugData, - })); - vi.doMock('@/utils/git-hash', () => ({ - gitHash: 'abc123', - gitDate: new Date('2020-01-01T00:00:00Z'), - })); - - const { default: Index } = await import('@/views/index'); - - return renderToString(); - }; - - it('shows debug info when enabled', async () => { - const html = await renderIndex('secret', 'secret'); - - expect(html).toContain('Debug Info'); - expect(html).toContain('TestNode'); - expect(html).toContain('abc123'); - expect(html).toContain('5 /foo'); - expect(html).toContain('2 /error'); - }); - - it('hides debug info when disabled', async () => { - const html = await renderIndex('false', 'secret'); - - expect(html).not.toContain('Debug Info'); - }); -}); diff --git a/lib/weibo-utils.test.ts b/lib/weibo-utils.test.ts deleted file mode 100644 index 1d4160286735..000000000000 --- a/lib/weibo-utils.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; - -const createDeferred = () => { - const { promise, reject, resolve } = Promise.withResolvers(); - - return { promise, reject, resolve }; -}; - -const loadWeiboUtils = async ({ pageReady = Promise.resolve() } = {}) => { - vi.resetModules(); - - const store = new Map(); - const cache = { - get: vi.fn((key: string) => store.get(key) || null), - has: vi.fn((key: string) => store.has(key)), - set: vi.fn((key: string, value?: string | Record) => { - store.set(key, typeof value === 'object' ? JSON.stringify(value) : value || ''); - }), - tryGet: vi.fn(async (key: string, getValue: () => Promise) => { - const cached = store.get(key); - if (cached) { - return cached; - } - const value = await getValue(); - store.set(key, JSON.stringify(value)); - return value; - }), - }; - const destroy = vi.fn(); - const page = { - route: vi.fn((_pattern: string, handler: (route: any) => void) => { - for (const requestUrl of ['https://m.weibo.cn/', 'https://m.weibo.cn/']) { - handler({ - abort: vi.fn(), - continue: vi.fn(), - request: () => ({ - resourceType: () => 'document', - url: () => requestUrl, - }), - }); - } - }), - setExtraHTTPHeaders: vi.fn(), - url: vi.fn(() => 'https://m.weibo.cn/'), - }; - const getPlaywrightPage = vi.fn(async (_url: string, options: any) => { - await pageReady; - await options.onBeforeLoad(page); - return { destroy, page }; - }); - const getCookies = vi.fn(() => Promise.resolve('SUB=mock')); - - vi.doMock('@/config', () => ({ - config: { - cache: { - contentExpire: 3600, - routeExpire: 300, - }, - weibo: {}, - }, - })); - vi.doMock('@/utils/cache', () => ({ default: cache })); - vi.doMock('@/utils/logger', () => ({ - default: { - info: vi.fn(), - warn: vi.fn(), - }, - })); - vi.doMock('@/utils/playwright', () => ({ getPlaywrightPage })); - vi.doMock('@/utils/playwright-utils', () => ({ getCookies })); - - const { default: weiboUtils } = await import('./routes/weibo/utils'); - - return { - cache, - getCookies, - getPlaywrightPage, - weiboUtils, - }; -}; - -afterEach(() => { - vi.useRealTimers(); - vi.restoreAllMocks(); - vi.resetModules(); -}); - -describe('weibo utils', () => { - it('shares an in-flight visitor cookie fetch across concurrent callers', async () => { - vi.useFakeTimers(); - const pageReady = createDeferred(); - const { getPlaywrightPage, weiboUtils } = await loadWeiboUtils({ pageReady: pageReady.promise }); - - const first = weiboUtils.getCookies(); - const second = weiboUtils.getCookies(); - pageReady.resolve(); - - await expect(Promise.all([first, second])).resolves.toEqual(['SUB=mock', 'SUB=mock']); - expect(getPlaywrightPage).toHaveBeenCalledTimes(1); - }); -}); diff --git a/lib/worker.worker.test.ts b/lib/worker.test.ts similarity index 100% rename from lib/worker.worker.test.ts rename to lib/worker.test.ts diff --git a/package.json b/package.json index 6df46c3011a7..6b4370fa0f7b 100644 --- a/package.json +++ b/package.json @@ -50,12 +50,13 @@ "vercel-build": "npm run build:routes && tsdown --config ./tsdown-vercel.config.ts", "vitest": "cross-env NODE_ENV=test vitest", "vitest:coverage": "cross-env NODE_ENV=test vitest --coverage.enabled --reporter=junit", - "vitest:fullroutes": "cross-env NODE_ENV=test FULL_ROUTES_TEST=true vitest --reporter=json --reporter=default --outputFile=\"./assets/build/test-full-routes.json\" routes", + "vitest:fullroutes": "cross-env NODE_ENV=test FULL_ROUTES_TEST=true vitest --reporter=json --reporter=default --outputFile=\"./assets/build/test-full-routes.json\" lib/app.test.ts", "vitest:watch": "cross-env NODE_ENV=test vitest --watch", + "vitest:workerd": "cross-env NODE_ENV=test vitest run --config vitest.workers.config.ts", "worker-build": "npm run build:routes:worker && tsdown --config ./tsdown-worker.config.ts", "worker-deploy": "npm run worker-build && wrangler deploy", "worker-dev": "npm run worker-build && wrangler dev", - "worker-test": "npm run worker-build && vitest run lib/worker.worker.test.ts" + "worker-test": "npm run worker-build && vitest run lib/worker.test.ts" }, "dependencies": { "@bbob/html": "4.3.1", @@ -147,6 +148,7 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", + "@cloudflare/vitest-pool-workers": "0.18.4", "@cloudflare/workers-types": "5.20260710.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b988d1ddb92f..9a0b8a447e25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -293,6 +293,9 @@ importers: '@cloudflare/playwright': specifier: 1.3.0 version: 1.3.0 + '@cloudflare/vitest-pool-workers': + specifier: 0.18.4 + version: 0.18.4(@cloudflare/workers-types@5.20260710.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': specifier: 5.20260710.1 version: 5.20260710.1 @@ -601,6 +604,13 @@ packages: workerd: optional: true + '@cloudflare/vitest-pool-workers@0.18.4': + resolution: {integrity: sha512-jOXvyLoR2b8jFvo3tX+mWxXXwcZ+PbId3d7vGFtZsuKakmDjKSeIq4o/sQjkqNv6q3XacIKSCAvfM8TfkPyrEw==} + peerDependencies: + '@vitest/runner': ^4.1.0 + '@vitest/snapshot': ^4.1.0 + vitest: ^4.1.0 + '@cloudflare/workerd-darwin-64@1.20260708.1': resolution: {integrity: sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg==} engines: {node: '>=16'} @@ -3342,6 +3352,9 @@ packages: city-timezones@1.3.4: resolution: {integrity: sha512-yLmtCxU4y5HLAw9XGIMcGEXO+R2KH1sb595wEQC/E9BxSDpazCD7v9ZQRySO7367IXWsWqt3z2xFVh3OkZdgEQ==} + cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -3593,7 +3606,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -6349,6 +6362,9 @@ packages: yuku-parser@0.5.44: resolution: {integrity: sha512-mAhpQZ/bXjxZmKiGUqEWskC9mZTcTBv6/fdzVdzdjM6XuD1DP3IavdLVWdM39L9ewK9vS9OtJmaKNeWgRzpy0w==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -6514,6 +6530,21 @@ snapshots: optionalDependencies: workerd: 1.20260708.1 + '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260710.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + dependencies: + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + cjs-module-lexer: 1.2.3 + esbuild: 0.28.1 + miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + wrangler: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + zod: 3.25.76 + transitivePeerDependencies: + - '@cloudflare/workers-types' + - bufferutil + - utf-8-validate + '@cloudflare/workerd-darwin-64@1.20260708.1': optional: true @@ -8632,6 +8663,8 @@ snapshots: city-timezones@1.3.4: {} + cjs-module-lexer@1.2.3: {} + cjs-module-lexer@2.2.0: {} class-transformer@0.3.1: {} @@ -12113,6 +12146,8 @@ snapshots: '@yuku-parser/binding-win32-arm64': 0.5.44 '@yuku-parser/binding-win32-x64': 0.5.44 + zod@3.25.76: {} + zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/scripts/workflow/build-routes.ts b/scripts/workflow/build-routes.ts index b6b8d860c2d8..af3a19e7d9ab 100644 --- a/scripts/workflow/build-routes.ts +++ b/scripts/workflow/build-routes.ts @@ -6,9 +6,15 @@ import toSource from 'tosource'; import type { RadarItem } from '../../lib/types'; import { getCurrentPath } from '../../lib/utils/helpers'; +import { findOrphanFiles } from './check-orphan-files'; const __dirname = getCurrentPath(import.meta.url); +const orphanTests = await findOrphanFiles(); +if (orphanTests.length) { + throw new Error(`Test files without a corresponding source file:\n${orphanTests.join('\n')}`); +} + // Check if building for Worker environment const isWorkerBuild = process.env.WORKER_BUILD === 'true'; diff --git a/scripts/workflow/check-orphan-files.ts b/scripts/workflow/check-orphan-files.ts new file mode 100644 index 000000000000..4d5008e4e6a3 --- /dev/null +++ b/scripts/workflow/check-orphan-files.ts @@ -0,0 +1,40 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import { getCurrentPath } from '../../lib/utils/helpers'; + +const __dirname = getCurrentPath(import.meta.url); +const repoRoot = path.join(__dirname, '../..'); + +const fileExists = async (filePath: string) => { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +}; + +export const findOrphanFiles = async (): Promise => { + const excludedDirs = ['lib/routes', 'lib/routes-deprecated'].map((dir) => path.join(repoRoot, dir) + path.sep); + + const entries = await fs.readdir(path.join(repoRoot, 'lib'), { recursive: true, withFileTypes: true }); + const candidates = entries + .filter((entry) => excludedDirs.every((dir) => !`${entry.parentPath}${path.sep}`.startsWith(dir))) + .filter((entry) => entry.isFile() && /\.test\.tsx?$/.test(entry.name)) + .map((entry) => { + const absolute = path.join(entry.parentPath, entry.name); + return { absolute, relative: path.relative(repoRoot, absolute).replaceAll('\\', '/') }; + }) + .filter(({ relative }) => relative !== 'lib/setup.test.ts'); + + const orphans = await Promise.all( + candidates.map(async ({ absolute, relative }) => { + const base = absolute.replace(/\.test\.tsx?$/, ''); + const [tsExists, tsxExists] = await Promise.all([fileExists(`${base}.ts`), fileExists(`${base}.tsx`)]); + return tsExists || tsxExists ? null : relative; + }) + ); + + return orphans.filter((relative) => relative !== null).toSorted((a, b) => a.localeCompare(b)); +}; diff --git a/vitest.config.ts b/vitest.config.ts index 668b66916a3e..b01f2bfeabef 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -11,6 +11,6 @@ export default defineConfig({ }, testTimeout: 10000, setupFiles: ['./lib/setup.test.ts'], - exclude: [...configDefaults.exclude, './lib/setup.test.ts'], + exclude: [...configDefaults.exclude, './lib/setup.test.ts', '**/*.worker.test.ts'], }, }); diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts new file mode 100644 index 000000000000..b3cf81bf9034 --- /dev/null +++ b/vitest.workers.config.ts @@ -0,0 +1,75 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import { cloudflareTest } from '@cloudflare/vitest-pool-workers'; +import type { Plugin } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +// Resolve .worker.ts files instead of .ts files, same as tsdown-worker.config.ts +function workerAliasPlugin(): Plugin { + return { + name: 'worker-alias', + enforce: 'pre', + resolveId(source, importer) { + // Skip if no importer (entry point) or already a .worker file + if (!importer || source.includes('.worker')) { + return null; + } + + // Handle relative imports + if (source.startsWith('.')) { + const importerDir = path.dirname(importer); + const resolved = path.resolve(importerDir, source); + + for (const ext of ['.worker.ts', '.worker.tsx']) { + const workerPath = resolved + ext; + if (fs.existsSync(workerPath)) { + return workerPath; + } + const withoutExt = resolved.replace(/\.(ts|tsx)$/, ''); + const workerPathAlt = withoutExt + ext; + if (fs.existsSync(workerPathAlt)) { + return workerPathAlt; + } + } + } + + // Handle @/ alias imports + if (source.startsWith('@/')) { + const libPath = path.resolve('./lib', source.slice(2)); + + for (const ext of ['.worker.ts', '.worker.tsx']) { + const workerPath = libPath + ext; + if (fs.existsSync(workerPath)) { + return workerPath; + } + } + } + + return null; + }, + }; +} + +export default defineConfig({ + plugins: [ + cloudflareTest({ + miniflare: { + compatibilityDate: '2025-06-17', + compatibilityFlags: ['nodejs_compat'], + kvNamespaces: ['CACHE'], + }, + }), + workerAliasPlugin(), + tsconfigPaths({ root: '.' }), + ], + resolve: { + alias: { + 'dotenv/config': path.resolve('./lib/shims/dotenv-config.ts'), + }, + }, + test: { + include: ['lib/**/*.worker.test.ts'], + }, +}); From 478df5e488c0ad3c0b4dc9b5ad52e1137e5b928a Mon Sep 17 00:00:00 2001 From: Arslan Ablikim Date: Sat, 11 Jul 2026 00:44:03 +0800 Subject: [PATCH 0286/1190] fix(route/makerworld): bypass Cloudflare challenge and adapt to new API shape (#22411) * fix(route/makerworld): bypass Cloudflare challenge and adapt to new API shape makerworld.com now sits behind a Cloudflare bot-management challenge that fingerprints the TLS/HTTP client rather than just headers, so plain fetch requests get a 403 even with a browser-like User-Agent. Fall back to a stealth Playwright page load when that happens. The trending page's data shape also changed: designs moved from `pageProps.popularDesignsData` to `pageProps.v2Props.foryouData.hits[].design`, mixed in with non-design promo/community entries, and `tags`/`startTime` were replaced by `createTime` (no tags exposed on this endpoint anymore). * fix(route/makerworld): always fetch via browser instead of trying ofetch first Cloudflare's challenge fingerprints the client itself, so the plain fetch attempt was guaranteed to always fail with a 403 first. Go straight to the Playwright fallback instead of paying for a request known to fail. --- lib/routes/makerworld/contest.ts | 10 ++------ lib/routes/makerworld/trending.ts | 27 +++++++++------------- lib/routes/makerworld/user-upload.ts | 13 ++--------- lib/routes/makerworld/utils.ts | 34 ++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/routes/makerworld/contest.ts b/lib/routes/makerworld/contest.ts index 5633f9cee819..5a465cfe14bd 100644 --- a/lib/routes/makerworld/contest.ts +++ b/lib/routes/makerworld/contest.ts @@ -1,9 +1,7 @@ -import { config } from '@/config'; import type { Route } from '@/types'; -import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { baseUrl, getNextBuildId } from './utils'; +import { baseUrl, fetchJson, getNextBuildId } from './utils'; export const route: Route = { path: '/contests', @@ -21,11 +19,7 @@ export const route: Route = { async function handler() { const nextBuildId = await getNextBuildId(); - const response = await ofetch(`${baseUrl}/_next/data/${nextBuildId}/en/contests.json`, { - headers: { - 'User-Agent': config.trueUA, - }, - }); + const response = await fetchJson(`${baseUrl}/_next/data/${nextBuildId}/en/contests.json`); const { listConst, previewList } = response.pageProps; const items = [ diff --git a/lib/routes/makerworld/trending.ts b/lib/routes/makerworld/trending.ts index 8ce6364c553b..6c3c2555a8d6 100644 --- a/lib/routes/makerworld/trending.ts +++ b/lib/routes/makerworld/trending.ts @@ -1,9 +1,7 @@ -import { config } from '@/config'; import type { Route } from '@/types'; -import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { baseUrl, getNextBuildId } from './utils'; +import { baseUrl, fetchJson, getNextBuildId } from './utils'; export const route: Route = { path: '/trending', @@ -21,20 +19,17 @@ export const route: Route = { async function handler() { const nextBuildId = await getNextBuildId(); - const response = await ofetch(`${baseUrl}/_next/data/${nextBuildId}/en.json`, { - headers: { - 'User-Agent': config.trueUA, - }, - }); + const response = await fetchJson(`${baseUrl}/_next/data/${nextBuildId}/en.json`); - const items = response.pageProps.popularDesignsData.map((d) => ({ - title: d.title, - link: `${baseUrl}/en/models/${d.id}-${d.slug}`, - author: d.designCreator.name, - category: d.tags, - pubDate: parseDate(d.startTime), - description: d.designExtension.design_pictures.map((i) => `
    ${d.name}
    ${i.name}
    `).join(''), - })); + const items = response.pageProps.v2Props.foryouData.hits + .filter((hit) => hit.design?.title) + .map(({ design: d }) => ({ + title: d.title, + link: `${baseUrl}/en/models/${d.id}-${d.slug}`, + author: d.designCreator.name, + pubDate: parseDate(d.createTime), + description: d.designExtension.design_pictures.map((i) => `
    ${d.title}
    ${i.name}
    `).join(''), + })); return { title: 'Trending Models - MakerWorld', diff --git a/lib/routes/makerworld/user-upload.ts b/lib/routes/makerworld/user-upload.ts index 2bb0f09a918a..65bab95cb9e6 100644 --- a/lib/routes/makerworld/user-upload.ts +++ b/lib/routes/makerworld/user-upload.ts @@ -1,9 +1,7 @@ -import { config } from '@/config'; import type { Route } from '@/types'; -import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { baseUrl, getNextBuildId } from './utils'; +import { baseUrl, fetchJson, getNextBuildId } from './utils'; export const route: Route = { path: '/user/:handle/upload', @@ -26,14 +24,7 @@ async function handler(ctx) { const { handle } = ctx.req.param(); const nextBuildId = await getNextBuildId(); - const response = await ofetch(`${baseUrl}/_next/data/${nextBuildId}/en/${handle}/upload.json`, { - headers: { - 'User-Agent': config.trueUA, - }, - query: { - handle, - }, - }); + const response = await fetchJson(`${baseUrl}/_next/data/${nextBuildId}/en/${handle}/upload.json`, { handle }); const { userInfo, designs } = response.pageProps; const items = designs.map((d) => ({ diff --git a/lib/routes/makerworld/utils.ts b/lib/routes/makerworld/utils.ts index 4930e6381292..f6b0afb7789e 100644 --- a/lib/routes/makerworld/utils.ts +++ b/lib/routes/makerworld/utils.ts @@ -1,18 +1,38 @@ import { load } from 'cheerio'; -import { config } from '@/config'; import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; +import { getPlaywrightPage } from '@/utils/playwright'; export const baseUrl = 'https://makerworld.com'; +// makerworld.com sits behind a Cloudflare bot-management challenge that fingerprints the +// TLS/HTTP client itself rather than just headers, so a plain fetch always gets a 403 no +// matter what headers are sent. A real (stealth-patched) browser is required to pass it. +const fetchViaBrowser = async (url: string, type: 'html' | 'json' = 'html') => { + const { page, destroy } = await getPlaywrightPage(url, { + onBeforeLoad: async (page) => { + const expectResourceTypes = new Set(['document', 'script', 'xhr', 'fetch']); + await page.route('**/*', (route) => { + const request = route.request(); + expectResourceTypes.has(request.resourceType()) ? route.continue() : route.abort(); + }); + }, + }); + try { + return (await page.evaluate(type === 'html' ? () => document.documentElement.innerHTML : () => document.documentElement.textContent)) ?? ''; + } finally { + await destroy(); + } +}; + +export const fetchJson = async (url: string, query?: Record) => { + const finalUrl = query ? `${url}?${new URLSearchParams(query).toString()}` : url; + return JSON.parse(await fetchViaBrowser(finalUrl, 'json')); +}; + export const getNextBuildId = () => cache.tryGet('makerworld:nextBuildId', async () => { - const response = await ofetch(`${baseUrl}/en`, { - headers: { - 'User-Agent': config.trueUA, - }, - }); + const response = await fetchViaBrowser(`${baseUrl}/en`); const $ = load(response); const nextData = JSON.parse($('script#__NEXT_DATA__').text()); return nextData.buildId; From d51b0601258220e3114b76f09e826c6e2acf2f96 Mon Sep 17 00:00:00 2001 From: "Jagger.H" Date: Sat, 11 Jul 2026 05:08:41 +0800 Subject: [PATCH 0287/1190] fix(route/comicat): pass visitor_test cookie to clear the anti-bot interstitial (#22656) comicat.org now fronts every page with a fake JS "captcha" (/public/html/start/) that redirects back only after setting a `visitor_test=human` cookie. A bare request 302s to that page, so #listTable and the detail selectors match nothing (empty feed). Send the cookie directly on both the search listing and the per-item detail fetches. --- lib/routes/comicat/search.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/routes/comicat/search.ts b/lib/routes/comicat/search.ts index 6051a5916418..3c013955a6c9 100644 --- a/lib/routes/comicat/search.ts +++ b/lib/routes/comicat/search.ts @@ -7,6 +7,12 @@ import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://comicat.org'; +// comicat.org fronts every page with a fake JS "captcha" interstitial (/public/html/start/) +// whose only effect is to POST a form that sets a `visitor_test=human` cookie. Sending that +// cookie directly skips the interstitial; without it every request 302s to the captcha page +// and the listing/detail selectors match nothing. +const headers = { Cookie: 'visitor_test=human' }; + export const route: Route = { path: '/search/:keyword', categories: ['anime'], @@ -27,7 +33,7 @@ export const route: Route = { async function handler(ctx) { const keyword = ctx.req.param('keyword'); - const { data: response } = await got(`${baseUrl}/search.php?keyword=${encodeURIComponent(keyword)}`); + const { data: response } = await got(`${baseUrl}/search.php?keyword=${encodeURIComponent(keyword)}`, { headers }); const $ = load(response); const list = $('#listTable tbody > tr') .toArray() @@ -41,7 +47,7 @@ async function handler(ctx) { const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const { data: response } = await got(item.link); + const { data: response } = await got(item.link, { headers }); const $ = load(response); item.pubDate = parseDate($('div.main > div.slayout > div > div.c1 > div:nth-child(1) > div > p:nth-child(4)').text().split('发布时间: ', 2)[1]); From 70b780b012b63d6d26f3adda9c33a46c545b8170 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello <3691490+PeterDaveHello@users.noreply.github.com> Date: Sat, 11 Jul 2026 23:36:33 +0800 Subject: [PATCH 0288/1190] fix(hackernews): improve route documentation and parameter naming (#22491) - Update route name from 'User' to 'Stories' - Rename parameter 'user' to 'value' for clarity - Document all available sections (index, newest, ask, show, jobs, over, threads, submitted) - Document type parameter options (sources, comments, comments_list) - Clarify 'over' section uses points threshold instead of user ID - Document value's general ?id= behavior for other sections - Add usage examples (HN100, user submitted, threads, comments_list) --- lib/routes/hackernews/index.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/routes/hackernews/index.ts b/lib/routes/hackernews/index.ts index 8e2e7af56041..9d6a35197359 100644 --- a/lib/routes/hackernews/index.ts +++ b/lib/routes/hackernews/index.ts @@ -7,19 +7,19 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/:section?/:type?/:user?', + path: '/:section?/:type?/:value?', categories: ['programming'], view: ViewType.Articles, example: '/hackernews/threads/comments_list/dang', parameters: { section: { - description: 'Content section, default to `index`', + description: 'Content section, default to `index`. Common sections: `index`, `newest`, `ask`, `show`, `jobs`, `over`, `threads`, `submitted`. Any valid HN section (e.g. `best`, `front`, `active`) is also accepted', }, type: { - description: 'Link type, default to `sources`', + description: 'Content format, default to `sources`. `sources` links to original articles, `comments` fetches full comment threads, `comments_list` shows parent story with single comment', }, - user: { - description: 'Set user, only valid in `threads` and `submitted` sections', + value: { + description: 'For `threads`/`submitted` sections, set user ID. For `over` section, set minimum points threshold (default 100). For other sections, appended as `?id=` (e.g. `value=dang` → `?id=dang`)', }, }, features: { @@ -35,23 +35,29 @@ export const route: Route = { source: ['news.ycombinator.com/:section', 'news.ycombinator.com/'], }, ], - name: 'User', + name: 'Stories', maintainers: ['nczitzk', 'xie-dongping'], handler, - description: 'Subscribe to the content of a specific user', + description: `Subscribe to Hacker News content by section, user, or minimum points + +Examples: + +| HN100 | User submitted | User threads | Comments list | +| ----- | -------------- | ------------ | ------------- | +| \`/hackernews/over\` | \`/hackernews/submitted/sources/dang\` | \`/hackernews/threads/sources/dang\` | \`/hackernews/threads/comments_list/dang\` |`, }; async function handler(ctx) { const section = ctx.req.param('section') ?? 'index'; const type = ctx.req.param('type') ?? 'sources'; - const user = ctx.req.param('user') ?? ''; + const value = ctx.req.param('value') ?? ''; const rootUrl = 'https://news.ycombinator.com'; const sectionUrl = section === 'index' ? '' : `/${section}`; - let optUrl = user === '' ? '' : '?id=' + user; + let optUrl = value === '' ? '' : '?id=' + value; if (section === 'over') { - optUrl = user === '' ? '?points=100' : '?points=' + user; + optUrl = value === '' ? '?points=100' : '?points=' + value; } const currentUrl = `${rootUrl}${sectionUrl}${optUrl}`; From e2487e6df34c97299aebbedbeccdbd384465879a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Jul 2026 15:38:03 +0000 Subject: [PATCH 0289/1190] style: auto format --- lib/routes/hackernews/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/hackernews/index.ts b/lib/routes/hackernews/index.ts index 9d6a35197359..53979fc0b634 100644 --- a/lib/routes/hackernews/index.ts +++ b/lib/routes/hackernews/index.ts @@ -42,8 +42,8 @@ export const route: Route = { Examples: -| HN100 | User submitted | User threads | Comments list | -| ----- | -------------- | ------------ | ------------- | +| HN100 | User submitted | User threads | Comments list | +| ------------------ | ------------------------------------ | ---------------------------------- | ---------------------------------------- | | \`/hackernews/over\` | \`/hackernews/submitted/sources/dang\` | \`/hackernews/threads/sources/dang\` | \`/hackernews/threads/comments_list/dang\` |`, }; From 48f7d4cf4cff36d35f1061c0c6174e06349934fa Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sun, 12 Jul 2026 00:09:42 +0800 Subject: [PATCH 0290/1190] feat(route): add Hunan Museum exhibiton and news routes (#22430) * feat(route): add Hunan Museum exhibiton and news routes * fix: update news ts file name * fix: rm hnmnes.ts * fix: update hostname * fix: add ctx for special exhibitions * fix:use flatmap and remove redundant --- lib/routes/hnmuseum/exhibitions.tsx | 215 ++++++++++++++++++++++++++++ lib/routes/hnmuseum/hnmnews.ts | 59 ++++++++ lib/routes/hnmuseum/namespace.ts | 10 ++ 3 files changed, 284 insertions(+) create mode 100644 lib/routes/hnmuseum/exhibitions.tsx create mode 100644 lib/routes/hnmuseum/hnmnews.ts create mode 100644 lib/routes/hnmuseum/namespace.ts diff --git a/lib/routes/hnmuseum/exhibitions.tsx b/lib/routes/hnmuseum/exhibitions.tsx new file mode 100644 index 000000000000..c15a76089877 --- /dev/null +++ b/lib/routes/hnmuseum/exhibitions.tsx @@ -0,0 +1,215 @@ +import { type Cheerio, load } from 'cheerio'; +import type { Element } from 'domhandler'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +interface ExhibitionItem { + exhibitionType: 'permanent' | 'special' | 'temporary'; + title: string; + itemLink: string; + imgUrl: string; + location?: string; + fullDuration?: string; +} + +type ExhibitionConfig = { + selector: string; + type: 'permanent' | 'special' | 'temporary'; + extra: ($item: Cheerio) => Partial>; +}; + +// convert date string like "YYYY年M月D日" to "YYYY-MM-DD" +const formatStr = (dateStr: string | undefined): string | undefined => { + if (!dateStr) { + return undefined; + } + + const match = dateStr.trim().match(/(\d{4})年\s*(\d{1,2})月\s*(\d{1,2})日/); + + if (match) { + const year = match[1]; + const month = match[2].padStart(2, '0'); + const day = match[3].padStart(2, '0'); + return `${year}-${month}-${day}`; + } + return undefined; +}; + +const extractDates = (durationStr?: string): { startDate?: string; endDate?: string } => { + if (!durationStr || durationStr.includes('永久')) { + return { startDate: undefined, endDate: undefined }; + } + const parts = durationStr.split(/—/); + + return { startDate: formatStr(parts[0]), endDate: formatStr(parts[1]) }; +}; + +const renderDescription = (imgUrl: string, location?: string, startDate?: string, endDate?: string, fullDuration?: string) => + renderToString( +
    + {} +
    +

    + 地点: + {location ?? '参考展览详情或图片'} +

    +

    + 开展: + {startDate ?? '参考展览详情或图片'} +

    +

    + 闭展: + {endDate ?? '参考展览详情或图片'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + +export const route: Route = { + path: '/current-exhibitions/:type?', + categories: ['travel'], + example: '/hnmuseum/current-exhibitions/special', + parameters: { + type: 'Exhibition type, supported values: special(临时展览 special+temporary). Default: All exhibitions.', + }, + name: 'Current Exhibitions', + maintainers: ['magazian'], + radar: [ + { + source: ['www.hnmuseum.com/zh-hans/content/当前展览-基本陈列'], + target: '/current-exhibitions', + }, + ], + + handler: async (ctx) => { + const typeParam = ctx.req.param('type'); + const isSpecial = typeParam === 'special'; + + const baseUrl = 'https://www.hnmuseum.com'; + const listUrl = `${baseUrl}/zh-hans/content/当前展览-基本陈列`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: listUrl, + }); + + const $ = load(response.data); + + const exhibitionConfigs: ExhibitionConfig[] = [ + { + selector: '#block-views-a784821b4fd9f41563c7164fd2a2f96e .views-row', + type: 'permanent' as const, + extra: ($item: Cheerio) => ({ + location: $item.find('.views_zhanting .field-content').text().trim(), + fullDuration: $item.find('.views_startdate .field-content').text().trim(), + }), + }, + { + selector: '#block-views-chen-lie-block .views-row', + type: 'special' as const, + extra: ($item: Cheerio) => ({ + location: $item.find('.views_zhanting .field-content').text().trim(), + }), + }, + { + selector: '#block-views-zhan-lan-block .views-row', + type: 'temporary' as const, + extra: (_$item: Cheerio) => ({}), + }, + ]; + + // use flatMap to avoid pushing into an external array, making the code cleaner and more functional + const list = exhibitionConfigs.flatMap((config) => + $(config.selector) + .toArray() + .map((item) => { + const $item = $(item); + const $a = $item.find('.views_title a, .views_img_bg a').first(); + const link = $a.attr('href') || ''; + + return { + exhibitionType: config.type, + title: $a.text(), + itemLink: link.startsWith('http') ? link : `${baseUrl}${link}`, + imgUrl: $item.find('img').attr('src') || '', + ...config.extra($item as Cheerio), + }; + }) + ); + + const targetList = isSpecial ? list.filter((item) => item.exhibitionType === 'special' || item.exhibitionType === 'temporary') : list; + + const items = await Promise.all( + targetList.map((item) => { + const cacheKey = item.itemLink; + + return cache.tryGet(cacheKey, async (): Promise => { + if (item.exhibitionType === 'permanent' || item.exhibitionType === 'special') { + const { startDate, endDate } = extractDates(item.fullDuration); + return { + title: item.title, + link: item.itemLink, + pubDate: startDate ? parseDate(startDate) : undefined, + description: renderDescription(item.imgUrl, item.location, startDate, endDate, item.fullDuration), + _extra: { museumName, location: item.location, startDate, endDate }, + }; + } + + const url = new URL(item.itemLink); + // for theme exhibition, the detail page may be a SPA, so need to fetch the JS file to extract the data + if (url.hostname === 'vrexhibition.hnmuseum.com') { + const htmlRes = await got({ method: 'get', url: item.itemLink }); + const $spa = load(htmlRes.data); + const jsSrc = $spa('script[type="module"][src]').attr('src') || ''; + const jsUrl = new URL(jsSrc, item.itemLink).href; + const jsRes = await got({ method: 'get', url: jsUrl }); + const jsContent = jsRes.data; + const titleMatch = jsContent.match(/"title"[^)]*\)\s*\},[^"]*"([^"]+)"/); + const title = titleMatch?.[1]; + const dateMatch = jsContent.match(/"(\d{4}年\d{1,2}月\d{1,2}日—\d{4}年\d{1,2}月\d{1,2}日)"/); + const fullDuration = dateMatch?.[1]; + const locMatch = jsContent.match(/"(湖南省博物馆[^"]+厅)"/); + const location = locMatch?.[1]; + const { startDate, endDate } = extractDates(fullDuration); + + return { + title, + link: item.itemLink, + pubDate: startDate ? parseDate(startDate) : undefined, + description: renderDescription(item.imgUrl, location, startDate, endDate, fullDuration), + _extra: { museumName, location, startDate, endDate }, + }; + } + + const detailResponse = await got({ method: 'get', url: item.itemLink }); + const content = load(detailResponse.data); + const title = content('h1#page-title').text(); + return { + title, + link: item.itemLink, + description: renderDescription(item.imgUrl), + _extra: { museumName }, + }; + }) as Promise; + }) + ); + + return { + title: `${museumName} - 当前展览${isSpecial ? ' - 专题&临时展览' : ''}`, + link: listUrl, + language: 'zh-CN', + item: items.filter((item) => item.title && Object.keys(item).length > 0) as DataItem[], + }; + }, +}; diff --git a/lib/routes/hnmuseum/hnmnews.ts b/lib/routes/hnmuseum/hnmnews.ts new file mode 100644 index 000000000000..dd41cdbf99e6 --- /dev/null +++ b/lib/routes/hnmuseum/hnmnews.ts @@ -0,0 +1,59 @@ +import { load } from 'cheerio'; + +import type { DataItem, Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +export const route: Route = { + path: '/hnmnews', + categories: ['travel'], + example: '/hnmuseum/hnmnews', + name: 'HNM News', + maintainers: ['magazian'], + radar: [ + { + source: ['www.hnmuseum.com/zh-hans/xiangbo_dongtai_news'], + target: '/hnmnews', + }, + ], + + handler: async () => { + const baseUrl = 'https://www.hnmuseum.com'; + const apiUrl = `${baseUrl}/zh-hans/xiangbo_dongtai_news`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: apiUrl, + }); + + const $ = load(response.data); + + const items = $('.view-content .views-row') + .toArray() + .map((el) => { + const $item = $(el); + const $a = $item.find('.views-field-title-1 a'); + const title = $a.text(); + const href = $a.attr('href'); + const link = new URL(href!, baseUrl).href; + const dateString = $item.find('.date-display-single').attr('content'); + + return { + title, + link, + pubDate: timezone(parseDate(dateString!), 8), + }; + }); + + return { + title: `${museumName} - 湘博动态`, + link: apiUrl, + language: 'zh-CN', + item: items as DataItem[], + }; + }, +}; diff --git a/lib/routes/hnmuseum/namespace.ts b/lib/routes/hnmuseum/namespace.ts new file mode 100644 index 000000000000..894154b0c165 --- /dev/null +++ b/lib/routes/hnmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Hunan Museum', + url: 'www.hnmuseum.com/zh-hans', + + zh: { + name: '湖南省博物馆', + }, +}; From af7f36d44c40fb21682a6d1a24ad51ccb5e50287 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sun, 12 Jul 2026 00:34:07 +0800 Subject: [PATCH 0291/1190] fix(route): refactor to adapt to new web design (#22383) * fix(route): refactor to adapt to new web design * fix:reduce complexity * fix: update CSS selector for xingnew and xwzt * fix: update selectors * fix:update hideBoxes * fix: replace for with find --- lib/routes/chnmuseum/xingnew.ts | 8 +- lib/routes/chnmuseum/xwzt.ts | 4 +- lib/routes/chnmuseum/zl.tsx | 160 ++++++++++++++++---------------- 3 files changed, 88 insertions(+), 84 deletions(-) diff --git a/lib/routes/chnmuseum/xingnew.ts b/lib/routes/chnmuseum/xingnew.ts index 85716e00450c..e50bd2ec87f0 100644 --- a/lib/routes/chnmuseum/xingnew.ts +++ b/lib/routes/chnmuseum/xingnew.ts @@ -31,12 +31,12 @@ export const route: Route = { const response = await ofetch('https://www.chnmuseum.cn/zx/xingnew/'); const $ = load(response); - const list = $('ul.cj_xushuliebao_list li') + const list = $('ul.xly_list_ts li') .toArray() .map((item) => { - item = $(item); - const a = item.find('a'); - const dateText = item.find('span.date').text(); + const $item = $(item); + const a = $item.find('a.titles'); + const dateText = $item.find('.times span.sp').text(); return { title: a.attr('title') || a.text(), diff --git a/lib/routes/chnmuseum/xwzt.ts b/lib/routes/chnmuseum/xwzt.ts index 1953957b3cd6..c8a6804dd6e7 100644 --- a/lib/routes/chnmuseum/xwzt.ts +++ b/lib/routes/chnmuseum/xwzt.ts @@ -28,11 +28,11 @@ export const route: Route = { const response = await ofetch('https://www.chnmuseum.cn/zx/xwzt/'); const $ = load(response); - const items = $('ul.cj_hd_zhanh li') + const items = $('ul.xly_list_ts li') .toArray() .map((item) => { item = $(item); - const a = item.find('div.cj_hd_biaoti a').first(); + const a = item.find('a.titles').first(); return { title: a.attr('title') || a.text(), diff --git a/lib/routes/chnmuseum/zl.tsx b/lib/routes/chnmuseum/zl.tsx index 65e832285061..b8a533b024cf 100644 --- a/lib/routes/chnmuseum/zl.tsx +++ b/lib/routes/chnmuseum/zl.tsx @@ -1,4 +1,5 @@ import { load } from 'cheerio'; +import dayjs from 'dayjs'; import type { Context } from 'hono'; import { renderToString } from 'hono/jsx/dom/server'; @@ -10,36 +11,26 @@ import { parseDate } from '@/utils/parse-date'; import { namespace } from './namespace'; const titleTagMap: Record = { - zhanlanyugao: '正在展出', // This is what the website used for current exhibition - ztzl: '主题展览', + zhanlanyugao: '正在展出', jbcl: '基本陈列', ztcl: '专题展览', lszl: '临时展览', - 'lszl/zdztzl': '临时展览 - 主题展览', - 'lszl/dfjpwwxl': '临时展览 - 精品文物展', - 'lszl/lswhxl': '临时展览 - 历史文化展', - 'lszl/kgfjxl': '临时展览 - 考古发现展', - 'lszl/kjcxz': '临时展览 - 科技创新展', - 'lszl/dywhxl': '临时展览 - 地域文化展', - 'lszl/jdmszpxl': '临时展览 - 经典美术展', - 'lszl/gjjlxl': '临时展览 - 国际交流展', + 'lszl/lswh': '临时展览 - 历史文化', + 'lszl/gjjl': '临时展览 - 国际交流', + 'lszl/zdzt': '临时展览 - 重大主题', + 'lszl/yscx': '临时展览 - 艺术创新', + gjzl: '国家展览', gbxz: '国博巡展', }; -// Formatting Function: Returns YYYY-MM-DD only if there are 3 valid numeric segments; otherwise, returns undefined. +// Formatting Function: Returns YYYY-MM-DD when there are 3 valid numeric segments that are formatted by parseExhibitionDate; otherwise, returns undefined. const formatExhibitionDate = (dateStr: string | undefined): string | undefined => { if (!dateStr) { return undefined; } - const normalized = dateStr - .replaceAll(/年|月/g, '-') - .replaceAll('日', '') - .replaceAll(/[/.]/g, '-'); - const parts = normalized.split('-').filter(Boolean); - if (parts.length === 3) { - return `${parts[0]}-${parts[1].padStart(2, '0')}-${parts[2].padStart(2, '0')}`; - } - return undefined; + const normalized = dateStr.replaceAll(/[年月/.]/g, '-').replaceAll('日', ''); + const d = dayjs(normalized); + return d.format('YYYY-MM-DD'); }; const parseExhibitionDuration = (duration: string) => { @@ -57,22 +48,20 @@ const parseExhibitionDuration = (duration: string) => { let startDateRaw: string | undefined; let endDateRaw: string | undefined; - if (cleanStr.startsWith('展至') && allDates.length > 0) { - endDateRaw = allDates[0]; - } else if (allDates.length >= 2) { + if (allDates.length >= 2) { startDateRaw = allDates[0]; let rawEnd = allDates[1]; // Logic to complete the year if (!/\d{4}/.test(rawEnd) && startDateRaw) { const startYear = startDateRaw.match(/^\d{4}/); - if (startYear) { - const sep = startDateRaw.includes('年') ? '年' : startDateRaw.includes('/') ? '/' : '-'; - rawEnd = `${startYear[0]}${sep}${rawEnd}`; + if (startYear?.[0]) { + rawEnd = `${startYear[0]}${startDateRaw[4]}${rawEnd}`; } } endDateRaw = rawEnd; } else if (allDates.length === 1) { - if (cleanStr.includes('展至')) { + if (cleanStr.includes('闭展')) { + // e.g. "2025年2月16日闭展" endDateRaw = allDates[0]; } else { startDateRaw = allDates[0]; @@ -101,23 +90,38 @@ const resolveRouteConfig = (type: string | undefined, subtype: string | undefine }; }; +// create itemLink +const buildItemLink = (rawLink: string, contextUrl: string, baseUrl: string) => (rawLink ? new URL(rawLink, contextUrl).href : baseUrl); + +// create exhibitionLink +const buildExhibitionLink = (rawZtzl: string, itemLink: string, baseUrl: string) => (rawZtzl ? new URL(rawZtzl, baseUrl).href : itemLink); + // to concurrent or single-page retrieval according to titletag const fetchTargetElements = async (cleanType: string, subtype: string | undefined, url: string, baseUrl: string) => { - const items: Array<{ $item: any; contextUrl: string }> = []; + const items: Array<{ $item: any; contextUrl: string; itemLink: string; exhibitionLink: string; rawZtzl: string }> = []; // Use a Set to track visited links and filter out duplicate HTML elements directly at the source // (e.g., when the same exhibition appears in both the main list and a specific sub-category list). const seenLinks = new Set(); const extractItems = (html: string, contextUrl: string) => { const $ = load(html); - const pageElements = $('ul[id="div"] a.recurl, ul.cj_hb_list a.recurl').toArray(); + const selectors = ['ul[id="div"] > li > a', 'li.scale_imgs > a'].join(', '); + + const pageElements = $(selectors).not('.zl_lszl_list *').toArray(); + for (const el of pageElements) { const $item = $(el).closest('li'); if ($item.length > 0) { - const href = $(el).attr('href') || ''; - if (href && !seenLinks.has(href)) { - seenLinks.add(href); - items.push({ $item, contextUrl }); + const rawLink = $(el).attr('href') || ''; + const rawZtzl = $(el).attr('ztzlurl')?.trim() || ''; // some exhibition links have a separate detailed page, use ztzlurl to get the detailed exhibition link if available + + // Use exhibitionLink to remove the repeat ones + const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); + const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); + + if (exhibitionLink && !seenLinks.has(exhibitionLink)) { + seenLinks.add(exhibitionLink); + items.push({ $item, contextUrl, itemLink, exhibitionLink, rawZtzl }); } } } @@ -142,23 +146,15 @@ const fetchTargetElements = async (cleanType: string, subtype: string | undefine return items; }; -// create itemLink -const buildItemLink = (rawLink: string, contextUrl: string, baseUrl: string) => (rawLink ? new URL(rawLink, contextUrl).href : baseUrl); - -// create exhibitionLink -const buildExhibitionLink = (rawZtzl: string, itemLink: string, baseUrl: string) => (rawZtzl ? new URL(rawZtzl, baseUrl).href : itemLink); - export const route: Route = { path: '/zl/:type?/:subType?', categories: ['travel'], - example: '/chnmuseum/zl/lszl/zdztzl', + example: '/chnmuseum/zl/lszl/zdzt', parameters: { - type: 'Exhibition type, supported values: zhanlanyugao(正在展出)、ztzl(主题展览)、jbcl(基本陈列)、ztcl(专题展览)、lszl(临时展览)、gbxz(国博巡展). Default: All exhibitions.', - subType: - 'subtype only works under type lszl(临时展览), supported values: zdztzl(主题展览)、dfjpwwxl(精品文物展)、lswhxl(历史文化展)、kgfjxl(考古发现展)、kjcxz(科技创新展)、dywhxl(地域文化展)、jdmszpxl(经典美术展)、gjjlxl(国际交流展)', + type: 'Exhibition type, supported values: zhanlanyugao(正在展出)、jbcl(基本陈列)、ztcl(专题展览)、lszl(临时展览)、gjzl(国家展览)、gbxz(国博巡展). Default: All exhibitions.', + subType: 'subtype only works under type lszl(临时展览), supported values: zdzt(重大主题)、lswh(历史文化)、yscx(艺术创新)、gjjl(国际交流)', }, - // Use Chnmuseum English version channel name - name: 'Exhibitions', + name: 'Exhibitions', // Use Chnmuseum English version channel name maintainers: ['magazian'], radar: [ { @@ -167,8 +163,8 @@ export const route: Route = { }, ], handler: async (ctx: Context): Promise => { - const type = ctx.req.param('type')?.trim(); - const subtype = ctx.req.param('subType')?.trim(); + const type = ctx.req.param('type'); + const subtype = ctx.req.param('subType'); const museumName = namespace.zh?.name || namespace.name; const baseUrl = 'https://www.chnmuseum.cn'; @@ -178,11 +174,7 @@ export const route: Route = { const list = ( await Promise.all( itemsToParse.map(({ $item, contextUrl }) => { - const aTag = $item.find('a.recurl'); - - if (!aTag.length) { - return null; - } + const aTag = $item.find('a').first(); const rawLink = aTag.attr('href') || ''; const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); @@ -194,56 +186,70 @@ export const route: Route = { // title may not have full display on the page, use the img alt information instead const imgTag = $item.find('img'); - const title = imgTag.attr('alt') || ''; + let title = $item.find('.hide_title').text() || ''; const rawSrc = imgTag.attr('src')!; const imgUrl = new URL(rawSrc, contextUrl).href; - const location = $item.find('div.cj_zxx1').text().trim(); + const hideBox = $item.find('.hide_box'); + const hideBoxes = hideBox.toArray().map((_, i) => hideBox.eq(i)); + const findValue = (keyword: string) => + hideBoxes + .find((box) => box.find('p').first().text().includes(keyword)) + ?.find('p') + .last() + .text() + .trim() ?? ''; - let fullDuration = $item.find('div.cj_zxx2 p').text().trim(); + const location = findValue('地点'); + let fullDuration = findValue('展期'); - if (fullDuration.endsWith('...')) { + if (!title || title.endsWith('...')) { const detailResponse = await got(itemLink); - const match = detailResponse.data.match(/var\s+qtxszq\s*=\s*"(.*?)";/); - if (match && match[1]) { - fullDuration = match[1]; + const $detail = load(detailResponse.data); + const detailTitle = $detail('.crumb_mod_box .title').text(); + title = + detailTitle || + $detail('title') + .text() + .replace(/-?\s*中国国家博物馆/, ''); + + if (!fullDuration) { + const textDuration = $detail('li, strong, p') + .toArray() + .map((el) => $detail(el).text().trim()) + .find((text) => text.startsWith('展期:') || text.includes('闭展')) + ?.replace('展期:', '') + .trim(); + const regexDuration = detailResponse.data.match(/var\s+qtxszq\s*=\s*"(.*?)";/)?.[1]; + fullDuration = textDuration || regexDuration || ''; } } const { startDate, endDate } = parseExhibitionDuration(fullDuration); // CHN museum didnot have pubDate on the page, use exhibition startDate instead. - // Variable Handling: If the value is `undefined`, it remains `undefined` to facilitate subsequent processing by the Calendar component. const pubDate = startDate ? parseDate(startDate) : undefined; const description = renderToString(
    - { - <> - -
    - - } + +

    地点: - {location} + {location || '参考详情'}

    开展: - {startDate ?? '未定/常设'} + {startDate || '未定/常设'}

    闭展: - {endDate ?? '未定/常设'} + {endDate || '未定/常设'}

    - {fullDuration && (

    - - 原始展期: - {fullDuration} - + 原始展期:{fullDuration}

    )}
    @@ -260,11 +266,9 @@ export const route: Route = { // For further .ics file processing _extra: { museumName, - title, location, - startDate, // format: YYYY-MM-DD or '未定/常设' - endDate, // format: YYYY-MM-DD or '未定/常设' - itemLink, + startDate, + endDate, }, } as DataItem; }) as Promise; From dcd21b8c0ad25cf3dfc4c5e198ae4f1c1f353f3d Mon Sep 17 00:00:00 2001 From: sirius60111 Date: Sun, 12 Jul 2026 19:42:04 +0800 Subject: [PATCH 0292/1190] feat(route/xueqiu): fetch user timeline via API without browser (#22348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The /xueqiu/user route drove a headless browser (Patchright) to load the profile page and navigate to each status for full text. The user timeline is public, so the data can be fetched directly from api.xueqiu.com once the WAF challenge cookie is obtained. - replace the browser navigation with direct ofetch calls to api.xueqiu.com/v4/statuses/user_timeline.json and statuses/show.json - requirePuppeteer: false (no longer drives a browser); antiCrawler: true - pass source=买卖 for the 交易 (type 11) tab so it filters correctly - skip the show.json detail request when legal_user_visible is true - inline images and the retweeted status into the description - derive screen_name and the avatar image from the timeline response Co-authored-by: Claude Opus 4.8 (1M context) --- lib/routes/xueqiu/user.ts | 226 +++++++++++++++++++------------------- 1 file changed, 110 insertions(+), 116 deletions(-) diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index ccda3ba3c32a..97b0bd24469f 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -3,10 +3,11 @@ import sanitizeHtml from 'sanitize-html'; import { parseToken } from '@/routes/xueqiu/cookies'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import playwright from '@/utils/playwright'; const rootUrl = 'https://xueqiu.com'; +const apiUrl = 'https://api.xueqiu.com'; export const route: Route = { path: '/user/:id/:type?', @@ -15,8 +16,8 @@ export const route: Route = { parameters: { id: '用户 id, 可在用户主页 URL 中找到', type: '动态的类型, 不填则默认全部' }, features: { requireConfig: false, - requirePuppeteer: true, - antiCrawler: false, + requirePuppeteer: false, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, @@ -35,9 +36,59 @@ export const route: Route = { | 0 | 2 | 4 | 9 | 11 |`, }; +const stripHtml = (html: string): string => sanitizeHtml(html, { allowedTags: [], allowedAttributes: {} }); + +// Build a feed item from the timeline list data alone (no detail request). +const buildListItem = (item: any) => ({ + title: item.title || stripHtml(item.description || ''), + description: item.description || '', + pubDate: parseDate(item.created_at), + link: rootUrl + item.target, +}); + +const buildTitle = (item: any, detail: any): string => { + if (item.title) { + return item.title; + } + return stripHtml(item.text || item.description || detail.text || ''); +}; + +const buildDescription = (detail: any): string => { + let text = detail.text ?? detail.description; + const images = detail.image_info_list ?? []; + for (const img of images) { + if (img?.filename) { + text += `
    `; + } + } + if (detail.retweeted_status) { + text += `
    ${detail.retweeted_status.user.screen_name}: ${detail.retweeted_status.text}
    `; + } + return text; +}; + +const extractProfileImage = (user: any): string | undefined => { + if (!user?.profile_image_url || !user?.photo_domain) { + return undefined; + } + + const imageUrls = user.profile_image_url.split(',').filter(Boolean); + if (imageUrls.length === 0) { + return undefined; + } + + // Priority order for image sizes + const sizePriority = ['!180x180.png', '!50x50.png', '!30x30.png']; + const selectedImageUrl = sizePriority.map((size) => imageUrls.find((url) => url.includes(size))).find(Boolean) || imageUrls[0]; + const baseDomain = user.photo_domain.startsWith('//') ? `https:${user.photo_domain}` : user.photo_domain; + + return `${baseDomain}${selectedImageUrl}`; +}; + async function handler(ctx) { const id = ctx.req.param('id'); const type = ctx.req.param('type') || 10; + const source = type === '11' ? '买卖' : ''; const typename = { 10: '全部', 0: '原发布', @@ -48,119 +99,62 @@ async function handler(ctx) { }; const link = `${rootUrl}/u/${id}`; - const token = await parseToken(link); + const cookie = await parseToken(link); - const context = await playwright(); - try { - const mainPage = await context.newPage(); - - await mainPage.setExtraHTTPHeaders({ - Cookie: token as string, + const response = await ofetch(`${apiUrl}/v4/statuses/user_timeline.json`, { + query: { + user_id: id, + type, + source, + }, + headers: { + Cookie: cookie, Referer: link, - }); - - await mainPage.goto(link, { - waitUntil: 'domcontentloaded', - }); - await mainPage.waitForFunction(() => document.readyState === 'complete'); - - const apiUrl = `${rootUrl}/v4/statuses/user_timeline.json?user_id=${id}&type=${type}`; - const response = await mainPage.evaluate(async (url) => { - const response = await fetch(url); - return response.json(); - }, apiUrl); - - if (!response?.statuses) { - throw new Error('获取用户动态数据失败'); - } - - const data = response.statuses.filter((s) => s.mark !== 1); - - if (!data.length) { - throw new Error('未找到有效的动态数据'); - } - - const items = await Promise.all( - data.map((item) => - cache.tryGet(item.target, async () => { - const detailUrl = rootUrl + item.target; - try { - await mainPage.goto(detailUrl, { - waitUntil: 'domcontentloaded', - }); - await mainPage.waitForFunction(() => document.readyState === 'complete'); - - const content = await mainPage.evaluate(() => { - const articleContent = document.querySelector('.article__bd')?.innerHTML || ''; - const statusMatch = document.documentElement.innerHTML.match(/SNOWMAN_STATUS = (.*?\});/); - return { - articleContent, - statusData: statusMatch ? statusMatch[1] : null, - }; - }); - - if (content.statusData) { - const data = JSON.parse(content.statusData); - item.text = data.text; - } - - const retweetedStatus = item.retweeted_status ? `
    ${item.retweeted_status.user.screen_name}: ${item.retweeted_status.description}
    ` : ''; - const description = content.articleContent || item.description + retweetedStatus; - - return { - title: item.title || sanitizeHtml(description, { allowedTags: [], allowedAttributes: {} }), - description: item.text ? item.text + retweetedStatus : description, - pubDate: parseDate(item.created_at), - link: rootUrl + item.target, - }; - } catch (error: unknown) { - if (error instanceof Error && !error.message?.includes('ERR_ABORTED')) { - throw error; - } - const retweetedStatus = item.retweeted_status ? `
    ${item.retweeted_status.user.screen_name}: ${item.retweeted_status.description}
    ` : ''; - const description = item.description + retweetedStatus; - - return { - title: item.title || sanitizeHtml(description, { allowedTags: [], allowedAttributes: {} }), - description: item.description, - pubDate: parseDate(item.created_at), - link: rootUrl + item.target, - }; - } - }) - ) - ); - - const extractProfileImage = (user: any): string | undefined => { - if (!user?.profile_image_url || !user?.photo_domain) { - return undefined; - } - - const imageUrls = user.profile_image_url.split(',').filter(Boolean); - if (imageUrls.length === 0) { - return undefined; - } - - // Priority order for image sizes - const sizePriority = ['!180x180.png', '!50x50.png', '!30x30.png']; - - const selectedImageUrl = sizePriority.map((size) => imageUrls.find((url) => url.includes(size))).find(Boolean) || imageUrls[0]; - - const baseDomain = user.photo_domain.startsWith('//') ? `https:${user.photo_domain}` : user.photo_domain; - - return `${baseDomain}${selectedImageUrl}`; - }; - - const profileImage = extractProfileImage(data[0].user); - - return { - title: `${data[0].user.screen_name} 的雪球${typename[type]}动态`, - link, - description: `${data[0].user.screen_name} 的雪球${typename[type]}动态`, - image: profileImage, - item: items, - }; - } finally { - await context.close(); - } + }, + }); + + const data = response.statuses.filter((s) => s.mark !== 1); // 去除置顶动态 + + const items = await Promise.all( + data.map((item) => + cache.tryGet(item.target, async () => { + // legal_user_visible 为 true 时列表已含完整内容,无需再请求详情 + if (item.legal_user_visible) { + return buildListItem(item); + } + + try { + const detail = await ofetch(`${apiUrl}/statuses/show.json`, { + query: { + id: item.id, + }, + headers: { + Cookie: cookie, + Referer: link, + }, + }); + + return { + title: buildTitle(item, detail), + description: buildDescription(detail), + pubDate: parseDate(item.created_at), + link: rootUrl + item.target, + }; + } catch { + return buildListItem(item); + } + }) + ) + ); + + const user = data[0]?.user; + + return { + title: `${user?.screen_name ?? id} 的雪球${typename[type]}动态`, + link, + description: `${user?.screen_name ?? id} 的雪球${typename[type]}动态`, + image: extractProfileImage(user), + item: items, + allowEmpty: true, + }; } From bf8e64e4844130b2661ebb9e07f86e18b7a4ef81 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 12 Jul 2026 21:48:17 +0800 Subject: [PATCH 0293/1190] fix(jihs/idwr): fix URL (#22685) --- lib/routes/go/jihs/idwr.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/routes/go/jihs/idwr.ts b/lib/routes/go/jihs/idwr.ts index c7e7cf476f05..08f6ed243086 100644 --- a/lib/routes/go/jihs/idwr.ts +++ b/lib/routes/go/jihs/idwr.ts @@ -13,7 +13,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://id-info.jihs.go.jp'; - const targetUrl: string = new URL(`surveillance/idwr/jp/idwr/${year}/`, baseUrl).href; + const targetUrl: string = new URL(`surveillance/idwr/idwr/${year}/`, baseUrl).href; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); @@ -93,7 +93,7 @@ export const route: Route = { }, }, description: `::: tip -To subscribe to [感染症発生動向調査週報](https://id-info.jihs.go.jp/surveillance/idwr/jp/idwr/2025/), where the source URL is \`https://id-info.jihs.go.jp/surveillance/idwr/jp/idwr/2025/\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/go/jihs/idwr/2025\`](https://rsshub.app/go/jihs/idwr/2025). +To subscribe to [感染症発生動向調査週報](https://id-info.jihs.go.jp/surveillance/idwr/idwr/2025/), where the source URL is \`https://id-info.jihs.go.jp/surveillance/idwr/idwr/2025/\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/go/jihs/idwr/2025\`](https://rsshub.app/go/jihs/idwr/2025). :::`, categories: ['government'], features: { @@ -107,7 +107,7 @@ To subscribe to [感染症発生動向調査週報](https://id-info.jihs.go.jp/s }, radar: [ { - source: ['id-info.jihs.go.jp/surveillance/idwr/jp/idwr/:year'], + source: ['id-info.jihs.go.jp/surveillance/idwr/idwr/:year'], target: (params) => { const year: string = params.year; @@ -130,7 +130,7 @@ To subscribe to [感染症発生動向調査週報](https://id-info.jihs.go.jp/s }, }, description: `::: tip -若订阅 [传染病发生动向调查周报](https://id-info.jihs.go.jp/surveillance/idwr/jp/idwr/2025/),网址为 \`https://id-info.jihs.go.jp/surveillance/idwr/jp/idwr/2025/\`,请截取 \`https://id-info.jihs.go.jp/surveillance/idwr/jp/idwr/\` 到末尾 \`/\` 的部分 \`2025\` 作为 \`year\` 参数填入,此时目标路由为 [\`/go/jihs/idwr/2025\`](https://rsshub.app/go/jihs/idwr/2025)。 +若订阅 [传染病发生动向调查周报](https://id-info.jihs.go.jp/surveillance/idwr/idwr/2025/),网址为 \`https://id-info.jihs.go.jp/surveillance/idwr/idwr/2025/\`,请截取 \`https://id-info.jihs.go.jp/surveillance/idwr/idwr/\` 到末尾 \`/\` 的部分 \`2025\` 作为 \`year\` 参数填入,此时目标路由为 [\`/go/jihs/idwr/2025\`](https://rsshub.app/go/jihs/idwr/2025)。 :::`, }, }; From 91cd1293811ca2e0029838a408cfe9938662ea85 Mon Sep 17 00:00:00 2001 From: sirius60111 Date: Mon, 13 Jul 2026 13:43:03 +0800 Subject: [PATCH 0294/1190] fix(route/xueqiu): fetch today hot list via api.xueqiu.com to bypass WAF (#22689) The /xueqiu/today route requested statuses/hot/listV2.json on xueqiu.com, which the Alibaba Cloud WAF blocks (returns a challenge page instead of JSON), so response.data.items was undefined and the route threw 503. Switch the endpoint to api.xueqiu.com, which serves the same JSON without the WAF challenge. Also mark antiCrawler: true. Co-authored-by: Claude Opus 4.8 (1M context) --- lib/routes/xueqiu/today.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/xueqiu/today.ts b/lib/routes/xueqiu/today.ts index 4a6b88ecbce1..fe57fac6bb24 100644 --- a/lib/routes/xueqiu/today.ts +++ b/lib/routes/xueqiu/today.ts @@ -14,7 +14,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: false, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, @@ -35,7 +35,7 @@ async function handler(ctx) { const rootUrl = 'https://xueqiu.com'; const currentUrl = `${rootUrl}/today`; - const apiUrl = `${rootUrl}/statuses/hot/listV2.json?since_id=-1&size=${size}`; + const apiUrl = `https://api.xueqiu.com/statuses/hot/listV2.json?since_id=-1&size=${size}`; const token = await parseToken(currentUrl); const response = await got({ From 4436842668489d9953e5d1981a7e385490634a34 Mon Sep 17 00:00:00 2001 From: Andvari <31068367+dzx-dzx@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:17:16 +0800 Subject: [PATCH 0295/1190] fix(route/cw): Use browser to fetch full text. (#22292) * fix(route/cw): Use browser to fetch full text. * . * . * Update utils.ts * Update lib/routes/cw/utils.ts Co-authored-by: Tony * Update lib/routes/cw/utils.ts Co-authored-by: Tony * Update lib/routes/cw/utils.ts Co-authored-by: Tony ------- --- lib/routes/cw/utils.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/routes/cw/utils.ts b/lib/routes/cw/utils.ts index 514064e458ee..2e68825e1d13 100644 --- a/lib/routes/cw/utils.ts +++ b/lib/routes/cw/utils.ts @@ -1,9 +1,8 @@ import { load } from 'cheerio'; +import type { BrowserContext } from 'patchright'; -import { config } from '@/config'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; -import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import { getCookies, setCookies } from '@/utils/playwright-utils'; @@ -50,7 +49,7 @@ const getCookie = async (context) => { return cookie; }; -const parsePage = async (path, context, ctx) => { +const parsePage = async (path, context: BrowserContext, ctx) => { const pageUrl = `${baseUrl}${pathMap[path].pageUrl(ctx.req.param('channel'))}`; const cookie = await getCookie(context); @@ -89,19 +88,23 @@ const parseList = ($, limit) => }) .slice(0, limit); -const parseItems = (list, context) => +const parseItems = (list, context: BrowserContext) => Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const response = await ofetch(item.link, { - headers: { - Cookie: await getCookie(context), - 'User-Agent': config.ua, - }, + const page = await context.newPage(); + await page.route('**/*', (route) => { + const request = route.request(); + request.resourceType() === 'document' || request.resourceType() === 'script' ? route.continue() : route.abort(); }); + await page.goto(item.link, { + waitUntil: 'domcontentloaded', + }); + const response = await page.evaluate(() => document.documentElement.innerHTML); + await page.close(); const $ = load(response); - const meta = JSON.parse($('head script[type="application/ld+json"]').eq(0).text()); + const meta = JSON.parse($('head script[type="application/ld+json"]:contains("NewsArticle")').first().text()); $('.article__head .breadcrumb, .article__head h1, .article__provideViews, .ad').remove(); $('img.lazyload').each((_, img) => { if (!img.attribs['data-src']) { @@ -115,7 +118,7 @@ const parseItems = (list, context) => item.title = $('head title').text(); item.category = $('meta[name=keywords]').attr('content').split(','); item.pubDate = parseDate(meta.datePublished); - item.author = meta.author.name.replace(',', ' ') || meta.publisher.name; + item.author = Array.isArray(meta.author) ? meta.author : meta.author.name; item.description = $('.article__head .container').html() + $('.article__content').html(); return item; From efb8568f5eb112c0da68eba92c7932cfd9cf6296 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:20:11 +0000 Subject: [PATCH 0296/1190] chore(deps-dev): bump tsdown from 0.22.4 to 0.22.7 (#22696) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.4 to 0.22.7. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.4...v0.22.7) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.7 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 376 +++++++++++++++++++++++++------------------------ 2 files changed, 195 insertions(+), 183 deletions(-) diff --git a/package.json b/package.json index 6b4370fa0f7b..43f9b400d1c6 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.4", + "tsdown": "0.22.7", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a0b8a447e25..fc9cfd37c42d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -453,8 +453,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.4 - version: 0.22.4(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)) + specifier: 0.22.7 + version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -1765,9 +1765,6 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxc-project/types@0.138.0': - resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} - '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} @@ -2116,8 +2113,8 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.1.4': - resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==} + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -2128,8 +2125,8 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.1.4': - resolution: {integrity: sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==} + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -2140,8 +2137,8 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.1.4': - resolution: {integrity: sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==} + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -2152,8 +2149,8 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.1.4': - resolution: {integrity: sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==} + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -2164,8 +2161,8 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.1.4': - resolution: {integrity: sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==} + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -2177,8 +2174,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.1.4': - resolution: {integrity: sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==} + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2191,8 +2188,8 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.1.4': - resolution: {integrity: sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==} + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2205,8 +2202,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-ppc64-gnu@1.1.4': - resolution: {integrity: sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==} + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -2219,8 +2216,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.1.4': - resolution: {integrity: sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==} + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -2233,8 +2230,8 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.1.4': - resolution: {integrity: sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==} + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2247,8 +2244,8 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-musl@1.1.4': - resolution: {integrity: sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==} + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2260,8 +2257,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.1.4': - resolution: {integrity: sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==} + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -2271,8 +2268,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.1.4': - resolution: {integrity: sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==} + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] @@ -2282,8 +2279,8 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.1.4': - resolution: {integrity: sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==} + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -2294,8 +2291,8 @@ packages: cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.1.4': - resolution: {integrity: sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==} + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2944,125 +2941,125 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.5.44': - resolution: {integrity: sha512-mpZc7hrjl/qxcbEMS6vVLE0lO4DjiXCvrp3gYbZ58bbmsSxSGCTlOCA0lpeLXAKOZxe0ZrVoqKteaewFF2Vbuw==} + '@yuku-codegen/binding-darwin-arm64@0.6.1': + resolution: {integrity: sha512-LDJtpOKtcv9f3V0eDUwFmmy47t2VC+DAuN+gq80R1IA+fa0d408i6sHsVtt6n+g5rf8f86ySoPSAe94lHt6Ixw==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.5.44': - resolution: {integrity: sha512-PCt776PnGtnQYimxk18IyvPf/BQ6CNU95W+6eaoQfeME8ra+7v3pNNoTAmLfSveUC9KZPaoajR9NqkKfEVjA2Q==} + '@yuku-codegen/binding-darwin-x64@0.6.1': + resolution: {integrity: sha512-fBwpBOh33W7N87F94SVNExwm2KUV3ROhk51okr3Oy2ost1/JJuBWINVjcgwd2WPKZEFzUXgCzj/03UR/G+WIrQ==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.5.44': - resolution: {integrity: sha512-5MNu1R4ysytPLMdl1UlGyjgAbUdT8fguW/QRo+pyEymbuWRN5n8JEHCFpm4AsWdP61fStagSpPDJcwN+mwC9Lg==} + '@yuku-codegen/binding-freebsd-x64@0.6.1': + resolution: {integrity: sha512-UpMkskQV3a5oPnJV+GFFupIqnLwSBD4ZsZAVUZuNVkrqct433FHKqTwuG+P5JhHZbmhf+++3Ie/V2sgduyXrAQ==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.5.44': - resolution: {integrity: sha512-xbP2qQ7/h6ZZKeckCiI2osB5SE5PBfLb4uzIfO1BSTX+9rlBKTqomxDaCib7aPf2X1CXMiIq+i7AK1EhBa5a7A==} + '@yuku-codegen/binding-linux-arm-gnu@0.6.1': + resolution: {integrity: sha512-HICjDelfEDeD6TD8OEz/Dvt8KHxJiETR+paI/Fr7eVTQbjMfRrXJz8O1qV1qGH5SHZUGl2SAw2Rp+MLtXOjCrQ==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.5.44': - resolution: {integrity: sha512-gmXKMCpgkUm5PllGMKBMoBmqgbTQkx+S85eE46LctuwTSKS/K7u65NE6t4zk6cLDvZpV67enycKXBORWn6q7xA==} + '@yuku-codegen/binding-linux-arm-musl@0.6.1': + resolution: {integrity: sha512-pUswnwa+WOmtH2ZGOWL05kFLMNY7/TnEAryfIv1yVFzKQnmSy9TKYi3oIOxGZL3w+cdUKCZ6Q+jaD0oI10ztzA==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.5.44': - resolution: {integrity: sha512-eOoejNUtnHs1/TMn4wryOAG/TarvlOqSZtRPeS56liBKp/GVQSirqTM9AITSGPLSdK1u7fZWMdj5IOEoJp3ruw==} + '@yuku-codegen/binding-linux-arm64-gnu@0.6.1': + resolution: {integrity: sha512-Zro0FOu9clLCmqCnUKzEWHAu30tss0iEfhs+KDXm9Dpm1FkIHAKu43tF6FQU2hsTA7a8xd93NGddzc2EOJFKUg==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.5.44': - resolution: {integrity: sha512-xTzrhEMy1mdv5+SbJPPlwqlMrhXGPntE2f12TLi+dNPXhif4iXCGJpoh8pV9nwZFE/cq1ITuTnjIfTFj/PifzA==} + '@yuku-codegen/binding-linux-arm64-musl@0.6.1': + resolution: {integrity: sha512-NZaT+mp9toqWuFEA4MYW5HMRxgIa8DCqnTTnM5SrrojZgm4QoMI/mJfifVet1ZHgl/Dly5m6H6GPpq43uXVj8g==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.5.44': - resolution: {integrity: sha512-2bvgIU4P+f4q18grdHoGnt9L6XlAciq/8GWpM06fmwj8qqruS8qwwwZXBk3/0U2+IHGv9pXRR8GtSXzl5n/blg==} + '@yuku-codegen/binding-linux-x64-gnu@0.6.1': + resolution: {integrity: sha512-M5macseSCBPvJ4yfKNyQpMc7nBQBtj39MNfMt0r+8UkTnR5qJE00JJx06puHgPxT5hnGxMAuAWZ+3a9H2ngqAw==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.5.44': - resolution: {integrity: sha512-EzX5hWK0MmU4fbqY9coc5PJ0y0LYjrBdAF7mjSyetdK/yWRGBfHi9nh0dUJ0lqb47653hxB8/r9byfYgg6ecbA==} + '@yuku-codegen/binding-linux-x64-musl@0.6.1': + resolution: {integrity: sha512-liAyBZI5AbazZGeeNfWj0jCD/TE2L84hgVYh4KkjJA/N9bNzFQCDf4BvWP76nEO89r2tIGEUjbXdM4mM26riHg==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.5.44': - resolution: {integrity: sha512-/ajGFWcOLGtmVUdxWKXDW6Ixtez68xxtmd2l95xNg8Lzs4aqbV2cy0Ns2Bzg9vjHsgDIooQlLXHpWh6HsHcy6w==} + '@yuku-codegen/binding-win32-arm64@0.6.1': + resolution: {integrity: sha512-qItzfH3x6MYChPeGfvh22rHD92WLgXQRSuwvspRVSnLvpnubEfZd+9REPRQVT2l9fIuETDCEkDNRqkDROQTkgA==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.5.44': - resolution: {integrity: sha512-geuJQ7FKI8YUtBgW8w72ChMfMvYy3gSytACxB4HOkylsoutrhH6lUNYHk1ny/p8u8t47NGxktpnzVJzI8IzJkA==} + '@yuku-codegen/binding-win32-x64@0.6.1': + resolution: {integrity: sha512-DFFkKROZ9ZAHmFMUFRtRTkosZds1KH8BOx5t3UpNULIjT3iuUmEx9V5pWR0xOi66sANY38Ap77nz1kOZraQxEg==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.5.44': - resolution: {integrity: sha512-EXSW5w1YOIMyxu53MFxP43gTDeHlQueOjk8AEI9tuLF5976bDq3MXuIgzQvZKPVAirxGZu8+ANVXH1WcAH1G6A==} + '@yuku-parser/binding-darwin-arm64@0.6.1': + resolution: {integrity: sha512-jORysyRZg5zGDgVw15LGMsjZDh7jwjpUIJRBHgFt0ir15O5pEazfvuF2dnwvrJiTF0IT1EgHAVbTAYJWwQLCjg==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.5.44': - resolution: {integrity: sha512-pJBHsKxqR/nPwwaXgkkYTVo3G9dJ/AXhWsYyKyadU1zLg9gGfVwVTMehqwwutMCONDsLqOmEv/UqItmhpVsQJw==} + '@yuku-parser/binding-darwin-x64@0.6.1': + resolution: {integrity: sha512-dTeYFkkFlbP/WCB2DtezXas3NApOPtFlXSdssB7wGtY9wpNp4HAkVo1KBwI5mcHK0e2joyUcqTSf44mFE+q7vg==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.5.44': - resolution: {integrity: sha512-fOqYX5BQML94uj9hd3a+LlBNz54OoDm6l+wgUIZ8UUkOBfPV+w2lux1jj9FKXT85wwjDOFhgZ/XQYSEDK/N9mA==} + '@yuku-parser/binding-freebsd-x64@0.6.1': + resolution: {integrity: sha512-GExDp3rebo28mt3EAjvKQs0ZC3gkznAErV0I9TUNDa9muuhvD35kft61Mpsc6+NWeE+BG+kUyKbm6iO5B6ZMMA==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.5.44': - resolution: {integrity: sha512-kA2jRNh2cbbHDbhBN9IR8CnYI4UZHNQg0OVz6+V16Ph5VlLYpfv/6GdCmvQQoTGAnhFlbQiRoQys03ey91FTew==} + '@yuku-parser/binding-linux-arm-gnu@0.6.1': + resolution: {integrity: sha512-a9MjABj4J0VE3Z2oROGhmeddZZrhwwrnl4ZWZOuHUhD/smDtDiNtr0LpCbKB7rEYaQ29snopOPdZ/0T3YgLglQ==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.5.44': - resolution: {integrity: sha512-TQ82L8c5Lxl3HBOmw0uGfCcsyw0mp7DVGwCuW24OdDWak6BKaumvB8/Ep1llPOvhk5xgSFB7fsqgh6sIwkNRew==} + '@yuku-parser/binding-linux-arm-musl@0.6.1': + resolution: {integrity: sha512-ctuvXJgDRKKlmJfHxT4RsTvcAcHEFNJHTCsGbtt4rluQpVDc+ezk9JvQ534ehoIfZ9T0eIHSBgqYAZ4xCatNmQ==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.5.44': - resolution: {integrity: sha512-o6up+m/MsoMEtq6h4JTzmzKOvH9o3o41vK8oiok0LOZoPOFOqOSqC+N5V2ArD1O54m5LUq6ErghAbGWiPsMsqg==} + '@yuku-parser/binding-linux-arm64-gnu@0.6.1': + resolution: {integrity: sha512-vRtyoTtT0Ltowuh9LWOl/ZNU9h79J89ilOz5SEGspcw0jfhoUt19i07VNitll4jjfg5p2EN00q+MqX0pAobrFw==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.5.44': - resolution: {integrity: sha512-3XZoiHhtrjWKgk7CJC/sGzk1TE57SDPYzOFXUg6CZrigRZ+c0Q5ItzJpyAvhzlxv5ruNRYiuGvFbaRSIDnp2BA==} + '@yuku-parser/binding-linux-arm64-musl@0.6.1': + resolution: {integrity: sha512-vCsc3GOe1ylmRyfo/WLjIhjiCmaTtJbWNF4ZtgjNegDjpsRsFuCcP9duXB41QcfnJK38repKVFqFh0LR3l48FA==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.5.44': - resolution: {integrity: sha512-7KYCySZI6cjsdeiiIwvviDfJFd4Xj5gjNBzm9akUpwxRXNwaHHuEq2yGkVdGqj3BT6dJsiNhJIhtS6k7/HJdFA==} + '@yuku-parser/binding-linux-x64-gnu@0.6.1': + resolution: {integrity: sha512-gw3d81RdUHSYwjDW2IG6gEtm4VDoPP4ZOqpuC6Nc8+UBfos+4gTWOgzmuxIOVhkSV2fJCcUDpSJIlPzEU0FLZw==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.5.44': - resolution: {integrity: sha512-SLU/nXwOjET2XlOiO984sAzCloiWw0+JdcWpGzXLz3JQWbdiblxWC9cIvB0fCtKdDhX1mIutKNEH+RRRJADYcQ==} + '@yuku-parser/binding-linux-x64-musl@0.6.1': + resolution: {integrity: sha512-nzU+Doq9UgZvYYvald36lZJ2Neeyheije6WE/YpoFt/oJiNmnjArRgr2CMtb/7gWBl80YSMwcHK4Ju0E+7wfWg==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.5.44': - resolution: {integrity: sha512-LGmaJtB2mVqPD7Gu/RKAu9aIVxlaKPOgKB8RPHeFFD5Tf/apCiWZix1tkBjdOAo9cVEAoR9qnVHE6zJ+OG0drQ==} + '@yuku-parser/binding-win32-arm64@0.6.1': + resolution: {integrity: sha512-r3tXFVDliWCPe7TL6DVxUkT4rkqnXyeFVSEDf+V9My6Gztq99/gIe3POQqFbshTRuSrpEYMGMbGxeFh+m+stxA==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.5.44': - resolution: {integrity: sha512-2i0FTklLuhBIgILvtEQ3IN3J4qaTMKlxptseWNrz4F5mimL9UpQFUniVju9OOInwP2O1vgLdZn8EBjEXGNCHmg==} + '@yuku-parser/binding-win32-x64@0.6.1': + resolution: {integrity: sha512-FqYMOqeCS2XTdn5yvaKlOhtSQ84mVO3aTXp6LGfMd9Zq8RsV4H8qLWv+sxJsgPCXfuBV64u8/f+CTr3uIwNLWA==} cpu: [x64] os: [win32] @@ -4886,6 +4883,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5209,6 +5211,10 @@ packages: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -5422,14 +5428,14 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.1: - resolution: {integrity: sha512-s1HLtZfsXcLsBrcqW6nmIcTqkBZd7Y9srgL9SMS2hNvl37US5jFl193d5ki2w9Ei5myP7ERLVlQX2o6ZTL/AQQ==} + rolldown-plugin-dts@0.27.8: + resolution: {integrity: sha512-IjBTFpkrYYJPRUmIjUJFsZdR1zeHskFcEwFDzSfFDoC2pZcSNgLrBUcDFY9K0Q+A1TZJR3q9MlVomxMDP8AuLQ==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@ts-macro/tsc': ^0.3.6 '@typescript/native-preview': '>=7.0.0-dev.20260325.1' rolldown: ^1.0.0 - typescript: ^5.0.0 || ^6.0.0 + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: '@ts-macro/tsc': @@ -5446,8 +5452,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.1.4: - resolution: {integrity: sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==} + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -5825,18 +5831,18 @@ packages: typescript: optional: true - tsdown@0.22.4: - resolution: {integrity: sha512-3a5FsNL2fH2jw3ozvFUuPMBgS0xXjX9wpZShHyB4klXelVhyaNw5Q5WA9TPCNeoGYpRZEc4OZdMx5wT4Fkma3A==} + tsdown@0.22.7: + resolution: {integrity: sha512-4egbOzc9dxVv/QS+gDV75FIxDIjQQeOnXBlUuikyjmn0ozuc6FW11djJjEEo3vqkuJRygpnKHurnj+Iwftk4VA==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.4 - '@tsdown/exe': 0.22.4 + '@tsdown/css': 0.22.7 + '@tsdown/exe': 0.22.7 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' - typescript: ^5.0.0 || ^6.0.0 + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 unplugin-unused: ^0.5.0 unrun: '*' peerDependenciesMeta: @@ -6356,11 +6362,11 @@ packages: yuku-ast@0.1.7: resolution: {integrity: sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA==} - yuku-codegen@0.5.44: - resolution: {integrity: sha512-0rhtgWGz+bR3Pe7xqJ5E4VqxI1vqNtkeJRkeXM0Qd3tgldYClQipxa9bRZyuNOOfk0Ri02scrjnkoAHM16/f2g==} + yuku-codegen@0.6.1: + resolution: {integrity: sha512-6RJqqON2xYhMEp/sZv5oOSI3uOpWwRwzAi2fc/rMcRFjcqedAC5Fyp4AD9Vn2b8SB7hf9ESqVW+YwbDs/KvyKA==} - yuku-parser@0.5.44: - resolution: {integrity: sha512-mAhpQZ/bXjxZmKiGUqEWskC9mZTcTBv6/fdzVdzdjM6XuD1DP3IavdLVWdM39L9ewK9vS9OtJmaKNeWgRzpy0w==} + yuku-parser@0.6.1: + resolution: {integrity: sha512-dPE3/+H2VBw9LhjoIVeW/axKidYGd+XzNtrwGGseZ0325cQFl0Dpwyh0R74XWe/WqQn4M8CR5YApsv2KF2zN1A==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -7435,8 +7441,6 @@ snapshots: '@oxc-project/types@0.127.0': optional: true - '@oxc-project/types@0.138.0': {} - '@oxc-project/types@0.139.0': {} '@oxfmt/binding-android-arm-eabi@0.58.0': @@ -7637,73 +7641,73 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-android-arm64@1.1.4': + '@rolldown/binding-android-arm64@1.1.5': optional: true '@rolldown/binding-darwin-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-arm64@1.1.4': + '@rolldown/binding-darwin-arm64@1.1.5': optional: true '@rolldown/binding-darwin-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-x64@1.1.4': + '@rolldown/binding-darwin-x64@1.1.5': optional: true '@rolldown/binding-freebsd-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-freebsd-x64@1.1.4': + '@rolldown/binding-freebsd-x64@1.1.5': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-gnu@1.1.4': + '@rolldown/binding-linux-arm64-gnu@1.1.5': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-musl@1.1.4': + '@rolldown/binding-linux-arm64-musl@1.1.5': optional: true '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.1.4': + '@rolldown/binding-linux-ppc64-gnu@1.1.5': optional: true '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-s390x-gnu@1.1.4': + '@rolldown/binding-linux-s390x-gnu@1.1.5': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-gnu@1.1.4': + '@rolldown/binding-linux-x64-gnu@1.1.5': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-musl@1.1.4': + '@rolldown/binding-linux-x64-musl@1.1.5': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-openharmony-arm64@1.1.4': + '@rolldown/binding-openharmony-arm64@1.1.5': optional: true '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': @@ -7713,7 +7717,7 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-wasm32-wasi@1.1.4': + '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 @@ -7723,13 +7727,13 @@ snapshots: '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-arm64-msvc@1.1.4': + '@rolldown/binding-win32-arm64-msvc@1.1.5': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-x64-msvc@1.1.4': + '@rolldown/binding-win32-x64-msvc@1.1.5': optional: true '@rolldown/pluginutils@1.0.0-rc.17': @@ -8343,70 +8347,70 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.5.44': + '@yuku-codegen/binding-darwin-arm64@0.6.1': optional: true - '@yuku-codegen/binding-darwin-x64@0.5.44': + '@yuku-codegen/binding-darwin-x64@0.6.1': optional: true - '@yuku-codegen/binding-freebsd-x64@0.5.44': + '@yuku-codegen/binding-freebsd-x64@0.6.1': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.5.44': + '@yuku-codegen/binding-linux-arm-gnu@0.6.1': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.5.44': + '@yuku-codegen/binding-linux-arm-musl@0.6.1': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.5.44': + '@yuku-codegen/binding-linux-arm64-gnu@0.6.1': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.5.44': + '@yuku-codegen/binding-linux-arm64-musl@0.6.1': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.5.44': + '@yuku-codegen/binding-linux-x64-gnu@0.6.1': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.5.44': + '@yuku-codegen/binding-linux-x64-musl@0.6.1': optional: true - '@yuku-codegen/binding-win32-arm64@0.5.44': + '@yuku-codegen/binding-win32-arm64@0.6.1': optional: true - '@yuku-codegen/binding-win32-x64@0.5.44': + '@yuku-codegen/binding-win32-x64@0.6.1': optional: true - '@yuku-parser/binding-darwin-arm64@0.5.44': + '@yuku-parser/binding-darwin-arm64@0.6.1': optional: true - '@yuku-parser/binding-darwin-x64@0.5.44': + '@yuku-parser/binding-darwin-x64@0.6.1': optional: true - '@yuku-parser/binding-freebsd-x64@0.5.44': + '@yuku-parser/binding-freebsd-x64@0.6.1': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.5.44': + '@yuku-parser/binding-linux-arm-gnu@0.6.1': optional: true - '@yuku-parser/binding-linux-arm-musl@0.5.44': + '@yuku-parser/binding-linux-arm-musl@0.6.1': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.5.44': + '@yuku-parser/binding-linux-arm64-gnu@0.6.1': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.5.44': + '@yuku-parser/binding-linux-arm64-musl@0.6.1': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.5.44': + '@yuku-parser/binding-linux-x64-gnu@0.6.1': optional: true - '@yuku-parser/binding-linux-x64-musl@0.5.44': + '@yuku-parser/binding-linux-x64-musl@0.6.1': optional: true - '@yuku-parser/binding-win32-arm64@0.5.44': + '@yuku-parser/binding-win32-arm64@0.6.1': optional: true - '@yuku-parser/binding-win32-x64@0.5.44': + '@yuku-parser/binding-win32-x64@0.6.1': optional: true '@yuku-toolchain/types@0.5.43': {} @@ -10516,6 +10520,8 @@ snapshots: nanoid@3.3.15: {} + nanoid@3.3.16: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10881,6 +10887,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.18: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.48: dependencies: '@postman/form-data': 3.1.1 @@ -11122,15 +11134,15 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.1(@typescript/typescript6@6.0.2)(rolldown@1.1.4): + rolldown-plugin-dts@0.27.8(@typescript/typescript6@6.0.2)(rolldown@1.1.5): dependencies: dts-resolver: 3.0.0 get-tsconfig: 5.0.0-beta.5 obug: 2.1.3 - rolldown: 1.1.4 + rolldown: 1.1.5 yuku-ast: 0.1.7 - yuku-codegen: 0.5.44 - yuku-parser: 0.5.44 + yuku-codegen: 0.6.1 + yuku-parser: 0.6.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11158,26 +11170,26 @@ snapshots: '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 optional: true - rolldown@1.1.4: + rolldown@1.1.5: dependencies: - '@oxc-project/types': 0.138.0 + '@oxc-project/types': 0.139.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.4 - '@rolldown/binding-darwin-arm64': 1.1.4 - '@rolldown/binding-darwin-x64': 1.1.4 - '@rolldown/binding-freebsd-x64': 1.1.4 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.4 - '@rolldown/binding-linux-arm64-gnu': 1.1.4 - '@rolldown/binding-linux-arm64-musl': 1.1.4 - '@rolldown/binding-linux-ppc64-gnu': 1.1.4 - '@rolldown/binding-linux-s390x-gnu': 1.1.4 - '@rolldown/binding-linux-x64-gnu': 1.1.4 - '@rolldown/binding-linux-x64-musl': 1.1.4 - '@rolldown/binding-openharmony-arm64': 1.1.4 - '@rolldown/binding-wasm32-wasi': 1.1.4 - '@rolldown/binding-win32-arm64-msvc': 1.1.4 - '@rolldown/binding-win32-x64-msvc': 1.1.4 + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 rollup@4.62.2: dependencies: @@ -11587,7 +11599,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.4(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)): + tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11597,8 +11609,8 @@ snapshots: import-without-cache: 0.4.0 obug: 2.1.3 picomatch: 4.0.5 - rolldown: 1.1.4 - rolldown-plugin-dts: 0.27.1(@typescript/typescript6@6.0.2)(rolldown@1.1.4) + rolldown: 1.1.5 + rolldown-plugin-dts: 0.27.8(@typescript/typescript6@6.0.2)(rolldown@1.1.5) semver: 7.8.5 tinyexec: 1.2.4 tinyglobby: 0.2.17 @@ -11846,7 +11858,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.16 + postcss: 8.5.18 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: @@ -12114,37 +12126,37 @@ snapshots: dependencies: '@yuku-toolchain/types': 0.5.43 - yuku-codegen@0.5.44: + yuku-codegen@0.6.1: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.5.44 - '@yuku-codegen/binding-darwin-x64': 0.5.44 - '@yuku-codegen/binding-freebsd-x64': 0.5.44 - '@yuku-codegen/binding-linux-arm-gnu': 0.5.44 - '@yuku-codegen/binding-linux-arm-musl': 0.5.44 - '@yuku-codegen/binding-linux-arm64-gnu': 0.5.44 - '@yuku-codegen/binding-linux-arm64-musl': 0.5.44 - '@yuku-codegen/binding-linux-x64-gnu': 0.5.44 - '@yuku-codegen/binding-linux-x64-musl': 0.5.44 - '@yuku-codegen/binding-win32-arm64': 0.5.44 - '@yuku-codegen/binding-win32-x64': 0.5.44 - - yuku-parser@0.5.44: + '@yuku-codegen/binding-darwin-arm64': 0.6.1 + '@yuku-codegen/binding-darwin-x64': 0.6.1 + '@yuku-codegen/binding-freebsd-x64': 0.6.1 + '@yuku-codegen/binding-linux-arm-gnu': 0.6.1 + '@yuku-codegen/binding-linux-arm-musl': 0.6.1 + '@yuku-codegen/binding-linux-arm64-gnu': 0.6.1 + '@yuku-codegen/binding-linux-arm64-musl': 0.6.1 + '@yuku-codegen/binding-linux-x64-gnu': 0.6.1 + '@yuku-codegen/binding-linux-x64-musl': 0.6.1 + '@yuku-codegen/binding-win32-arm64': 0.6.1 + '@yuku-codegen/binding-win32-x64': 0.6.1 + + yuku-parser@0.6.1: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.5.44 - '@yuku-parser/binding-darwin-x64': 0.5.44 - '@yuku-parser/binding-freebsd-x64': 0.5.44 - '@yuku-parser/binding-linux-arm-gnu': 0.5.44 - '@yuku-parser/binding-linux-arm-musl': 0.5.44 - '@yuku-parser/binding-linux-arm64-gnu': 0.5.44 - '@yuku-parser/binding-linux-arm64-musl': 0.5.44 - '@yuku-parser/binding-linux-x64-gnu': 0.5.44 - '@yuku-parser/binding-linux-x64-musl': 0.5.44 - '@yuku-parser/binding-win32-arm64': 0.5.44 - '@yuku-parser/binding-win32-x64': 0.5.44 + '@yuku-parser/binding-darwin-arm64': 0.6.1 + '@yuku-parser/binding-darwin-x64': 0.6.1 + '@yuku-parser/binding-freebsd-x64': 0.6.1 + '@yuku-parser/binding-linux-arm-gnu': 0.6.1 + '@yuku-parser/binding-linux-arm-musl': 0.6.1 + '@yuku-parser/binding-linux-arm64-gnu': 0.6.1 + '@yuku-parser/binding-linux-arm64-musl': 0.6.1 + '@yuku-parser/binding-linux-x64-gnu': 0.6.1 + '@yuku-parser/binding-linux-x64-musl': 0.6.1 + '@yuku-parser/binding-win32-arm64': 0.6.1 + '@yuku-parser/binding-win32-x64': 0.6.1 zod@3.25.76: {} From 91c6a380761c0cf6c9954dd34fe53551f2e462b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:21:01 +0000 Subject: [PATCH 0297/1190] chore(deps): bump sanitize-html from 2.17.5 to 2.17.6 (#22695) Bumps [sanitize-html](https://github.com/apostrophecms/apostrophe/tree/HEAD/packages/sanitize-html) from 2.17.5 to 2.17.6. - [Changelog](https://github.com/apostrophecms/apostrophe/blob/main/packages/sanitize-html/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/apostrophe/commits/HEAD/packages/sanitize-html) --- updated-dependencies: - dependency-name: sanitize-html dependency-version: 2.17.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 60 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 43f9b400d1c6..532eeab2b0ae 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "re2js": "2.8.6", "rfc4648": "1.5.4", "rss-parser": "3.13.0", - "sanitize-html": "2.17.5", + "sanitize-html": "2.17.6", "simplecc-wasm": "1.1.1", "socks-proxy-agent": "10.1.0", "source-map": "0.7.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc9cfd37c42d..12c91279a315 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -221,8 +221,8 @@ importers: specifier: 3.13.0 version: 3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d) sanitize-html: - specifier: 2.17.5 - version: 2.17.5 + specifier: 2.17.6 + version: 2.17.6 simplecc-wasm: specifier: 1.1.1 version: 1.1.1 @@ -3615,6 +3615,10 @@ 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==} @@ -3640,6 +3644,10 @@ packages: 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-prop@6.0.1: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} @@ -4192,6 +4200,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'} + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} @@ -4878,8 +4890,8 @@ packages: nan@1.8.4: resolution: {integrity: sha512-609zQ1h3ApgH/94qmbbEklSrjcYYXCHnsWk4MAojq4OUk3tidhDYhPaMasMFKsZPZ96r4eQA1hbR2W4H7/77XA==} - nanoid@3.3.15: - resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -5207,8 +5219,8 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - postcss@8.5.16: - resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} postcss@8.5.18: @@ -5473,8 +5485,9 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - sanitize-html@2.17.5: - resolution: {integrity: sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg==} + sanitize-html@2.17.6: + resolution: {integrity: sha512-M4bo9tfv1yfhQZZKkc6dL07ALrGJtfvNOuhX3hU9AVPR/uPQ+nKOJBqTYc7LfMQblTW04mtSWDJWEyLvygJsLA==} + engines: {node: '>=22.12.0'} sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} @@ -8890,6 +8903,12 @@ 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: {} @@ -8918,6 +8937,12 @@ snapshots: 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-prop@6.0.1: dependencies: is-obj: 2.0.0 @@ -9604,6 +9629,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 + htmlparser2@6.1.0: dependencies: domelementtype: 2.3.0 @@ -10518,7 +10550,7 @@ snapshots: nan@1.8.4: optional: true - nanoid@3.3.15: {} + nanoid@3.3.16: {} nanoid@3.3.16: {} @@ -10881,9 +10913,9 @@ snapshots: pluralize@8.0.0: {} - postcss@8.5.16: + postcss@8.5.18: dependencies: - nanoid: 3.3.15 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -11233,15 +11265,15 @@ snapshots: safe-stable-stringify@2.5.0: {} - sanitize-html@2.17.5: + sanitize-html@2.17.6: dependencies: deepmerge: 4.3.1 escape-string-regexp: 4.0.0 - htmlparser2: 10.1.0 + htmlparser2: 12.0.0 is-plain-object: 5.0.0 launder: 1.7.1 parse-srcset: 1.0.2 - postcss: 8.5.16 + postcss: 8.5.18 sax@1.6.0: {} From 752c1ae54ab9438b6c0135013ef575b8c2cec213 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:25:19 +0000 Subject: [PATCH 0298/1190] chore(deps): bump imapflow from 1.4.6 to 1.4.7 (#22699) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.4.6 to 1.4.7. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.4.6...v1.4.7) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.4.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 532eeab2b0ae..b0d721157ff8 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.4.6", + "imapflow": "1.4.7", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12c91279a315..b7c7e7e954cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.4.6 - version: 1.4.6 + specifier: 1.4.7 + version: 1.4.7 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4285,8 +4285,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.4.6: - resolution: {integrity: sha512-rStOxV2g1WKKSkctKluUHSJB99mRT2l8Dr0W67owCHsCjqYSMY+AnvzXbaG3NGSpRziu6PWNik1/nVwL0Zlycg==} + imapflow@1.4.7: + resolution: {integrity: sha512-nK03WfN16inwm0//0Q70T21G0g4H9ArMVyzKDRjAshOVV8iw71PP9HTpAXNmWB9giZWfPiS+ltHByi37cqwSgg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -4900,6 +4900,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5227,6 +5232,10 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -9722,7 +9731,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.4.6: + imapflow@1.4.7: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 @@ -10554,6 +10563,8 @@ snapshots: nanoid@3.3.16: {} + nanoid@3.3.16: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10925,6 +10936,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.18: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.48: dependencies: '@postman/form-data': 3.1.1 From b405c486b3831d004b5d51d293f9824588ad241d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:25:55 +0000 Subject: [PATCH 0299/1190] chore(deps): bump hono from 4.12.28 to 4.12.30 (#22698) Bumps [hono](https://github.com/honojs/hono) from 4.12.28 to 4.12.30. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.28...v4.12.30) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.30 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 51 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index b0d721157ff8..1e1cd0615c6b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", "header-generator": "2.1.82", - "hono": "4.12.28", + "hono": "4.12.30", "html-to-text": "10.0.0", "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7c7e7e954cf..a2dd6cdc17d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,10 +48,10 @@ importers: version: 6.14.0 '@hono/node-server': specifier: 2.0.8 - version: 2.0.8(hono@4.12.28) + version: 2.0.8(hono@4.12.30) '@hono/zod-openapi': specifier: 1.4.0 - version: 1.4.0(hono@4.12.28)(zod@4.4.3) + version: 1.4.0(hono@4.12.30)(zod@4.4.3) '@jocmp/mercury-parser': specifier: 3.0.9 version: 3.0.9 @@ -84,7 +84,7 @@ importers: version: 0.0.25 '@scalar/hono-api-reference': specifier: 0.11.9 - version: 0.11.9(hono@4.12.28) + version: 0.11.9(hono@4.12.30) '@sentry/node': specifier: 10.64.0 version: 10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) @@ -128,8 +128,8 @@ importers: specifier: 2.1.82 version: 2.1.82 hono: - specifier: 4.12.28 - version: 4.12.28 + specifier: 4.12.30 + version: 4.12.30 html-to-text: specifier: 10.0.0 version: 10.0.0 @@ -4179,8 +4179,8 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.12.28: - resolution: {integrity: sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA==} + hono@4.12.30: + resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==} engines: {node: '>=16.9.0'} hookable@6.1.1: @@ -4905,6 +4905,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5236,6 +5241,10 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -6900,21 +6909,21 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.8(hono@4.12.28)': + '@hono/node-server@2.0.8(hono@4.12.30)': dependencies: - hono: 4.12.28 + hono: 4.12.30 - '@hono/zod-openapi@1.4.0(hono@4.12.28)(zod@4.4.3)': + '@hono/zod-openapi@1.4.0(hono@4.12.30)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) - '@hono/zod-validator': 0.8.0(hono@4.12.28)(zod@4.4.3) - hono: 4.12.28 + '@hono/zod-validator': 0.8.0(hono@4.12.30)(zod@4.4.3) + hono: 4.12.30 openapi3-ts: 4.6.0 zod: 4.4.3 - '@hono/zod-validator@0.8.0(hono@4.12.28)(zod@4.4.3)': + '@hono/zod-validator@0.8.0(hono@4.12.30)(zod@4.4.3)': dependencies: - hono: 4.12.28 + hono: 4.12.30 zod: 4.4.3 '@humanfs/core@0.19.2': @@ -7869,10 +7878,10 @@ snapshots: '@scalar/helpers@0.9.0': {} - '@scalar/hono-api-reference@0.11.9(hono@4.12.28)': + '@scalar/hono-api-reference@0.11.9(hono@4.12.30)': dependencies: '@scalar/client-side-rendering': 0.3.2 - hono: 4.12.28 + hono: 4.12.30 '@scalar/schemas@0.7.2': dependencies: @@ -9611,7 +9620,7 @@ snapshots: hmacsha1@1.0.0: {} - hono@4.12.28: {} + hono@4.12.30: {} hookable@6.1.1: {} @@ -10565,6 +10574,8 @@ snapshots: nanoid@3.3.16: {} + nanoid@3.3.16: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10942,6 +10953,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.18: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.48: dependencies: '@postman/form-data': 3.1.1 From c83f1cb29cf58503066c103be2f7d94a53fec0b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:26:41 +0000 Subject: [PATCH 0300/1190] chore(deps-dev): bump eslint-plugin-n from 18.2.1 to 18.2.2 (#22701) Bumps [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) from 18.2.1 to 18.2.2. - [Release notes](https://github.com/eslint-community/eslint-plugin-n/releases) - [Changelog](https://github.com/eslint-community/eslint-plugin-n/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint-community/eslint-plugin-n/compare/v18.2.1...v18.2.2) --- updated-dependencies: - dependency-name: eslint-plugin-n dependency-version: 18.2.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 35 ++++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 1e1cd0615c6b..0d6f3abcca08 100644 --- a/package.json +++ b/package.json @@ -177,7 +177,7 @@ "domhandler": "6.0.1", "eslint": "10.6.0", "eslint-nibble": "9.1.1", - "eslint-plugin-n": "18.2.1", + "eslint-plugin-n": "18.2.2", "eslint-plugin-regexp": "3.1.1", "eslint-plugin-simple-import-sort": "13.0.0", "eslint-plugin-unicorn": "71.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2dd6cdc17d5..b1c74e34b864 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -381,8 +381,8 @@ importers: specifier: 9.1.1 version: 9.1.1(@types/node@26.1.1)(eslint@10.6.0(jiti@2.6.1)) eslint-plugin-n: - specifier: 18.2.1 - version: 18.2.1(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)) + specifier: 18.2.2 + version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)) eslint-plugin-regexp: specifier: 3.1.1 version: 3.1.1(eslint@10.6.0(jiti@2.6.1)) @@ -3711,8 +3711,8 @@ packages: endpoint@0.4.5: resolution: {integrity: sha512-oA2ALUF+d4Y0I8/WMV/0BuAZGHxfIdAygr9ZXP4rfzmp5zpYZmYKHKAbqRQnrE1YGdPhVg4D24CQkyx2qYEoHg==} - enhanced-resolve@5.24.1: - resolution: {integrity: sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==} + enhanced-resolve@5.24.2: + resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -3810,8 +3810,8 @@ packages: peerDependencies: eslint: '>=8' - eslint-plugin-n@18.2.1: - resolution: {integrity: sha512-aO3C9//yq8JIvYOi/T+jPvcZ9hZzpwzbR8esrYpFtgE9vpbyM8kn42AQOtIqYspVmpaSWr8X+nrlQuAJYxXAaw==} + eslint-plugin-n@18.2.2: + resolution: {integrity: sha512-gOO0lIqwEjZ750kv9/SptCWArUoAZXJoBr0vYWTO2dCBxctHUXlBIigiC8xuxxr/NKqgIT6Ehz1xRcilj8a5cA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: '>=8.57.1' @@ -4910,6 +4910,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5245,6 +5250,10 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -9012,7 +9021,7 @@ snapshots: dependencies: inherits: 2.0.4 - enhanced-resolve@5.24.1: + enhanced-resolve@5.24.2: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -9161,10 +9170,10 @@ snapshots: eslint: 10.6.0(jiti@2.6.1) eslint-compat-utils: 0.5.1(eslint@10.6.0(jiti@2.6.1)) - eslint-plugin-n@18.2.1(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)): + eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) - enhanced-resolve: 5.24.1 + enhanced-resolve: 5.24.2 eslint: 10.6.0(jiti@2.6.1) eslint-plugin-es-x: 7.8.0(eslint@10.6.0(jiti@2.6.1)) get-tsconfig: 4.14.0 @@ -10576,6 +10585,8 @@ snapshots: nanoid@3.3.16: {} + nanoid@3.3.16: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10959,6 +10970,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.18: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.48: dependencies: '@postman/form-data': 3.1.1 From 7d71294f50e0fe1a8d6180daedfd16e06102dd69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:48:24 +0800 Subject: [PATCH 0301/1190] chore(deps): bump mitchellh/vouch/action/check-pr from 1.4.2 to 1.5.0 (#22691) Bumps [mitchellh/vouch/action/check-pr](https://github.com/mitchellh/vouch) from 1.4.2 to 1.5.0. - [Release notes](https://github.com/mitchellh/vouch/releases) - [Commits](https://github.com/mitchellh/vouch/compare/c6d80ead49839655b61b422700b7a3bc9d0804a9...d66fa29a64600490892131ad87597c30c91fcac4) --- updated-dependencies: - dependency-name: mitchellh/vouch/action/check-pr dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b73f0f124b90..d5912c5d9a35 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -81,7 +81,7 @@ jobs: timeout-minutes: 5 steps: - name: Check if PR author is denounced - uses: mitchellh/vouch/action/check-pr@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + uses: mitchellh/vouch/action/check-pr@d66fa29a64600490892131ad87597c30c91fcac4 # v1.5.0 with: pr-number: ${{ github.event.pull_request.number }} auto-close: true From dd197c7856c1762e0c973ad367bb6a337186189a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 08:50:03 +0000 Subject: [PATCH 0302/1190] style: auto format --- pnpm-lock.yaml | 1399 +++++++++++------------------------------------- 1 file changed, 312 insertions(+), 1087 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1c74e34b864..804924f74dd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,13 +304,13 @@ importers: version: 3.3.5 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.6.0(jiti@2.6.1)) + version: 10.0.1(eslint@10.6.0) '@oxlint/plugins': specifier: 1.73.0 version: 1.73.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.6.0(jiti@2.6.1)) + version: 5.10.0(eslint@10.6.0) '@types/babel__preset-env': specifier: 7.10.0 version: 7.10.0 @@ -358,13 +358,13 @@ importers: version: 2.16.1 '@typescript-eslint/eslint-plugin': specifier: 8.63.0 - version: 8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)))(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) + version: 8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0))(@typescript/typescript6@6.0.2)(eslint@10.6.0) '@typescript-eslint/parser': specifier: 8.63.0 - version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) + version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) '@vercel/nft': specifier: 1.10.2 - version: 1.10.2(rollup@4.62.2) + version: 1.10.2 '@vitest/coverage-v8': specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) @@ -376,25 +376,25 @@ importers: version: 6.0.1 eslint: specifier: 10.6.0 - version: 10.6.0(jiti@2.6.1) + version: 10.6.0 eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.1.1)(eslint@10.6.0(jiti@2.6.1)) + version: 9.1.1(@types/node@26.1.1)(eslint@10.6.0) eslint-plugin-n: specifier: 18.2.2 - version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)) + version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0) eslint-plugin-regexp: specifier: 3.1.1 - version: 3.1.1(eslint@10.6.0(jiti@2.6.1)) + version: 3.1.1(eslint@10.6.0) eslint-plugin-simple-import-sort: specifier: 13.0.0 - version: 13.0.0(eslint@10.6.0(jiti@2.6.1)) + version: 13.0.0(eslint@10.6.0) eslint-plugin-unicorn: specifier: 71.1.0 - version: 71.1.0(eslint@10.6.0(jiti@2.6.1)) + version: 71.1.0(eslint@10.6.0) eslint-plugin-yml: specifier: 3.6.0 - version: 3.6.0(eslint@10.6.0(jiti@2.6.1)) + version: 3.6.0(eslint@10.6.0) fast-string-width: specifier: 3.0.2 version: 3.0.2 @@ -454,7 +454,7 @@ importers: version: 11.0.0 tsdown: specifier: 0.22.7 - version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)) + version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -466,10 +466,10 @@ importers: version: 11.0.5 vite-tsconfig-paths: specifier: 6.1.1 - version: 6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + version: 6.1.1(@typescript/typescript6@6.0.2)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) wrangler: specifier: 4.110.0 version: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -694,344 +694,171 @@ packages: '@dabh/diagnostics@2.0.8': resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} - '@edge-runtime/primitives@4.1.0': - resolution: {integrity: sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==} - engines: {node: '>=16'} - - '@edge-runtime/vm@3.2.0': - resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==} - engines: {node: '>=16'} - - '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} - '@emnapi/wasi-threads@1.2.1': - resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.28.1': resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.28.1': resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.28.1': resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.28.1': resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.28.1': resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.28.1': resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.28.1': resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.28.1': resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.28.1': resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.28.1': resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.28.1': resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.28.1': resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.28.1': resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.28.1': resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.28.1': resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.28.1': resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.28.1': resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.28.1': resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.28.1': resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.28.1': resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.28.1': resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.28.1': resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.28.1': resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.28.1': resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.28.1': resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} @@ -1513,8 +1340,8 @@ packages: resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} - '@octokit/request@10.0.10': - resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} + '@octokit/request@10.0.11': + resolution: {integrity: sha512-+s7HUxjfFqOMS9VlIwDffq0MikjSAK0gSpG73W+meAvVAvX4MBrHYTK5Bj3Uot55qFT4gzUtfzE4mGWY4Br8/Q==} engines: {node: '>= 20'} '@octokit/types@16.0.0': @@ -1762,9 +1589,6 @@ packages: cpu: [x64] os: [win32] - '@oxc-project/types@0.127.0': - resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} @@ -2053,10 +1877,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.10': - resolution: {integrity: sha512-x6fFWCeak8aCGfqZfe6CXYt5xVjxe9Os1cIPmVRcToInKLjhJkRVXvJ/L3/1KxFkjDQdbZV/YsuLKqa8t/xKpA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@poppinss/colors@4.1.6': resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} @@ -2101,79 +1921,42 @@ packages: '@protobufjs/pool@1.1.0': resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - '@protobufjs/utf8@1.1.1': - resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rolldown/binding-android-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - '@rolldown/binding-android-arm64@1.1.5': resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - '@rolldown/binding-darwin-arm64@1.1.5': resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.17': - resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - '@rolldown/binding-darwin-x64@1.1.5': resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': - resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - '@rolldown/binding-freebsd-x64@1.1.5': resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': - resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.1.5': resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-arm64-gnu@1.1.5': resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2181,13 +1964,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rolldown/binding-linux-arm64-musl@1.1.5': resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2195,13 +1971,6 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-ppc64-gnu@1.1.5': resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2209,13 +1978,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.1.5': resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2223,13 +1985,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': - resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.1.5': resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2237,13 +1992,6 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': - resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - '@rolldown/binding-linux-x64-musl@1.1.5': resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2251,55 +1999,29 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': - resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.1.5': resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': - resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.1.5': resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.1.5': resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - '@rolldown/binding-win32-x64-msvc@1.1.5': resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.17': - resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} - '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -2312,144 +2034,6 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.62.2': - resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.62.2': - resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.62.2': - resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.62.2': - resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.62.2': - resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.62.2': - resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.62.2': - resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.62.2': - resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.62.2': - resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.62.2': - resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.62.2': - resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.62.2': - resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.62.2': - resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.62.2': - resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.62.2': - resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.62.2': - resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.62.2': - resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.62.2': - resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.62.2': - resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.62.2': - resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.62.2': - resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} - cpu: [x64] - os: [win32] - '@rss3/api-core@0.0.25': resolution: {integrity: sha512-YVH1QwF4P4r1JCYkkfK+UrKsa2PlJCOjViKhybRUanyR8TwwebpFPV+f5rcDDLRISlCdvhErpiFk9TntstYiCw==} @@ -2612,8 +2196,8 @@ packages: '@types/etag@1.8.4': resolution: {integrity: sha512-f1z/UMth8gQ6636NBqhFmJ3zES7EuDcUnV6K1gl1osHp+85KPKX+VixYWUpqLkw1fftCagyHJjJOZjZkEi2rHw==} - '@types/express-serve-static-core@5.1.1': - resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + '@types/express-serve-static-core@5.1.2': + resolution: {integrity: sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==} '@types/express@5.0.6': resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} @@ -2749,10 +2333,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.62.0': - resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.63.0': resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3194,8 +2774,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.42: - resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -3237,8 +2817,8 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.15: - resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} brace-expansion@2.1.2: resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} @@ -3247,13 +2827,8 @@ packages: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} - browserslist@4.28.4: - resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - browserslist@4.28.5: - resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3302,11 +2877,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001802: - resolution: {integrity: sha512-vmv8ub2xwTNmljSKf82mtCk5JH7hC+YgzLj3P5zotvA0tPQ9016tdNNOG8WRca1IxOnhSsivB+J0z5FeE5LOUw==} - - caniuse-lite@1.0.30001803: - resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} + caniuse-lite@1.0.30001805: + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -3603,7 +3175,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, integrity: sha512-OG2C++3YYIzflFjA9irnexBs9eUS6KUOViAssV4oSo4W8HAl86TV+DoFYhdSXVaFd4z5htsM3zzs64WJybH4cQ==, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -3679,11 +3251,8 @@ packages: engines: {node: '>=20'} hasBin: true - electron-to-chromium@1.5.387: - resolution: {integrity: sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ==} - - electron-to-chromium@1.5.388: - resolution: {integrity: sha512-Pl/aJaqOOxYxda3vcx1IKSJimwYXHDkEnGn0F+kG2EE68dDtx2uCinaS+Vih8Z91B9t8CSAbiF/HKyWcnXjhzw==} + electron-to-chromium@1.5.389: + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3741,8 +3310,12 @@ packages: error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - es-module-lexer@2.3.0: - resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} @@ -3758,11 +3331,6 @@ packages: es6-weak-map@2.0.3: resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} @@ -4276,8 +3844,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} image-size@0.7.5: @@ -4450,10 +4018,6 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - js-beautify@2.0.3: resolution: {integrity: sha512-cyFbh3tkPhknnTD/0bLf0T0yy2ZIbqL05mttzbt4y1Zfr7NxqXQZ62dkBLKs3oHH/lpjmDRAnciJiSUyOy8XwQ==} engines: {node: '>=14'} @@ -4518,8 +4082,8 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json-with-bigint@3.5.8: - resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} + json-with-bigint@3.5.10: + resolution: {integrity: sha512-Vcx+JVNEBts/xfcoCS69sKrOhOk/3TVlvlT+XzUOefVKnnrbYSCKpDCm10pohsJFtsJVYnwa/cXRZ4eElzaM6w==} jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} @@ -4578,6 +4142,80 @@ packages: libqp@2.1.1: resolution: {integrity: sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} @@ -4895,26 +4533,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -4968,8 +4586,8 @@ packages: peerDependencies: undici: ^6 - node-releases@2.0.50: - resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} engines: {node: '>=18'} nodemailer@9.0.3: @@ -5212,10 +4830,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} @@ -5238,22 +4852,6 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.18: - resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.18: - resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.18: - resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.18: - resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} - engines: {node: ^10 || ^12 || >=14} - postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -5268,8 +4866,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@7.6.4: - resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==} + protobufjs@7.6.5: + resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} engines: {node: '>=12.0.0'} proxy-agent-negotiate@1.1.0: @@ -5296,6 +4894,10 @@ packages: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + engines: {node: '>=0.6'} + qs@6.5.5: resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==} engines: {node: '>=0.6'} @@ -5486,21 +5088,11 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-rc.17: - resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - rolldown@1.1.5: resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.62.2: - resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rss-parser@3.13.0: resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==} @@ -5539,8 +5131,8 @@ packages: engines: {node: '>=10'} hasBin: true - set-cookie-parser@3.1.1: - resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} @@ -5640,8 +5232,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stealthy-require@1.1.1: resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} @@ -5675,8 +5267,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.1: - resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} engines: {node: '>=20'} string_decoder@1.3.0: @@ -5720,10 +5312,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} - engines: {node: ^14.18.0 || >=16.0.0} - system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -5736,8 +5324,8 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - tar@7.5.19: - resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} + tar@7.5.20: + resolution: {integrity: sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==} engines: {node: '>=18'} telegram@2.26.22: @@ -5852,11 +5440,6 @@ packages: resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} engines: {node: '>=14.0.0'} - ts-declaration-location@1.0.7: - resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} - peerDependencies: - typescript: '>=4.0.0' - ts-xor@1.3.0: resolution: {integrity: sha512-RLXVjliCzc1gfKQFLRpfeD0rrWmjnSTgj7+RFhoq3KRkUYa8LE/TIidYOzM5h+IdFBDSjjSgk9Lto9sdMfDFEA==} @@ -6037,16 +5620,6 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unrun@0.2.37: - resolution: {integrity: sha512-AA7vDuYsgeSYVzJMm16UKA+aXFKhy7nFqW9z5l7q44K4ppFWZAMqYS58ePRZbugMLPH0fwwMzD5A8nP0avxwZQ==} - engines: {node: '>=20.19.0'} - hasBin: true - peerDependencies: - synckit: ^0.11.11 - peerDependenciesMeta: - synckit: - optional: true - until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} @@ -6121,15 +5694,16 @@ packages: peerDependencies: vite: '*' - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@8.1.4: + resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -6140,12 +5714,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -6434,7 +6010,7 @@ snapshots: '@octokit/core': 7.0.6 '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) - '@octokit/request': 10.0.10 + '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 undici: 6.27.0 @@ -6453,7 +6029,7 @@ snapshots: '@apm-js-collab/code-transformer-bundler-plugins@0.5.0': dependencies: '@apm-js-collab/code-transformer': 0.15.0 - es-module-lexer: 2.3.0 + es-module-lexer: 2.3.1 magic-string: 0.30.21 module-details-from-path: 1.0.4 @@ -6583,7 +6159,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) wrangler: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -6646,42 +6222,18 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@edge-runtime/primitives@4.1.0': - optional: true - - '@edge-runtime/vm@3.2.0': - dependencies: - '@edge-runtime/primitives': 4.1.0 - optional: true - - '@emnapi/core@1.10.0': - dependencies: - '@emnapi/wasi-threads': 1.2.1 - tslib: 2.8.1 - optional: true - '@emnapi/core@1.11.1': dependencies: '@emnapi/wasi-threads': 1.2.2 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.11.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.11.2': + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.1': + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 optional: true @@ -6693,165 +6245,87 @@ snapshots: '@epic-web/invariant@1.0.0': {} - '@esbuild/aix-ppc64@0.27.7': - optional: true - '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm64@0.27.7': - optional: true - '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/android-arm@0.27.7': - optional: true - '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/android-x64@0.27.7': - optional: true - '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.27.7': - optional: true - '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/darwin-x64@0.27.7': - optional: true - '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.27.7': - optional: true - '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/freebsd-x64@0.27.7': - optional: true - '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.27.7': - optional: true - '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/linux-arm@0.27.7': - optional: true - '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/linux-ia32@0.27.7': - optional: true - '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/linux-loong64@0.27.7': - optional: true - '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.27.7': - optional: true - '@esbuild/linux-mips64el@0.28.1': optional: true - '@esbuild/linux-ppc64@0.27.7': - optional: true - '@esbuild/linux-ppc64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.27.7': - optional: true - '@esbuild/linux-riscv64@0.28.1': optional: true - '@esbuild/linux-s390x@0.27.7': - optional: true - '@esbuild/linux-s390x@0.28.1': optional: true - '@esbuild/linux-x64@0.27.7': - optional: true - '@esbuild/linux-x64@0.28.1': optional: true - '@esbuild/netbsd-arm64@0.27.7': - optional: true - '@esbuild/netbsd-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.27.7': - optional: true - '@esbuild/netbsd-x64@0.28.1': optional: true - '@esbuild/openbsd-arm64@0.27.7': - optional: true - '@esbuild/openbsd-arm64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.27.7': - optional: true - '@esbuild/openbsd-x64@0.28.1': optional: true - '@esbuild/openharmony-arm64@0.27.7': - optional: true - '@esbuild/openharmony-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.27.7': - optional: true - '@esbuild/sunos-x64@0.28.1': optional: true - '@esbuild/win32-arm64@0.27.7': - optional: true - '@esbuild/win32-arm64@0.28.1': optional: true - '@esbuild/win32-ia32@0.27.7': - optional: true - '@esbuild/win32-ia32@0.28.1': optional: true - '@esbuild/win32-x64@0.27.7': - optional: true - '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)': dependencies: - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -6886,9 +6360,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.6.0(jiti@2.6.1))': + '@eslint/js@10.0.1(eslint@10.6.0)': optionalDependencies: - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 '@eslint/object-schema@3.0.5': {} @@ -7186,7 +6660,7 @@ snapshots: node-fetch: 2.7.0 nopt: 8.1.0 semver: 7.8.5 - tar: 7.5.19 + tar: 7.5.20 transitivePeerDependencies: - encoding - supports-color @@ -7202,13 +6676,6 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.3 - optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -7236,7 +6703,7 @@ snapshots: dependencies: '@octokit/auth-token': 6.0.0 '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.10 + '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 before-after-hook: 4.0.0 @@ -7249,7 +6716,7 @@ snapshots: '@octokit/graphql@9.0.3': dependencies: - '@octokit/request': 10.0.10 + '@octokit/request': 10.0.11 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -7269,13 +6736,13 @@ snapshots: dependencies: '@octokit/types': 16.0.0 - '@octokit/request@10.0.10': + '@octokit/request@10.0.11': dependencies: '@octokit/endpoint': 11.0.3 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 content-type: 2.0.0 - json-with-bigint: 3.5.8 + json-with-bigint: 3.5.10 universal-user-agent: 7.0.3 '@octokit/types@16.0.0': @@ -7478,9 +6945,6 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.139.0': optional: true - '@oxc-project/types@0.127.0': - optional: true - '@oxc-project/types@0.139.0': {} '@oxfmt/binding-android-arm-eabi@0.58.0': @@ -7622,9 +7086,6 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.10': - optional: true - '@poppinss/colors@4.1.6': dependencies: kleur: 4.1.5 @@ -7672,91 +7133,48 @@ snapshots: '@protobufjs/pool@1.1.0': {} - '@protobufjs/utf8@1.1.1': {} + '@protobufjs/utf8@1.1.2': {} '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 - '@rolldown/binding-android-arm64@1.0.0-rc.17': - optional: true - '@rolldown/binding-android-arm64@1.1.5': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.17': - optional: true - '@rolldown/binding-darwin-arm64@1.1.5': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.17': - optional: true - '@rolldown/binding-darwin-x64@1.1.5': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.17': - optional: true - '@rolldown/binding-freebsd-x64@1.1.5': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.1.5': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-arm64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-arm64-musl@1.1.5': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-ppc64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-s390x-gnu@1.1.5': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-x64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': - optional: true - '@rolldown/binding-linux-x64-musl@1.1.5': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': - optional: true - '@rolldown/binding-openharmony-arm64@1.1.5': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - optional: true - '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: '@emnapi/core': 1.11.1 @@ -7764,105 +7182,19 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': - optional: true - '@rolldown/binding-win32-arm64-msvc@1.1.5': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': - optional: true - '@rolldown/binding-win32-x64-msvc@1.1.5': optional: true - '@rolldown/pluginutils@1.0.0-rc.17': - optional: true - '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + '@rollup/pluginutils@5.4.0': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.5 - optionalDependencies: - rollup: 4.62.2 - - '@rollup/rollup-android-arm-eabi@4.62.2': - optional: true - - '@rollup/rollup-android-arm64@4.62.2': - optional: true - - '@rollup/rollup-darwin-arm64@4.62.2': - optional: true - - '@rollup/rollup-darwin-x64@4.62.2': - optional: true - - '@rollup/rollup-freebsd-arm64@4.62.2': - optional: true - - '@rollup/rollup-freebsd-x64@4.62.2': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.62.2': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.62.2': - optional: true - - '@rollup/rollup-linux-x64-musl@4.62.2': - optional: true - - '@rollup/rollup-openbsd-x64@4.62.2': - optional: true - - '@rollup/rollup-openharmony-arm64@4.62.2': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.62.2': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.62.2': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.62.2': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.62.2': - optional: true '@rss3/api-core@0.0.25': dependencies: @@ -7983,15 +7315,15 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.6.0(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.6.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) - '@typescript-eslint/types': 8.62.0 - eslint: 10.6.0(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@typescript-eslint/types': 8.63.0 + eslint: 10.6.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 - picomatch: 4.0.4 + picomatch: 4.0.5 '@tybys/wasm-util@0.10.3': dependencies: @@ -8043,7 +7375,7 @@ snapshots: dependencies: '@types/node': 26.1.1 - '@types/express-serve-static-core@5.1.1': + '@types/express-serve-static-core@5.1.2': dependencies: '@types/node': 26.1.1 '@types/qs': 6.15.1 @@ -8053,7 +7385,7 @@ snapshots: '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.1 + '@types/express-serve-static-core': 5.1.2 '@types/serve-static': 2.2.0 '@types/fs-extra@11.0.4': @@ -8155,30 +7487,30 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)))(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))': + '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0))(@typescript/typescript6@6.0.2)(eslint@10.6.0)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) + '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/type-utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) + '@typescript-eslint/type-utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) + '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) '@typescript-eslint/visitor-keys': 8.63.0 - eslint: 10.6.0(jiti@2.6.1) - ignore: 7.0.5 + eslint: 10.6.0 + ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))': + '@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': dependencies: '@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) '@typescript-eslint/visitor-keys': 8.63.0 debug: 4.4.3 - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8201,20 +7533,18 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))': + '@typescript-eslint/type-utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': dependencies: '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1)) + '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) debug: 4.4.3 - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.62.0': {} - '@typescript-eslint/types@8.63.0': {} '@typescript-eslint/typescript-estree@8.63.0(@typescript/typescript6@6.0.2)': @@ -8232,13 +7562,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))': + '@typescript-eslint/utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8312,10 +7642,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.10.2(rollup@4.62.2)': + '@vercel/nft@1.10.2': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + '@rollup/pluginutils': 5.4.0 acorn: 8.17.0 acorn-import-attributes: 1.9.5(acorn@8.17.0) async-sema: 3.1.1 @@ -8324,7 +7654,7 @@ snapshots: glob: 13.0.6 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 - picomatch: 4.0.4 + picomatch: 4.0.5 resolve-from: 5.0.0 transitivePeerDependencies: - encoding @@ -8341,9 +7671,9 @@ snapshots: istanbul-reports: 3.2.0 magicast: 0.5.3 obug: 2.1.3 - std-env: 4.1.0 + std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -8354,14 +7684,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -8552,7 +7882,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.42: {} + baseline-browser-mapping@2.10.43: {} basic-ftp@5.3.1: {} @@ -8585,7 +7915,7 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.15: + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -8598,21 +7928,13 @@ snapshots: dependencies: balanced-match: 4.0.4 - browserslist@4.28.4: - dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001802 - electron-to-chromium: 1.5.387 - node-releases: 2.0.50 - update-browserslist-db: 1.2.3(browserslist@4.28.4) - - browserslist@4.28.5: + browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001803 - electron-to-chromium: 1.5.388 - node-releases: 2.0.50 - update-browserslist-db: 1.2.3(browserslist@4.28.5) + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001805 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) buffer-equal-constant-time@1.0.1: {} @@ -8660,9 +7982,7 @@ snapshots: camelcase@8.0.0: {} - caniuse-lite@1.0.30001802: {} - - caniuse-lite@1.0.30001803: {} + caniuse-lite@1.0.30001805: {} caseless@0.12.0: {} @@ -8720,7 +8040,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.1 + string-width: 8.2.2 cli-width@4.1.0: {} @@ -8786,7 +8106,7 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 core-js@2.6.12: {} @@ -8996,9 +8316,7 @@ snapshots: minimatch: 10.2.5 semver: 7.8.5 - electron-to-chromium@1.5.387: {} - - electron-to-chromium@1.5.388: {} + electron-to-chromium@1.5.389: {} emoji-regex@10.6.0: {} @@ -9040,7 +8358,9 @@ snapshots: error-stack-parser-es@1.0.5: {} - es-module-lexer@2.3.0: {} + es-define-property@1.0.1: {} + + es-module-lexer@2.3.1: {} es5-ext@0.10.64: dependencies: @@ -9067,35 +8387,6 @@ snapshots: es6-iterator: 2.0.3 es6-symbol: 3.1.4 - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - esbuild@0.28.1: optionalDependencies: '@esbuild/aix-ppc64': 0.28.1 @@ -9139,76 +8430,75 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.6.0(jiti@2.6.1)): + eslint-compat-utils@0.5.1(eslint@10.6.0): dependencies: - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 semver: 7.8.5 - eslint-filtered-fix@0.3.0(eslint@10.6.0(jiti@2.6.1)): + eslint-filtered-fix@0.3.0(eslint@10.6.0): dependencies: - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.6.0(jiti@2.6.1)): + eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.6.0): dependencies: '@babel/code-frame': 7.29.7 '@inquirer/checkbox': 4.3.2(@types/node@26.1.1) '@inquirer/confirm': 5.1.21(@types/node@26.1.1) '@inquirer/select': 4.4.2(@types/node@26.1.1) - eslint: 10.6.0(jiti@2.6.1) - eslint-filtered-fix: 0.3.0(eslint@10.6.0(jiti@2.6.1)) + eslint: 10.6.0 + eslint-filtered-fix: 0.3.0(eslint@10.6.0) optionator: 0.9.4 text-table: 0.2.0 yoctocolors: 2.1.2 transitivePeerDependencies: - '@types/node' - eslint-plugin-es-x@7.8.0(eslint@10.6.0(jiti@2.6.1)): + eslint-plugin-es-x@7.8.0(eslint@10.6.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/regexpp': 4.12.2 - eslint: 10.6.0(jiti@2.6.1) - eslint-compat-utils: 0.5.1(eslint@10.6.0(jiti@2.6.1)) + eslint: 10.6.0 + eslint-compat-utils: 0.5.1(eslint@10.6.0) - eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0(jiti@2.6.1))(ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2)): + eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) enhanced-resolve: 5.24.2 - eslint: 10.6.0(jiti@2.6.1) - eslint-plugin-es-x: 7.8.0(eslint@10.6.0(jiti@2.6.1)) + eslint: 10.6.0 + eslint-plugin-es-x: 7.8.0(eslint@10.6.0) get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 semver: 7.8.5 optionalDependencies: - ts-declaration-location: 1.0.7(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.1.1(eslint@10.6.0(jiti@2.6.1)): + eslint-plugin-regexp@3.1.1(eslint@10.6.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.7 - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 jsdoc-type-pratt-parser: 7.2.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@13.0.0(eslint@10.6.0(jiti@2.6.1)): + eslint-plugin-simple-import-sort@13.0.0(eslint@10.6.0): dependencies: - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 - eslint-plugin-unicorn@71.1.0(eslint@10.6.0(jiti@2.6.1)): + eslint-plugin-unicorn@71.1.0(eslint@10.6.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) - browserslist: 4.28.5 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + browserslist: 4.28.6 change-case: 5.4.4 ci-info: 4.4.0 core-js-compat: 3.49.0 detect-indent: 7.0.2 - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 find-up-simple: 1.0.1 globals: 17.7.0 indent-string: 5.0.0 @@ -9221,14 +8511,14 @@ snapshots: semver: 7.8.5 strip-indent: 4.1.1 - eslint-plugin-yml@3.6.0(eslint@10.6.0(jiti@2.6.1)): + eslint-plugin-yml@3.6.0(eslint@10.6.0): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.6.0(jiti@2.6.1) + eslint: 10.6.0 natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -9245,9 +8535,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.6.0(jiti@2.6.1): + eslint@10.6.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.6.0 @@ -9277,8 +8567,6 @@ snapshots: minimatch: 10.2.5 natural-compare: 1.4.0 optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -9571,7 +8859,7 @@ snapshots: gaxios: 7.1.3 google-auth-library: 10.5.0 google-logging-utils: 1.1.3 - qs: 6.14.2 + qs: 6.15.3 url-template: 2.0.8 transitivePeerDependencies: - supports-color @@ -9615,7 +8903,7 @@ snapshots: header-generator@2.1.82: dependencies: - browserslist: 4.28.4 + browserslist: 4.28.6 generative-bayesian-network: 2.1.83 ow: 0.28.2 tslib: 2.8.1 @@ -9623,7 +8911,7 @@ snapshots: headers-polyfill@5.0.1: dependencies: '@types/set-cookie-parser': 2.4.10 - set-cookie-parser: 3.1.1 + set-cookie-parser: 3.1.2 heap@0.2.7: {} @@ -9745,7 +9033,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.5: {} + ignore@7.0.6: {} image-size@0.7.5: {} @@ -9769,7 +9057,7 @@ snapshots: import-in-the-middle@3.3.1: dependencies: cjs-module-lexer: 2.2.0 - es-module-lexer: 2.3.0 + es-module-lexer: 2.3.1 module-details-from-path: 1.0.4 import-without-cache@0.4.0: {} @@ -9915,9 +9203,6 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@2.6.1: - optional: true - js-beautify@2.0.3: dependencies: config-chain: 1.1.13 @@ -9986,7 +9271,7 @@ snapshots: json-stringify-safe@5.0.1: {} - json-with-bigint@3.5.8: {} + json-with-bigint@3.5.10: {} jsonfile@6.2.1: dependencies: @@ -10061,6 +9346,55 @@ snapshots: libqp@2.1.1: {} + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 @@ -10068,7 +9402,7 @@ snapshots: lint-staged@17.0.8: dependencies: listr2: 10.2.2 - picomatch: 4.0.4 + picomatch: 4.0.5 string-argv: 0.3.2 tinyexec: 1.2.4 optionalDependencies: @@ -10521,7 +9855,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.15 + brace-expansion: 1.1.16 minimatch@9.0.9: dependencies: @@ -10535,7 +9869,7 @@ snapshots: mixi2@0.2.2: dependencies: - protobufjs: 7.6.4 + protobufjs: 7.6.5 mockdate@3.0.5: {} @@ -10579,14 +9913,6 @@ snapshots: nanoid@3.3.16: {} - nanoid@3.3.16: {} - - nanoid@3.3.16: {} - - nanoid@3.3.16: {} - - nanoid@3.3.16: {} - nanoid@5.1.16: {} narou@2.0.1: {} @@ -10628,7 +9954,7 @@ snapshots: transitivePeerDependencies: - utf-8-validate - node-releases@2.0.50: {} + node-releases@2.0.51: {} nodemailer@9.0.3: {} @@ -10920,8 +10246,6 @@ snapshots: picocolors@1.1.1: {} - picomatch@4.0.4: {} - picomatch@4.0.5: {} pino-abstract-transport@3.0.0: @@ -10946,30 +10270,6 @@ snapshots: pluralize@8.0.0: {} - postcss@8.5.18: - dependencies: - nanoid: 3.3.16 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.18: - dependencies: - nanoid: 3.3.16 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.18: - dependencies: - nanoid: 3.3.16 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.18: - dependencies: - nanoid: 3.3.16 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.18: dependencies: nanoid: 3.3.16 @@ -11007,7 +10307,7 @@ snapshots: proto-list@1.2.4: {} - protobufjs@7.6.4: + protobufjs@7.6.5: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -11017,7 +10317,7 @@ snapshots: '@protobufjs/float': 1.0.2 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.1 + '@protobufjs/utf8': 1.1.2 '@types/node': 26.1.1 long: 5.3.2 @@ -11035,6 +10335,11 @@ snapshots: dependencies: side-channel: '@nolyfill/side-channel@1.0.44' + qs@6.15.3: + dependencies: + es-define-property: 1.0.1 + side-channel: '@nolyfill/side-channel@1.0.44' + qs@6.5.5: {} quansync@1.0.0: {} @@ -11231,28 +10536,6 @@ snapshots: transitivePeerDependencies: - oxc-resolver - rolldown@1.0.0-rc.17: - dependencies: - '@oxc-project/types': 0.127.0 - '@rolldown/pluginutils': 1.0.0-rc.17 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 - '@rolldown/binding-darwin-x64': 1.0.0-rc.17 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 - optional: true - rolldown@1.1.5: dependencies: '@oxc-project/types': 0.139.0 @@ -11274,37 +10557,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 - rollup@4.62.2: - dependencies: - '@types/estree': 1.0.9 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.2 - '@rollup/rollup-android-arm64': 4.62.2 - '@rollup/rollup-darwin-arm64': 4.62.2 - '@rollup/rollup-darwin-x64': 4.62.2 - '@rollup/rollup-freebsd-arm64': 4.62.2 - '@rollup/rollup-freebsd-x64': 4.62.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 - '@rollup/rollup-linux-arm-musleabihf': 4.62.2 - '@rollup/rollup-linux-arm64-gnu': 4.62.2 - '@rollup/rollup-linux-arm64-musl': 4.62.2 - '@rollup/rollup-linux-loong64-gnu': 4.62.2 - '@rollup/rollup-linux-loong64-musl': 4.62.2 - '@rollup/rollup-linux-ppc64-gnu': 4.62.2 - '@rollup/rollup-linux-ppc64-musl': 4.62.2 - '@rollup/rollup-linux-riscv64-gnu': 4.62.2 - '@rollup/rollup-linux-riscv64-musl': 4.62.2 - '@rollup/rollup-linux-s390x-gnu': 4.62.2 - '@rollup/rollup-linux-x64-gnu': 4.62.2 - '@rollup/rollup-linux-x64-musl': 4.62.2 - '@rollup/rollup-openbsd-x64': 4.62.2 - '@rollup/rollup-openharmony-arm64': 4.62.2 - '@rollup/rollup-win32-arm64-msvc': 4.62.2 - '@rollup/rollup-win32-ia32-msvc': 4.62.2 - '@rollup/rollup-win32-x64-gnu': 4.62.2 - '@rollup/rollup-win32-x64-msvc': 4.62.2 - fsevents: 2.3.3 - rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): dependencies: entities: 7.0.1 @@ -11346,7 +10598,7 @@ snapshots: semver@7.8.5: {} - set-cookie-parser@3.1.1: {} + set-cookie-parser@3.1.2: {} sharp@0.34.5: dependencies: @@ -11469,7 +10721,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.2.0: {} stealthy-require@1.1.1: {} @@ -11503,7 +10755,7 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string-width@8.2.1: + string-width@8.2.2: dependencies: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 @@ -11542,18 +10794,13 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.11.12: - dependencies: - '@pkgr/core': 0.2.10 - optional: true - system-architecture@0.1.0: {} tagged-tag@1.0.0: {} tapable@2.3.3: {} - tar@7.5.19: + tar@7.5.20: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -11670,19 +10917,13 @@ snapshots: ts-custom-error@3.3.1: {} - ts-declaration-location@1.0.7(@typescript/typescript6@6.0.2): - dependencies: - picomatch: 4.0.5 - typescript: '@typescript/typescript6@6.0.2' - optional: true - ts-xor@1.3.0: {} tsconfck@3.1.6(@typescript/typescript6@6.0.2): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0)(unrun@0.2.37(synckit@0.11.12)): + tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11702,7 +10943,6 @@ snapshots: optionalDependencies: tsx: 4.23.0 typescript: '@typescript/typescript6@6.0.2' - unrun: 0.2.37(synckit@0.11.12) transitivePeerDependencies: - '@ts-macro/tsc' - '@typescript/native-preview' @@ -11851,24 +11091,11 @@ snapshots: universalify@2.0.1: {} - unrun@0.2.37(synckit@0.11.12): - dependencies: - rolldown: 1.0.0-rc.17 - optionalDependencies: - synckit: 0.11.12 - optional: true - until-async@3.0.2: {} - update-browserslist-db@1.2.3(browserslist@4.28.4): - dependencies: - browserslist: 4.28.4 - escalade: 3.2.0 - picocolors: 1.1.1 - - update-browserslist-db@1.2.3(browserslist@4.28.5): + update-browserslist-db@1.2.3(browserslist@4.28.6): dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 escalade: 3.2.0 picocolors: 1.1.1 @@ -11926,55 +11153,53 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)): + vite-tsconfig-paths@6.1.1(@typescript/typescript6@6.0.2)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0): + vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0): dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.5) + lightningcss: 1.32.0 picomatch: 4.0.5 postcss: 8.5.18 - rollup: 4.62.2 + rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.1.1 + esbuild: 0.28.1 fsevents: 2.3.3 - jiti: 2.6.1 tsx: 4.23.0 yaml: 2.9.0 - vitest@4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 - es-module-lexer: 2.3.0 + es-module-lexer: 2.3.1 expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 picomatch: 4.0.5 - std-env: 4.1.0 + std-env: 4.2.0 tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@26.1.1)(jiti@2.6.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@edge-runtime/vm': 3.2.0 '@opentelemetry/api': 1.9.1 '@types/node': 26.1.1 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) @@ -12085,7 +11310,7 @@ snapshots: wrap-ansi@10.0.0: dependencies: ansi-styles: 6.2.3 - string-width: 8.2.1 + string-width: 8.2.2 strip-ansi: 7.2.0 wrap-ansi@6.2.0: From 5f94f8fdc4d35ff6ec90d7c72f7f495b41448628 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:53:06 +0800 Subject: [PATCH 0303/1190] chore(deps): bump mitchellh/vouch/action/manage-by-issue (#22692) Bumps [mitchellh/vouch/action/manage-by-issue](https://github.com/mitchellh/vouch) from 1.4.2 to 1.5.0. - [Release notes](https://github.com/mitchellh/vouch/releases) - [Commits](https://github.com/mitchellh/vouch/compare/c6d80ead49839655b61b422700b7a3bc9d0804a9...d66fa29a64600490892131ad87597c30c91fcac4) --- updated-dependencies: - dependency-name: mitchellh/vouch/action/manage-by-issue dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue-command.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 09b5275b8143..48832e6346e9 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -52,7 +52,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - id: vouch - uses: mitchellh/vouch/action/manage-by-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + uses: mitchellh/vouch/action/manage-by-issue@d66fa29a64600490892131ad87597c30c91fcac4 # v1.5.0 with: issue-id: ${{ github.event.issue.number }} comment-id: ${{ github.event.comment.id }} From 42c6d097f0025fa4fecb40424bfb786406a882c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:10:39 +0800 Subject: [PATCH 0304/1190] chore(deps): bump tsx from 4.23.0 to 4.23.1 (#22700) * chore(deps): bump tsx from 4.23.0 to 4.23.1 Bumps [tsx](https://github.com/privatenumber/tsx) from 4.23.0 to 4.23.1. - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.23.0...v4.23.1) --- updated-dependencies: - dependency-name: tsx dependency-version: 4.23.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * fix: resolve pnpm-lock.yaml merge conflicts * fix: regenerate pnpm-lock.yaml to fix missing jiti@2.6.1 entry --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 590 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 555 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 0d6f3abcca08..ca5906771fd4 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "tldts": "7.4.8", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", - "tsx": "4.23.0", + "tsx": "4.23.1", "twitter-api-v2": "1.29.0", "ufo": "1.6.4", "undici": "8.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 804924f74dd0..15487b34e193 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,8 +248,8 @@ importers: specifier: 6.0.2 version: 6.0.2 tsx: - specifier: 4.23.0 - version: 4.23.0 + specifier: 4.23.1 + version: 4.23.1 twitter-api-v2: specifier: 1.29.0 version: 1.29.0 @@ -364,7 +364,7 @@ importers: version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) '@vercel/nft': specifier: 1.10.2 - version: 1.10.2 + version: 1.10.2(rollup@4.62.2) '@vitest/coverage-v8': specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) @@ -454,7 +454,7 @@ importers: version: 11.0.0 tsdown: specifier: 0.22.7 - version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0) + version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -466,10 +466,10 @@ importers: version: 11.0.5 vite-tsconfig-paths: specifier: 6.1.1 - version: 6.1.1(@typescript/typescript6@6.0.2)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) + version: 6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.110.0 version: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -709,156 +709,312 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.28.1': resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.28.1': resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.28.1': resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.28.1': resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.28.1': resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.28.1': resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.28.1': resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.28.1': resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.28.1': resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.28.1': resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.28.1': resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.28.1': resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.28.1': resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.28.1': resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.28.1': resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.28.1': resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.28.1': resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.28.1': resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.28.1': resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.28.1': resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.28.1': resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.28.1': resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.28.1': resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.28.1': resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.28.1': resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} @@ -2034,6 +2190,144 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + '@rss3/api-core@0.0.25': resolution: {integrity: sha512-YVH1QwF4P4r1JCYkkfK+UrKsa2PlJCOjViKhybRUanyR8TwwebpFPV+f5rcDDLRISlCdvhErpiFk9TntstYiCw==} @@ -3331,6 +3625,11 @@ packages: es6-weak-map@2.0.3: resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} @@ -5093,6 +5392,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rss-parser@3.13.0: resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==} @@ -5494,8 +5798,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.0: - resolution: {integrity: sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==} + tsx@4.23.1: + resolution: {integrity: sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -5694,16 +5998,15 @@ packages: peerDependencies: vite: '*' - vite@8.1.4: - resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.3.0 - esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 + lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -5714,14 +6017,12 @@ packages: peerDependenciesMeta: '@types/node': optional: true - '@vitejs/devtools': - optional: true - esbuild: - optional: true jiti: optional: true less: optional: true + lightningcss: + optional: true sass: optional: true sass-embedded: @@ -6159,7 +6460,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -6245,81 +6546,159 @@ snapshots: '@epic-web/invariant@1.0.0': {} + '@esbuild/aix-ppc64@0.27.7': + optional: true + '@esbuild/aix-ppc64@0.28.1': optional: true + '@esbuild/android-arm64@0.27.7': + optional: true + '@esbuild/android-arm64@0.28.1': optional: true + '@esbuild/android-arm@0.27.7': + optional: true + '@esbuild/android-arm@0.28.1': optional: true + '@esbuild/android-x64@0.27.7': + optional: true + '@esbuild/android-x64@0.28.1': optional: true + '@esbuild/darwin-arm64@0.27.7': + optional: true + '@esbuild/darwin-arm64@0.28.1': optional: true + '@esbuild/darwin-x64@0.27.7': + optional: true + '@esbuild/darwin-x64@0.28.1': optional: true + '@esbuild/freebsd-arm64@0.27.7': + optional: true + '@esbuild/freebsd-arm64@0.28.1': optional: true + '@esbuild/freebsd-x64@0.27.7': + optional: true + '@esbuild/freebsd-x64@0.28.1': optional: true + '@esbuild/linux-arm64@0.27.7': + optional: true + '@esbuild/linux-arm64@0.28.1': optional: true + '@esbuild/linux-arm@0.27.7': + optional: true + '@esbuild/linux-arm@0.28.1': optional: true + '@esbuild/linux-ia32@0.27.7': + optional: true + '@esbuild/linux-ia32@0.28.1': optional: true + '@esbuild/linux-loong64@0.27.7': + optional: true + '@esbuild/linux-loong64@0.28.1': optional: true + '@esbuild/linux-mips64el@0.27.7': + optional: true + '@esbuild/linux-mips64el@0.28.1': optional: true + '@esbuild/linux-ppc64@0.27.7': + optional: true + '@esbuild/linux-ppc64@0.28.1': optional: true + '@esbuild/linux-riscv64@0.27.7': + optional: true + '@esbuild/linux-riscv64@0.28.1': optional: true + '@esbuild/linux-s390x@0.27.7': + optional: true + '@esbuild/linux-s390x@0.28.1': optional: true + '@esbuild/linux-x64@0.27.7': + optional: true + '@esbuild/linux-x64@0.28.1': optional: true + '@esbuild/netbsd-arm64@0.27.7': + optional: true + '@esbuild/netbsd-arm64@0.28.1': optional: true + '@esbuild/netbsd-x64@0.27.7': + optional: true + '@esbuild/netbsd-x64@0.28.1': optional: true + '@esbuild/openbsd-arm64@0.27.7': + optional: true + '@esbuild/openbsd-arm64@0.28.1': optional: true + '@esbuild/openbsd-x64@0.27.7': + optional: true + '@esbuild/openbsd-x64@0.28.1': optional: true + '@esbuild/openharmony-arm64@0.27.7': + optional: true + '@esbuild/openharmony-arm64@0.28.1': optional: true + '@esbuild/sunos-x64@0.27.7': + optional: true + '@esbuild/sunos-x64@0.28.1': optional: true + '@esbuild/win32-arm64@0.27.7': + optional: true + '@esbuild/win32-arm64@0.28.1': optional: true + '@esbuild/win32-ia32@0.27.7': + optional: true + '@esbuild/win32-ia32@0.28.1': optional: true + '@esbuild/win32-x64@0.27.7': + optional: true + '@esbuild/win32-x64@0.28.1': optional: true @@ -7190,11 +7569,88 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0': + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.5 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true '@rss3/api-core@0.0.25': dependencies: @@ -7642,10 +8098,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.10.2': + '@vercel/nft@1.10.2(rollup@4.62.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0 + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) acorn: 8.17.0 acorn-import-attributes: 1.9.5(acorn@8.17.0) async-sema: 3.1.1 @@ -7673,7 +8129,7 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -7684,14 +8140,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) - vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -8387,6 +8843,35 @@ snapshots: es6-iterator: 2.0.3 es6-symbol: 3.1.4 + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + esbuild@0.28.1: optionalDependencies: '@esbuild/aix-ppc64': 0.28.1 @@ -9394,6 +9879,7 @@ snapshots: lightningcss-linux-x64-musl: 1.32.0 lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + optional: true linkify-it@5.0.2: dependencies: @@ -10557,6 +11043,37 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): dependencies: entities: 7.0.1 @@ -10923,7 +11440,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.0): + tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -10941,7 +11458,7 @@ snapshots: tree-kill: 1.2.2 unconfig-core: 7.5.0 optionalDependencies: - tsx: 4.23.0 + tsx: 4.23.1 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - '@ts-macro/tsc' @@ -10953,7 +11470,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.23.0: + tsx@4.23.1: dependencies: esbuild: 0.28.1 optionalDependencies: @@ -11153,34 +11670,35 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(@typescript/typescript6@6.0.2)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)): + vite-tsconfig-paths@6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) - vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0): + vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0): dependencies: - lightningcss: 1.32.0 + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 postcss: 8.5.18 - rolldown: 1.1.5 + rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.1.1 - esbuild: 0.28.1 fsevents: 2.3.3 - tsx: 4.23.0 + lightningcss: 1.32.0 + tsx: 4.23.1 yaml: 2.9.0 - vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -11197,7 +11715,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.1.4(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.0)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 From a65933680db51288c21a692067553a7ee98fcae8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:25:16 +0800 Subject: [PATCH 0305/1190] chore(deps-dev): bump @cloudflare/workers-types (#22693) Bumps the cloudflare group with 1 update in the / directory: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260710.1 to 5.20260713.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260713.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ca5906771fd4..1a24cf9d6a54 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.4", - "@cloudflare/workers-types": "5.20260710.1", + "@cloudflare/workers-types": "5.20260713.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.73.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15487b34e193..e321dca38229 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.4 - version: 0.18.4(@cloudflare/workers-types@5.20260710.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.4(@cloudflare/workers-types@5.20260713.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260710.1 - version: 5.20260710.1 + specifier: 5.20260713.1 + version: 5.20260713.1 '@eslint/eslintrc': specifier: 3.3.5 version: 3.3.5 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.110.0 - version: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -641,8 +641,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260710.1': - resolution: {integrity: sha512-4ooaY2Pb5XGwDn8Fzm6jnTAJkIX0R5LBvL9euQpp2T58sQItlAQd9yivAlkwGhpY5cM1u81/9HaXwKAjXwtyzA==} + '@cloudflare/workers-types@5.20260713.1': + resolution: {integrity: sha512-4YZE9YIzr3iP9eRUV4ekZozLLsYQFFKxmMJfAcsteCuzKJ7RJrLqcCxm1aMzTE9uFcy+D6A89WE+cjJadF2c9g==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3469,7 +3469,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, integrity: sha512-OG2C++3YYIzflFjA9irnexBs9eUS6KUOViAssV4oSo4W8HAl86TV+DoFYhdSXVaFd4z5htsM3zzs64WJybH4cQ==, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -6453,7 +6453,7 @@ snapshots: optionalDependencies: workerd: 1.20260708.1 - '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260710.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260713.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6461,7 +6461,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6483,7 +6483,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260708.1': optional: true - '@cloudflare/workers-types@5.20260710.1': {} + '@cloudflare/workers-types@5.20260713.1': {} '@colors/colors@1.6.0': {} @@ -11808,7 +11808,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260708.1 '@cloudflare/workerd-windows-64': 1.20260708.1 - wrangler@4.110.0(@cloudflare/workers-types@5.20260710.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260708.1) @@ -11819,7 +11819,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260708.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260710.1 + '@cloudflare/workers-types': 5.20260713.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From f10dc7dca9a759f01ef423d88e0939cf253fb4b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 17:41:26 +0800 Subject: [PATCH 0306/1190] chore(deps-dev): bump the eslint group across 1 directory with 2 updates (#22694) Bumps the eslint group with 2 updates in the / directory: [@eslint/eslintrc](https://github.com/eslint/eslintrc) and [eslint](https://github.com/eslint/eslint). Updates `@eslint/eslintrc` from 3.3.5 to 3.3.6 - [Release notes](https://github.com/eslint/eslintrc/releases) - [Changelog](https://github.com/eslint/eslintrc/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.5...eslintrc-v3.3.6) Updates `eslint` from 10.6.0 to 10.7.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.6.0...v10.7.0) --- updated-dependencies: - dependency-name: "@eslint/eslintrc" dependency-version: 3.3.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: eslint - dependency-name: eslint dependency-version: 10.7.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 132 ++++++++++++++++++++++++------------------------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index 1a24cf9d6a54..5a0841810b71 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.4", "@cloudflare/workers-types": "5.20260713.1", - "@eslint/eslintrc": "3.3.5", + "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.73.0", "@stylistic/eslint-plugin": "5.10.0", @@ -175,7 +175,7 @@ "@vitest/coverage-v8": "4.1.10", "discord-api-types": "0.38.49", "domhandler": "6.0.1", - "eslint": "10.6.0", + "eslint": "10.7.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.2.2", "eslint-plugin-regexp": "3.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e321dca38229..16b5e93ee281 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -300,17 +300,17 @@ importers: specifier: 5.20260713.1 version: 5.20260713.1 '@eslint/eslintrc': - specifier: 3.3.5 - version: 3.3.5 + specifier: 3.3.6 + version: 3.3.6 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.6.0) + version: 10.0.1(eslint@10.7.0) '@oxlint/plugins': specifier: 1.73.0 version: 1.73.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.6.0) + version: 5.10.0(eslint@10.7.0) '@types/babel__preset-env': specifier: 7.10.0 version: 7.10.0 @@ -358,10 +358,10 @@ importers: version: 2.16.1 '@typescript-eslint/eslint-plugin': specifier: 8.63.0 - version: 8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0))(@typescript/typescript6@6.0.2)(eslint@10.6.0) + version: 8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@typescript-eslint/parser': specifier: 8.63.0 - version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) + version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@vercel/nft': specifier: 1.10.2 version: 1.10.2(rollup@4.62.2) @@ -375,26 +375,26 @@ importers: specifier: 6.0.1 version: 6.0.1 eslint: - specifier: 10.6.0 - version: 10.6.0 + specifier: 10.7.0 + version: 10.7.0 eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.1.1)(eslint@10.6.0) + version: 9.1.1(@types/node@26.1.1)(eslint@10.7.0) eslint-plugin-n: specifier: 18.2.2 - version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0) + version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.7.0) eslint-plugin-regexp: specifier: 3.1.1 - version: 3.1.1(eslint@10.6.0) + version: 3.1.1(eslint@10.7.0) eslint-plugin-simple-import-sort: specifier: 13.0.0 - version: 13.0.0(eslint@10.6.0) + version: 13.0.0(eslint@10.7.0) eslint-plugin-unicorn: specifier: 71.1.0 - version: 71.1.0(eslint@10.6.0) + version: 71.1.0(eslint@10.7.0) eslint-plugin-yml: specifier: 3.6.0 - version: 3.6.0(eslint@10.6.0) + version: 3.6.0(eslint@10.7.0) fast-string-width: specifier: 3.0.2 version: 3.0.2 @@ -1043,8 +1043,8 @@ packages: resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@10.0.1': @@ -3729,8 +3729,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.6.0: - resolution: {integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==} + eslint@10.7.0: + resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -6702,9 +6702,9 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0)': dependencies: - eslint: 10.6.0 + eslint: 10.7.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -6725,7 +6725,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.6': dependencies: ajv: 6.15.0 debug: 4.4.3 @@ -6739,9 +6739,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.6.0)': + '@eslint/js@10.0.1(eslint@10.7.0)': optionalDependencies: - eslint: 10.6.0 + eslint: 10.7.0 '@eslint/object-schema@3.0.5': {} @@ -7771,11 +7771,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.6.0)': + '@stylistic/eslint-plugin@5.10.0(eslint@10.7.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) '@typescript-eslint/types': 8.63.0 - eslint: 10.6.0 + eslint: 10.7.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -7943,15 +7943,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0))(@typescript/typescript6@6.0.2)(eslint@10.6.0)': + '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) + '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/type-utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) + '@typescript-eslint/type-utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@typescript-eslint/visitor-keys': 8.63.0 - eslint: 10.6.0 + eslint: 10.7.0 ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -7959,14 +7959,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': + '@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) '@typescript-eslint/visitor-keys': 8.63.0 debug: 4.4.3 - eslint: 10.6.0 + eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -7989,13 +7989,13 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': + '@typescript-eslint/type-utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0) + '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) debug: 4.4.3 - eslint: 10.6.0 + eslint: 10.7.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -8018,13 +8018,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.6.0)': + '@typescript-eslint/utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) '@typescript-eslint/scope-manager': 8.63.0 '@typescript-eslint/types': 8.63.0 '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - eslint: 10.6.0 + eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8915,43 +8915,43 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.6.0): + eslint-compat-utils@0.5.1(eslint@10.7.0): dependencies: - eslint: 10.6.0 + eslint: 10.7.0 semver: 7.8.5 - eslint-filtered-fix@0.3.0(eslint@10.6.0): + eslint-filtered-fix@0.3.0(eslint@10.7.0): dependencies: - eslint: 10.6.0 + eslint: 10.7.0 optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.6.0): + eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.7.0): dependencies: '@babel/code-frame': 7.29.7 '@inquirer/checkbox': 4.3.2(@types/node@26.1.1) '@inquirer/confirm': 5.1.21(@types/node@26.1.1) '@inquirer/select': 4.4.2(@types/node@26.1.1) - eslint: 10.6.0 - eslint-filtered-fix: 0.3.0(eslint@10.6.0) + eslint: 10.7.0 + eslint-filtered-fix: 0.3.0(eslint@10.7.0) optionator: 0.9.4 text-table: 0.2.0 yoctocolors: 2.1.2 transitivePeerDependencies: - '@types/node' - eslint-plugin-es-x@7.8.0(eslint@10.6.0): + eslint-plugin-es-x@7.8.0(eslint@10.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) '@eslint-community/regexpp': 4.12.2 - eslint: 10.6.0 - eslint-compat-utils: 0.5.1(eslint@10.6.0) + eslint: 10.7.0 + eslint-compat-utils: 0.5.1(eslint@10.7.0) - eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.6.0): + eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) enhanced-resolve: 5.24.2 - eslint: 10.6.0 - eslint-plugin-es-x: 7.8.0(eslint@10.6.0) + eslint: 10.7.0 + eslint-plugin-es-x: 7.8.0(eslint@10.7.0) get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 @@ -8960,30 +8960,30 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.1.1(eslint@10.6.0): + eslint-plugin-regexp@3.1.1(eslint@10.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.7 - eslint: 10.6.0 + eslint: 10.7.0 jsdoc-type-pratt-parser: 7.2.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@13.0.0(eslint@10.6.0): + eslint-plugin-simple-import-sort@13.0.0(eslint@10.7.0): dependencies: - eslint: 10.6.0 + eslint: 10.7.0 - eslint-plugin-unicorn@71.1.0(eslint@10.6.0): + eslint-plugin-unicorn@71.1.0(eslint@10.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) browserslist: 4.28.6 change-case: 5.4.4 ci-info: 4.4.0 core-js-compat: 3.49.0 detect-indent: 7.0.2 - eslint: 10.6.0 + eslint: 10.7.0 find-up-simple: 1.0.1 globals: 17.7.0 indent-string: 5.0.0 @@ -8996,14 +8996,14 @@ snapshots: semver: 7.8.5 strip-indent: 4.1.1 - eslint-plugin-yml@3.6.0(eslint@10.6.0): + eslint-plugin-yml@3.6.0(eslint@10.7.0): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.6.0 + eslint: 10.7.0 natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -9020,9 +9020,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.6.0: + eslint@10.7.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.6.0 From 4638e5c8287835809dedb34887d7dc669eee9a51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:30:20 +0800 Subject: [PATCH 0307/1190] chore(deps): bump @sentry/node from 10.64.0 to 10.65.0 (#22697) * chore(deps): bump @sentry/node from 10.64.0 to 10.65.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.64.0 to 10.65.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.64.0...10.65.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.65.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: fix workerd build with unrun * chore: bump vite-tsconfig-paths --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 5 +- pnpm-lock.yaml | 304 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 268 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index 5a0841810b71..8ebb54a98297 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.11.9", - "@sentry/node": "10.64.0", + "@sentry/node": "10.65.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", @@ -205,7 +205,8 @@ "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", - "vite-tsconfig-paths": "6.1.1", + "unrun": "0.3.1", + "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", "wrangler": "4.110.0", "yaml-eslint-parser": "2.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16b5e93ee281..59691ad2faba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,8 +86,8 @@ importers: specifier: 0.11.9 version: 0.11.9(hono@4.12.30) '@sentry/node': - specifier: 10.64.0 - version: 10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) + specifier: 10.65.0 + version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -454,7 +454,7 @@ importers: version: 11.0.0 tsdown: specifier: 0.22.7 - version: 0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.1) + version: 0.22.7(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -464,9 +464,12 @@ importers: unified: specifier: 11.0.5 version: 11.0.5 + unrun: + specifier: 0.3.1 + version: 0.3.1 vite-tsconfig-paths: - specifier: 6.1.1 - version: 6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + specifier: 7.0.0-alpha.1 + version: 7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) @@ -697,6 +700,9 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} @@ -1748,6 +1754,109 @@ packages: '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.24.2': + resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.24.2': + resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==} + cpu: [x64] + os: [win32] + '@oxfmt/binding-android-arm-eabi@0.58.0': resolution: {integrity: sha512-Uz62sHduGGPftXtILGyxdSW4PX82rUg+rfdNqhsgxe881g4rIoXlIqmZQ6HVKcF4f+F8qMhdD03Bx5u7gmeTdg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2378,12 +2487,12 @@ packages: resolution: {integrity: sha512-ZLP8bRdMON3prWE2tJyImuYscCxdcJeIPIhrOs/rgyFm3C1nCh1B6gdvPj3AZ5zW08oSFFCsq7T+tYEW3h8MNA==} engines: {node: '>=14'} - '@sentry/core@10.64.0': - resolution: {integrity: sha512-HjojJcXD1l2qZ1AXje2s0XY/nYsaUt00wsM1HMBImA8vAClyPisFE/CC0/UD6pEvsGFhVgi8Dcxo7EN41uyeFw==} + '@sentry/core@10.65.0': + resolution: {integrity: sha512-3aqtmM5NgNGo45BNaaBzi0LPQZAw//NEL4HKS5fXm12pJMa4KEkze8DEKnkTEIrGnWaOJKamecHKlnNg/Mqf/Q==} engines: {node: '>=18'} - '@sentry/node-core@10.64.0': - resolution: {integrity: sha512-dcma5uEWl0SXywY4vzrlOwuz4NBPyPhrzqL1NjlU6Di/OVbyYAZJPCwsR8XYDz73PGc5sU3qKSRoXWX56Ip0wA==} + '@sentry/node-core@10.65.0': + resolution: {integrity: sha512-U01X9mPT+jZnsLPmPWfBU67Ka+t/Sdd9RGAuvGoKdrI6N47a/9PDkM9oCW+kj0fmZwogZHTgSnzJU5oi3pImgA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2403,20 +2512,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.64.0': - resolution: {integrity: sha512-rhNZ3CTqwdTQHo9Zd+NsoLyW69VIzbMcGMRX9y8W1A6runT4YH/MDhmT0U9oWfNZKN8ngqR/gfVMTnlYVQliCA==} + '@sentry/node@10.65.0': + resolution: {integrity: sha512-t35dcdyksysVch/m/XdLgGJqGKJhr9eMD30Ctn3TeQ8yMB0wNXySfjPR5Yg93fpjmfaHtzc6iYIXRAvgNVfrvA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.64.0': - resolution: {integrity: sha512-hXw8dwSSA9/9r3VGy0PO2SJp7g5/yH2+7Bak60EkOT21AT1BwFnVEsVQyZb+UHjG7UWne/jDbwdWPSUm/rtovw==} + '@sentry/opentelemetry@10.65.0': + resolution: {integrity: sha512-8C6FPvm3XBvUrkM52dX3Gz0p2H0Ij8t4sahUA+GTiCz0WM0fnyPeQPGC/b6I4jamV9UXyCZRnE1UEEGCoD+c7A==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.64.0': - resolution: {integrity: sha512-d568Jhn3WBfm07dsdPsLh0q+qRj71xZEDZFsA33kizD0UuSCR8axtc2YW6aKdPhtuqgm9tHjSX35Q9vK/XmujA==} + '@sentry/server-utils@10.65.0': + resolution: {integrity: sha512-80toEFD6s+0Le7jrYB6pHWLF703WSg0WyavAWqrBGWG8JkREHgedAxzFYgoY5GlMI756qk6Ea7UzhJTHd2zAXA==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -3469,7 +3578,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -4981,6 +5090,9 @@ packages: resolution: {integrity: sha512-cf1TKZN+zc0lwqigeyXKzKVk5+vNRe99Or2+wVJsXLdlhJgC+gsIniYDfj/ZEBzCJ8Xm21ZG6YtMbR262CcS2w==} engines: {node: ^20.19.0 || >=22.12.0} + oxc-resolver@11.24.2: + resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} + oxfmt@0.58.0: resolution: {integrity: sha512-8feG/7NVEHDVwc1OUpP6Pks+TnaDFUw2jLLFIMi5bcmmwxAX2wBQvjSzj62RRTYBf2Op1Wt8xbkmagmPTR5ETg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5924,6 +6036,16 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unrun@0.3.1: + resolution: {integrity: sha512-onIck/oNnCaytwths1ZVp1LK2Gq2hPoyFhiHebObuUXqR3S0uHuLLaBK8K6mRRgV7Ptip8AnNvaUsgzwWwBZuA==} + engines: {node: ^22.13.0 || >=24.0.0} + hasBin: true + peerDependencies: + synckit: ^0.11.11 + peerDependenciesMeta: + synckit: + optional: true + until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} @@ -5993,10 +6115,11 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-tsconfig-paths@6.1.1: - resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} + vite-tsconfig-paths@7.0.0-alpha.1: + resolution: {integrity: sha512-R2xUKqgU3lETsbx66sOoVj6/2hf9J9Aizavs03obMY5TxC99UbF1tM4bSfn1KZ8HxtOEaK3gdm/CEutS3yBvog==} + engines: {node: '>=18'} peerDependencies: - vite: '*' + vite: '>=5.0.0' vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} @@ -6529,6 +6652,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 @@ -7062,6 +7191,13 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 + optional: true + '@noble/hashes@2.2.0': {} '@nolyfill/es-set-tostringtag@1.0.44': {} @@ -7326,6 +7462,67 @@ snapshots: '@oxc-project/types@0.139.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': + optional: true + + '@oxc-resolver/binding-android-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.24.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.24.2': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.24.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.24.2': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.24.2': + optional: true + '@oxfmt/binding-android-arm-eabi@0.58.0': optional: true @@ -7706,15 +7903,15 @@ snapshots: '@sentry/conventions@0.15.1': {} - '@sentry/core@10.64.0': + '@sentry/core@10.65.0': dependencies: '@sentry/conventions': 0.15.1 - '@sentry/node-core@10.64.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.15.1 - '@sentry/core': 10.64.0 - '@sentry/opentelemetry': 10.64.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/core': 10.65.0 + '@sentry/opentelemetry': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.3.1 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -7723,37 +7920,37 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.64.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.15.1 - '@sentry/core': 10.64.0 - '@sentry/node-core': 10.64.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.64.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.64.0 + '@sentry/core': 10.65.0 + '@sentry/node-core': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.65.0 import-in-the-middle: 3.3.1 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.64.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.15.1 - '@sentry/core': 10.64.0 + '@sentry/core': 10.65.0 - '@sentry/server-utils@10.64.0': + '@sentry/server-utils@10.65.0': dependencies: '@apm-js-collab/code-transformer': 0.15.0 '@apm-js-collab/code-transformer-bundler-plugins': 0.5.0 '@apm-js-collab/tracing-hooks': 0.10.1 '@sentry/conventions': 0.15.1 - '@sentry/core': 10.64.0 + '@sentry/core': 10.65.0 magic-string: 0.30.21 transitivePeerDependencies: - supports-color @@ -8752,7 +8949,9 @@ snapshots: dotenv@17.4.2: {} - dts-resolver@3.0.0: {} + dts-resolver@3.0.0(oxc-resolver@11.24.2): + optionalDependencies: + oxc-resolver: 11.24.2 eastasianwidth@0.2.0: {} @@ -10566,6 +10765,28 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.139.0 '@oxc-parser/binding-win32-x64-msvc': 0.139.0 + oxc-resolver@11.24.2: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.24.2 + '@oxc-resolver/binding-android-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-arm64': 11.24.2 + '@oxc-resolver/binding-darwin-x64': 11.24.2 + '@oxc-resolver/binding-freebsd-x64': 11.24.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.24.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.24.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.24.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.24.2 + '@oxc-resolver/binding-linux-x64-musl': 11.24.2 + '@oxc-resolver/binding-openharmony-arm64': 11.24.2 + '@oxc-resolver/binding-wasm32-wasi': 11.24.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 + oxfmt@0.58.0: dependencies: tinypool: 2.1.0 @@ -11008,9 +11229,9 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.8(@typescript/typescript6@6.0.2)(rolldown@1.1.5): + rolldown-plugin-dts@0.27.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5): dependencies: - dts-resolver: 3.0.0 + dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.5 obug: 2.1.3 rolldown: 1.1.5 @@ -11440,7 +11661,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.7(@typescript/typescript6@6.0.2)(tsx@4.23.1): + tsdown@0.22.7(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11451,7 +11672,7 @@ snapshots: obug: 2.1.3 picomatch: 4.0.5 rolldown: 1.1.5 - rolldown-plugin-dts: 0.27.8(@typescript/typescript6@6.0.2)(rolldown@1.1.5) + rolldown-plugin-dts: 0.27.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5) semver: 7.8.5 tinyexec: 1.2.4 tinyglobby: 0.2.17 @@ -11460,6 +11681,7 @@ snapshots: optionalDependencies: tsx: 4.23.1 typescript: '@typescript/typescript6@6.0.2' + unrun: 0.3.1 transitivePeerDependencies: - '@ts-macro/tsc' - '@typescript/native-preview' @@ -11608,6 +11830,10 @@ snapshots: universalify@2.0.1: {} + unrun@0.3.1: + dependencies: + rolldown: 1.1.5 + until-async@3.0.2: {} update-browserslist-db@1.2.3(browserslist@4.28.6): @@ -11670,10 +11896,10 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: debug: 4.4.3 - globrex: 0.1.2 + oxc-resolver: 11.24.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) transitivePeerDependencies: From 81db43553fe85be29a94bc6548b03e3ab81bae43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:34:04 +0800 Subject: [PATCH 0308/1190] chore(deps): bump nixpkgs from `d407951` to `e7a3ca8` (#22702) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `d407951` to `e7a3ca8`. - [Commits](https://github.com/NixOS/nixpkgs/compare/d407951447dcd00442e97087bf374aad70c04cea...e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index a4d516ae5730..03e2f3060215 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1783224372, - "narHash": "sha256-8i/87eeoqiGE4yOTjwSA3Eh/ziJRQEmd/unYU+K27sk=", + "lastModified": 1783776592, + "narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d407951447dcd00442e97087bf374aad70c04cea", + "rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3", "type": "github" }, "original": { From a6708ba74a62d6f164ee52a27315106592ab3559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:01:15 +0800 Subject: [PATCH 0309/1190] chore(deps): bump devenv from `d1fb321` to `407080f` (#22703) Bumps [devenv](https://github.com/cachix/devenv) from `d1fb321` to `407080f`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/d1fb321e73048d0e1e2b523580268ebb3df2ae90...407080febcc800abfd0fd688a0d513884aad620c) --- updated-dependencies: - dependency-name: devenv dependency-version: 407080febcc800abfd0fd688a0d513884aad620c dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 03e2f3060215..b91aca95ad01 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1783538213, - "narHash": "sha256-jNjKlmrejUrBgVAeiavlMLlkmC7ZEv1D9nhfuwMW5Q8=", + "lastModified": 1783911906, + "narHash": "sha256-VKpbIaPO4OkONY88DQHD/nOPSkV6o4wWMra7E4faaXU=", "owner": "cachix", "repo": "devenv", - "rev": "d1fb321e73048d0e1e2b523580268ebb3df2ae90", + "rev": "407080febcc800abfd0fd688a0d513884aad620c", "type": "github" }, "original": { From 9d85d4449254b06bfe67981a59068a71524d32d0 Mon Sep 17 00:00:00 2001 From: Dzming Li Date: Mon, 13 Jul 2026 19:21:57 +0800 Subject: [PATCH 0310/1190] fix(route/zhihu): generate __zse_ck with JSDOM (#22319) * fix(route/zhihu): obtain __zse_ck from a browser session `__zse_ck` is computed at runtime by Zhihu's JS from the device fingerprint and `d_c0`, rotates every few days, and is cross-checked against `d_c0` by the backend, so it has to come from a real browser session. Drive a browser seeded with the configured cookies (including the `z_c0` login cookie that most endpoints now require) to compute a fresh, consistent `__zse_ck`, harvest the cookie jar, and cache it for 30 minutes so it is refreshed automatically. Recommended ZHIHU_COOKIES: "d_c0=...; z_c0=..." (omit `__zse_ck`). * fix(route/zhihu): fetch posts profile via API instead of HTML The user's HTML homepage (www.zhihu.com/people/:id) is now rate-limited (403) more aggressively than the API, which broke /zhihu/posts on the profile fetch even though the article-list API works. Read the profile (name, headline, avatar) from /api/v4/members/:id instead. * fix(route/zhihu): generate __zse_ck with JSDOM * fix(route/zhihu): use members API for org posts --------- Co-authored-by: DzmingLi --- lib/routes/zhihu/posts.ts | 23 ++-- lib/routes/zhihu/utils.ts | 231 +++++++++++++++++++++++++++----------- 2 files changed, 181 insertions(+), 73 deletions(-) diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 4be2629e3367..1366b4c9c929 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -1,11 +1,9 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import type { Articles, Profile } from './types'; +import type { Articles } from './types'; import { getSignedHeader, header, processImage } from './utils'; export const route: Route = { @@ -45,21 +43,26 @@ async function handler(ctx) { const usertype = ctx.req.param('usertype'); const userProfile = await cache.tryGet(`zhihu:posts:profile:${id}`, async () => { - const userAPIPath = `/${usertype === 'people' ? 'people' : 'org'}/${id}`; + // Read the profile from the API instead of scraping the user's HTML + // homepage, which is now rate-limited (403) more aggressively than the API. + const profileApiPath = `/api/v4/members/${id}`; - const result = await ofetch(`https://www.zhihu.com${userAPIPath}`, { + const result = await ofetch(`https://www.zhihu.com${profileApiPath}`, { headers: { ...header, - ...(await getSignedHeader(`https://www.zhihu.com/${usertype}/${id}/`, userAPIPath)), + ...(await getSignedHeader(`https://www.zhihu.com/${usertype}/${id}/`, profileApiPath)), Referer: `https://www.zhihu.com/${usertype}/${id}/`, }, }); - const $ = load(result); - const data = JSON.parse($('#js-initialData').text()); - return data?.initialState?.entities?.users[id] as Profile; + + return { + name: result.name, + headline: result.headline, + avatarUrl: result.avatar_url, + }; }); - const apiPath = `/api/v4/${usertype === 'people' ? 'members' : 'org'}/${id}/articles?${new URLSearchParams({ + const apiPath = `/api/v4/members/${id}/articles?${new URLSearchParams({ include: 'data[*].comment_count,suggest_edit,is_normal,thumbnail_extra_info,thumbnail,can_comment,comment_permission,admin_closed_comment,content,voteup_count,created,updated,upvoted_followees,voting,review_info,reaction_instruction,is_labeled,label_info;data[*].vessay_info;data[*].author.badge[?(type=best_answerer)].topics;data[*].author.vip_info;', offset: '0', diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index 296b28bdf7d9..ab2422897ea8 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -1,9 +1,14 @@ +import { Script } from 'node:vm'; + import { load } from 'cheerio'; +import { JSDOM, VirtualConsole } from 'jsdom'; import { config } from '@/config'; import cache from '@/utils/cache'; +import { generateHeaders } from '@/utils/header-generator'; import md5 from '@/utils/md5'; import ofetch from '@/utils/ofetch'; +import wait from '@/utils/wait'; import { encrypt as g_encrypt } from './execlib/x-zse-96-v3'; @@ -58,87 +63,187 @@ export const processImage = (content: string) => { return $.html(); }; -export const getCookieValueByKey = (key: string) => - config.zhihu.cookies +const getCookieValueFrom = (cookieStr: string | undefined, key: string) => + cookieStr ?.split(';') .map((e) => e.trim()) .find((e) => e.startsWith(key + '=')) ?.slice(key.length + 1) || ''; -export const getSignedHeader = async (url: string, apiPath: string) => { - if (config?.zhihu?.cookies) { - const dc0 = getCookieValueByKey('d_c0'); - - const xzse93 = '101_3_3.0'; - const f = `${xzse93}+${apiPath}+${dc0}`; - const xzse96 = '2.0_' + g_encrypt(md5(f)); - - // If __zse_ck is absent from ZHIHU_COOKIES, fetch it automatically from - // Zhihu's public static JS. The value is site-wide (not user-specific) - // and requires no login, but it expires and must be kept up to date. - let cookieStr = config.zhihu.cookies; - if (!getCookieValueByKey('__zse_ck')) { - const zseCk = await cache.tryGet('zhihu:zse_ck', async () => { - const response = await ofetch.raw('https://static.zhihu.com/zse-ck/v3.js'); - const script = await response._data.text(); - return script.match(/__g\.ck\|\|"([\w+/=\\]*)",_=/)?.[1] || ''; - }); - if (zseCk) { - cookieStr += `; __zse_ck=${zseCk}`; - } - } +export const getCookieValueByKey = (key: string) => getCookieValueFrom(config.zhihu.cookies as string | undefined, key); - return { - cookie: cookieStr, - 'x-zse-96': xzse96, - 'x-app-za': 'OS=Web', - 'x-zse-93': xzse93, - }; +let isUnreachableRuntimeErrorGuarded = false; +const pendingZseCredentials = new Map>(); + +const preventUnreachableRuntimeError = () => { + if (isUnreachableRuntimeErrorGuarded) { + return; } - // NOTICE: this method is out of date. - // Because the API of zhihu.com has changed, we must use the value of `d_c0` (extracted from cookies) to calculate - // `x-zse-96`. So first get `d_c0`, then get the actual data of a ZhiHu question. In this way, we don't need to - // require users to set the cookie in environmental variables anymore. - - // fisrt: get cookie(dc_0) from zhihu.com - const { dc0, zseCk } = await cache.tryGet('zhihu:cookies:d_c0', async () => { - if (getCookieValueByKey('d_c0') && getCookieValueByKey('__zse_ck')) { - return { dc0: getCookieValueByKey('d_c0'), zseCk: getCookieValueByKey('__zse_ck') }; + isUnreachableRuntimeErrorGuarded = true; + process.on('unhandledRejection', (reason) => { + const error = reason as { name?: string; message?: string } | undefined; + if (error?.name === 'RuntimeError' && error.message === 'unreachable') { + return; } - const response1 = await ofetch.raw('https://static.zhihu.com/zse-ck/v3.js'); - const script = await response1._data.text(); - const zseCk = script.match(/__g\.ck\|\|"([\w+/=\\]*)",_=/)?.[1]; - const response2 = zseCk - ? await ofetch.raw(url, { - headers: { - cookie: `${response1.headers - .getSetCookie() - .map((s) => s.split(';', 1)[0]) - .join('; ')}; __zse_ck=${zseCk}`, - }, - }) - : null; - - const dc0 = - (response2 || response1).headers - .getSetCookie() - .find((s) => s.startsWith('d_c0=')) + throw reason; + }); +}; + +const generateZseCk = async (url: string, apiPath: string, configuredDc0: string) => { + preventUnreachableRuntimeError(); + + // `__zse_ck` is checked against the user-agent that generated it. + const ua = generateHeaders()['user-agent']; + const headers = { 'user-agent': ua }; + + let dc0 = configuredDc0; + if (!dc0) { + const seed = await ofetch.raw('https://www.zhihu.com/explore', { + headers, + redirect: 'manual', + ignoreResponseError: true, + }); + dc0 = + (seed.headers.getSetCookie?.() ?? []) + .find((line) => line.startsWith('d_c0=')) ?.split(';', 1)[0] - .trim() .slice('d_c0='.length) || ''; + } + if (!dc0) { + throw new Error('zhihu: failed to obtain a guest d_c0 cookie'); + } - return { dc0, zseCk }; + const challenge = await ofetch.raw(`https://www.zhihu.com${apiPath}`, { + headers: { + ...headers, + cookie: `d_c0=${dc0}; __zse_ck=005_x-x`, + referer: url, + 'x-requested-with': 'fetch', + }, + ignoreResponseError: true, }); + const html = challenge._data as string; + const meta = html.match(/id="zh-zse-ck"[^>]*content="([^"]*)"/)?.[1]; + const hash = html.match(/zse-ck\/v4\/([a-f0-9]+)\.js/)?.[1]; + if (!meta || !hash) { + throw new Error('zhihu: challenge page did not contain an URL to __zse_ck meta/script'); + } - // calculate x-zse-96, refer to https://github.com/srx-2000/spider_collection/issues/18 + const vmScript = await ofetch(`https://static.zhihu.com/zse-ck/v4/${hash}.js`, { + headers, + parseResponse: (text) => text, + }); + const dom = new JSDOM(``, { + url, + referrer: 'https://www.zhihu.com/', + runScripts: 'outside-only', + pretendToBeVisual: true, + virtualConsole: new VirtualConsole(), + }); + const { window } = dom; + Object.defineProperties(window.navigator, { + userAgent: { value: ua, configurable: true }, + webdriver: { value: false, configurable: true }, + }); + window.TextEncoder = TextEncoder; + window.TextDecoder = TextDecoder as typeof window.TextDecoder; + window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary'); + window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64'); + Object.assign(window, { __g: {} }); + + const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie'); + if (!cookieDescriptor?.get || !cookieDescriptor.set) { + window.close(); + throw new Error('zhihu: JSDOM did not provide document.cookie accessors'); + } + const tokenPromise = new Promise((resolve) => { + Object.defineProperty(window.document, 'cookie', { + configurable: true, + get: cookieDescriptor.get, + set(value: string) { + Reflect.apply(cookieDescriptor.set, window.document, [value]); + const token = value.match(/__zse_ck=([^;]+)/)?.[1]; + if (token?.includes('-')) { + resolve(token); + } + }, + }); + }); + + let zseCk: string | undefined; + try { + // Zhihu's challenge is intentionally delivered as executable JavaScript. + new Script(vmScript).runInContext(dom.getInternalVMContext()); + zseCk = (await Promise.race([tokenPromise, wait(3000)])) as string | undefined; + } finally { + window.close(); + } + if (!zseCk) { + throw new Error('zhihu: WASM VM did not produce a __zse_ck'); + } + return { dc0, zseCk, ua }; +}; + +const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: string) => { + const cacheKey = `zhihu:zse-ck:v4:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; + const pending = pendingZseCredentials.get(cacheKey); + if (pending) { + return pending; + } + + const created = (async () => { + try { + return await cache.tryGet(cacheKey, () => generateZseCk(url, apiPath, configuredDc0), config.cache.contentExpire, false); + } finally { + pendingZseCredentials.delete(cacheKey); + } + })(); + pendingZseCredentials.set(cacheKey, created); + return created; +}; + +const mergeGeneratedCookies = (configured: string, dc0: string, zseCk: string) => { + const remaining = configured + .split(';') + .map((pair) => pair.trim()) + .filter((pair) => { + const name = pair.split('=', 1)[0]; + return name && name !== 'd_c0' && name !== '__zse_ck'; + }); + return [`__zse_ck=${zseCk}`, `d_c0=${dc0}`, ...remaining].join('; '); +}; + +export const getSignedHeader = async (url: string, apiPath: string) => { + const configured = (config?.zhihu?.cookies as string | undefined) || ''; + + const configuredDc0 = getCookieValueFrom(configured, 'd_c0'); + const configuredZseCk = getCookieValueFrom(configured, '__zse_ck'); + + // A configured pair may have been generated with a different user-agent, so + // preserve the previous behavior and trust it as-is. Generated credentials + // always return their matching user-agent. + let cookieStr: string; + let ua: string | undefined; + if (configuredDc0 && configuredZseCk) { + cookieStr = configured; + } else { + const credentials = await getGeneratedZseCredentials(url, apiPath, configuredDc0); + // Login cookies only belong to the configured d_c0 session. Do not mix + // an isolated z_c0 with a newly-created guest session. + cookieStr = configuredDc0 ? mergeGeneratedCookies(configured, credentials.dc0, credentials.zseCk) : `__zse_ck=${credentials.zseCk}; d_c0=${credentials.dc0}`; + ua = credentials.ua; + } + + // Sign with the same `d_c0` that is sent, otherwise the backend rejects the + // request. Refer to https://github.com/srx-2000/spider_collection/issues/18 + const dc0 = getCookieValueFrom(cookieStr, 'd_c0'); const xzse93 = '101_3_3.0'; const f = `${xzse93}+${apiPath}+${dc0}`; const xzse96 = '2.0_' + g_encrypt(md5(f)); - const zc0 = getCookieValueByKey('z_c0'); - return { - cookie: `__zse_ck=${zseCk}; d_c0=${dc0}${zc0 ? `;z_c0=${zc0}` : ''}`, + cookie: cookieStr, + ...(ua && { 'user-agent': ua }), 'x-zse-96': xzse96, 'x-app-za': 'OS=Web', 'x-zse-93': xzse93, From ecfefda0fe97b7b26c05c6b3bc491c137efb9e42 Mon Sep 17 00:00:00 2001 From: Yulong Ming Date: Mon, 13 Jul 2026 22:35:39 +0800 Subject: [PATCH 0311/1190] fix(route/zaobao): update finance route path (#22705) --- lib/routes/zaobao/realtime.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/routes/zaobao/realtime.ts b/lib/routes/zaobao/realtime.ts index 1ce55e27f9d6..576082a4ed87 100644 --- a/lib/routes/zaobao/realtime.ts +++ b/lib/routes/zaobao/realtime.ts @@ -37,9 +37,7 @@ async function handler(ctx) { case 'zfinance': name = '财经'; - // this is for HK version; for SG version, it's redirected to - // /realtime/finance - sectionLink = '/finance/realtime'; + sectionLink = '/realtime/finance'; break; From 66b2c550b4de85d0e43da31d59a8b681eab41d66 Mon Sep 17 00:00:00 2001 From: TonyRL Date: Tue, 14 Jul 2026 00:37:46 +0800 Subject: [PATCH 0312/1190] chore: fix typing --- .oxlintrc.json | 8 ++++++++ lib/routes/ai-bot/daily-ai-news.ts | 8 +++----- lib/routes/dailypush/utils.ts | 11 ++++++----- lib/routes/f95zone/thread.ts | 4 ++-- lib/routes/gigazine/en.ts | 6 +++--- lib/routes/whu/rsgis.ts | 5 +++-- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 6a3ed7f705a3..b937c76587cd 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -602,6 +602,14 @@ { "selector": "CallExpression[callee.property.name=\"map\"] > FunctionExpression", "message": "Use an arrow function instead." + }, + { + "selector": "TSTypeReference[typeName.name='ReturnType'] TSTypeQuery[exprName.name='load']", + "message": "Usage of ReturnType is not allowed. Please use appropriate types from cheerio instead." + }, + { + "selector": "TSTypeReference[typeName.name='ReturnType'] TSTypeReference[typeName.name='CheerioAPI']", + "message": "Usage of ReturnType is not allowed. Please use appropriate types from cheerio and domhandler instead." } ], diff --git a/lib/routes/ai-bot/daily-ai-news.ts b/lib/routes/ai-bot/daily-ai-news.ts index 13f42a0cf2ba..d40cb3a59618 100755 --- a/lib/routes/ai-bot/daily-ai-news.ts +++ b/lib/routes/ai-bot/daily-ai-news.ts @@ -1,13 +1,11 @@ -import { load } from 'cheerio'; +import { type Cheerio, type CheerioAPI, load } from 'cheerio'; +import type { Element } from 'domhandler'; import type { Data, DataItem, Route } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -type CheerioInstance = ReturnType; -type CheerioSelection = ReturnType; - interface DateContext { currentYear: number; prevMonth: number; @@ -36,7 +34,7 @@ function parseDateString(dateStr: string, ctx: DateContext): Date | undefined { return timezone(parseDate(`${ctx.currentYear}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`), 8); } -function processNewsList($: CheerioInstance, $newsList: CheerioSelection, ctx: DateContext): DataItem[] { +function processNewsList($: CheerioAPI, $newsList: Cheerio, ctx: DateContext): DataItem[] { let currentPubDate: Date | undefined; return $newsList diff --git a/lib/routes/dailypush/utils.ts b/lib/routes/dailypush/utils.ts index 134eca52ff32..695f0cf756ed 100644 --- a/lib/routes/dailypush/utils.ts +++ b/lib/routes/dailypush/utils.ts @@ -1,5 +1,6 @@ -import type { CheerioAPI } from 'cheerio'; +import type { Cheerio, CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; +import type { Element } from 'domhandler'; import type { BrowserContext } from 'patchright'; import type { DataItem } from '@/types'; @@ -68,7 +69,7 @@ function tryParseAsDate(text: string): Date | undefined { /** * Extract author from article element */ -function extractAuthor(article: ReturnType): DataItem['author'] { +function extractAuthor(article: Cheerio): DataItem['author'] { const container = article.find('.flex.items-center.gap-3').first(); if (container.length === 0) { return undefined; @@ -134,7 +135,7 @@ function extractAuthor(article: ReturnType): DataItem['author'] { /** * Extract categories/tags from article element */ -function extractCategories(article: ReturnType, $: CheerioAPI): string[] { +function extractCategories(article: Cheerio, $: CheerioAPI): string[] { return article .find('a[href^="/"]') .toArray() @@ -155,7 +156,7 @@ function extractCategories(article: ReturnType, $: CheerioAPI): stri /** * Extract publication date from article element */ -function extractPubDate(article: ReturnType): Date | undefined { +function extractPubDate(article: Cheerio): Date | undefined { const container = article.find('.flex.items-center.gap-3').first(); if (container.length === 0) { return undefined; @@ -208,7 +209,7 @@ function extractPubDate(article: ReturnType): Date | undefined { /** * Parse a single article element into an ArticleItem */ -function parseArticle(article: ReturnType, $: CheerioAPI, baseUrl: string): (DataItem & ArticleItem) | null { +function parseArticle(article: Cheerio, $: CheerioAPI, baseUrl: string): (DataItem & ArticleItem) | null { // Find the title link in h2 > a const titleLink = article.find('h2 a[href^="http"]'); if (titleLink.length === 0) { diff --git a/lib/routes/f95zone/thread.ts b/lib/routes/f95zone/thread.ts index 97c3b3963365..6bbf15b30eab 100644 --- a/lib/routes/f95zone/thread.ts +++ b/lib/routes/f95zone/thread.ts @@ -1,4 +1,4 @@ -import { load } from 'cheerio'; +import { type CheerioAPI, load } from 'cheerio'; import { config } from '@/config'; import type { DataItem, Route } from '@/types'; @@ -61,7 +61,7 @@ Note: If you want to track a specific post's content changes (e.g., first post w const lastPageLink = $firstPage('ul.pageNav-main li.pageNav-page:last-child a').attr('href'); const totalPages = lastPageLink ? Number(lastPageLink.match(/page-(\d+)/)?.[1] || '1') : 1; - const extractPosts = ($: ReturnType): DataItem[] => + const extractPosts = ($: CheerioAPI): DataItem[] => $('article.message') .toArray() .flatMap((article) => { diff --git a/lib/routes/gigazine/en.ts b/lib/routes/gigazine/en.ts index 276ed27a800d..baa148eebc64 100644 --- a/lib/routes/gigazine/en.ts +++ b/lib/routes/gigazine/en.ts @@ -1,4 +1,4 @@ -import { load } from 'cheerio'; +import { type CheerioAPI, load } from 'cheerio'; import pMap from 'p-map'; import type { DataItem, Route } from '@/types'; @@ -19,12 +19,12 @@ const getRequestOptions = (referer: string) => ({ }, }); const getAbsoluteUrl = (path: string | undefined) => (path ? new URL(path, ROOT_URL).href : undefined); -const getArticleAuthor = ($: ReturnType) => +const getArticleAuthor = ($: CheerioAPI) => $('#article .items p') .text() .match(/Posted by\s+(\S.*)$/)?.[1] ?.trim(); -const getArticleCategories = ($: ReturnType) => [ +const getArticleCategories = ($: CheerioAPI) => [ ...new Set( $('#article .items p a[href*="/gsc_news/en/C"]') .toArray() diff --git a/lib/routes/whu/rsgis.ts b/lib/routes/whu/rsgis.ts index 283a7661100f..c9ce6eb71255 100644 --- a/lib/routes/whu/rsgis.ts +++ b/lib/routes/whu/rsgis.ts @@ -1,5 +1,6 @@ -import type { AnyNode, Cheerio } from 'cheerio'; +import type { Cheerio } from 'cheerio'; import { load } from 'cheerio'; +import type { Element } from 'domhandler'; import type { Context } from 'hono'; import type { DataItem, Route } from '@/types'; @@ -103,7 +104,7 @@ function checkExternal(link: string): boolean { * @param element * @returns A list of RSS meta node. */ -function parseListLinkDateItem(element: Cheerio, currentUrl: string) { +function parseListLinkDateItem(element: Cheerio, currentUrl: string) { const linkElement = element.find('a').first(); const title = linkElement.text(); const href = linkElement.attr('href'); From 875dbcec7464f31e63e5b49edfa16d649e8b285f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 08:15:41 +0000 Subject: [PATCH 0313/1190] chore(deps): bump @notionhq/client from 5.23.0 to 5.23.1 (#22714) Bumps [@notionhq/client](https://github.com/makenotion/notion-sdk-js) from 5.23.0 to 5.23.1. - [Release notes](https://github.com/makenotion/notion-sdk-js/releases) - [Commits](https://github.com/makenotion/notion-sdk-js/compare/v5.23.0...v5.23.1) --- updated-dependencies: - dependency-name: "@notionhq/client" dependency-version: 5.23.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 8ebb54a98297..e296b0983bab 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@hono/node-server": "2.0.8", "@hono/zod-openapi": "1.4.0", "@jocmp/mercury-parser": "3.0.9", - "@notionhq/client": "5.23.0", + "@notionhq/client": "5.23.1", "@opentelemetry/api": "1.9.1", "@opentelemetry/exporter-prometheus": "0.220.0", "@opentelemetry/exporter-trace-otlp-http": "0.220.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59691ad2faba..68a7298136e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,8 +56,8 @@ importers: specifier: 3.0.9 version: 3.0.9 '@notionhq/client': - specifier: 5.23.0 - version: 5.23.0 + specifier: 5.23.1 + version: 5.23.1 '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -1463,8 +1463,8 @@ packages: resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==} engines: {node: '>=12.4.0'} - '@notionhq/client@5.23.0': - resolution: {integrity: sha512-vTlkart40A89+l7+aRGNXLr+z3Af94dbaj/PFtommcwqCxWF9aByXmKgCGteD77It8u4ePE1DSL8eIVSANFGPg==} + '@notionhq/client@5.23.1': + resolution: {integrity: sha512-Y0jhh0ulft1x1c3uHFzBMHHQZzdfpCUV5JNFAZxzmjKEIkephQ0fle0g0ZqxTmHl1vP1ZOjzq0v47Ao4udBevw==} engines: {node: '>=18'} '@octokit/auth-token@6.0.0': @@ -5263,6 +5263,10 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.48: resolution: {integrity: sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==} engines: {node: '>= 16'} @@ -7210,7 +7214,7 @@ snapshots: '@nolyfill/side-channel@1.0.44': {} - '@notionhq/client@5.23.0': {} + '@notionhq/client@5.23.1': {} '@octokit/auth-token@6.0.0': {} @@ -10983,6 +10987,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.19: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.48: dependencies: '@postman/form-data': 3.1.1 @@ -11911,7 +11921,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.18 + postcss: 8.5.19 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: From cbdaca214037b5edb3cfe9cbe4bfbaeaf8e66ad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:02:59 +0800 Subject: [PATCH 0314/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22710) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260713.1 to 5.20260714.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260714.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index e296b0983bab..dc352642ba82 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.4", - "@cloudflare/workers-types": "5.20260713.1", + "@cloudflare/workers-types": "5.20260714.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.73.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68a7298136e6..e6682971bda2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.4 - version: 0.18.4(@cloudflare/workers-types@5.20260713.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.4(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260713.1 - version: 5.20260713.1 + specifier: 5.20260714.1 + version: 5.20260714.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -475,7 +475,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.110.0 - version: 4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -644,8 +644,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260713.1': - resolution: {integrity: sha512-4YZE9YIzr3iP9eRUV4ekZozLLsYQFFKxmMJfAcsteCuzKJ7RJrLqcCxm1aMzTE9uFcy+D6A89WE+cjJadF2c9g==} + '@cloudflare/workers-types@5.20260714.1': + resolution: {integrity: sha512-HGCTQVIQwzqAMLrZBCgLDrWKMgOdi6yn+S0Do1rF6z7t8tVvNprQfa53V6EDRRwsVO92uN5gviX2SxASDINZfA==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3578,7 +3578,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -6580,7 +6580,7 @@ snapshots: optionalDependencies: workerd: 1.20260708.1 - '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260713.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6588,7 +6588,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6610,7 +6610,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260708.1': optional: true - '@cloudflare/workers-types@5.20260713.1': {} + '@cloudflare/workers-types@5.20260714.1': {} '@colors/colors@1.6.0': {} @@ -12044,7 +12044,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260708.1 '@cloudflare/workerd-windows-64': 1.20260708.1 - wrangler@4.110.0(@cloudflare/workers-types@5.20260713.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260708.1) @@ -12055,7 +12055,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260708.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260713.1 + '@cloudflare/workers-types': 5.20260714.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From ce488d728c6c400b879e31b74bc428341c38b5fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:18:00 +0800 Subject: [PATCH 0315/1190] chore(deps): bump devenv from `407080f` to `32f6747` (#22716) Bumps [devenv](https://github.com/cachix/devenv) from `407080f` to `32f6747`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/407080febcc800abfd0fd688a0d513884aad620c...32f6747aabbd5aeb7413bae53d7e01e224ec77bc) --- updated-dependencies: - dependency-name: devenv dependency-version: 32f6747aabbd5aeb7413bae53d7e01e224ec77bc dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index b91aca95ad01..25853472584b 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1783911906, - "narHash": "sha256-VKpbIaPO4OkONY88DQHD/nOPSkV6o4wWMra7E4faaXU=", + "lastModified": 1783972995, + "narHash": "sha256-BIti2dNiCWjOpemU6z0sKaSa214XwdynXckvX5Sirkk=", "owner": "cachix", "repo": "devenv", - "rev": "407080febcc800abfd0fd688a0d513884aad620c", + "rev": "32f6747aabbd5aeb7413bae53d7e01e224ec77bc", "type": "github" }, "original": { @@ -226,11 +226,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1780381423, - "narHash": "sha256-S1BIJiQF4lRtJUKak0e97JwNvbe69/LW78YJJuB18Qk=", + "lastModified": 1783935112, + "narHash": "sha256-IAQ14nteIKXAz4cd75UcZrsHGEVJ7QNrkUwX9rmeZ/Y=", "owner": "nix-community", "repo": "nixd", - "rev": "0e07c08c448a2995e7793d1098437b29bbe80b02", + "rev": "a64cd33e53b316b6b092ea0a966640cd2309bf3d", "type": "github" }, "original": { From 42731bce179d81e86a995fb1b630d7bcddc6b37d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:27:44 +0800 Subject: [PATCH 0316/1190] chore(deps): bump @hono/zod-openapi from 1.4.0 to 1.5.0 (#22715) Bumps [@hono/zod-openapi](https://github.com/honojs/middleware/tree/HEAD/packages/zod-openapi) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/honojs/middleware/releases) - [Changelog](https://github.com/honojs/middleware/blob/main/packages/zod-openapi/CHANGELOG.md) - [Commits](https://github.com/honojs/middleware/commits/@hono/zod-openapi@1.5.0/packages/zod-openapi) --- updated-dependencies: - dependency-name: "@hono/zod-openapi" dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index dc352642ba82..336fe6f27fca 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.14.0", "@hono/node-server": "2.0.8", - "@hono/zod-openapi": "1.4.0", + "@hono/zod-openapi": "1.5.0", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.1", "@opentelemetry/api": "1.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6682971bda2..af5b302426b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,8 +50,8 @@ importers: specifier: 2.0.8 version: 2.0.8(hono@4.12.30) '@hono/zod-openapi': - specifier: 1.4.0 - version: 1.4.0(hono@4.12.30)(zod@4.4.3) + specifier: 1.5.0 + version: 1.5.0(hono@4.12.30)(zod@4.4.3) '@jocmp/mercury-parser': specifier: 3.0.9 version: 3.0.9 @@ -1098,8 +1098,8 @@ packages: peerDependencies: hono: ^4 - '@hono/zod-openapi@1.4.0': - resolution: {integrity: sha512-AFchqR1N/NxfI4hUOSGI2/g8zLROxA1OE7Oh5JJFlTaGxhrdRyH+93gd0tIBpb0z8s9r8hUoNnaOBfHbdb4NMw==} + '@hono/zod-openapi@1.5.0': + resolution: {integrity: sha512-/clSBZWBht7G4SItlTo+4ElbdIUw+987kZYttaUUJWksJoqK905z1pBNTwoJm/P1ibA2ka8nNbqb39eOlreH2g==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.10.0' @@ -6908,7 +6908,7 @@ snapshots: dependencies: hono: 4.12.30 - '@hono/zod-openapi@1.4.0(hono@4.12.30)(zod@4.4.3)': + '@hono/zod-openapi@1.5.0(hono@4.12.30)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) '@hono/zod-validator': 0.8.0(hono@4.12.30)(zod@4.4.3) From 07df8508d4a2762adca19d1a849c8fb0797de265 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:33:06 +0800 Subject: [PATCH 0317/1190] chore(deps-dev): bump the typescript-eslint group with 2 updates (#22713) Bumps the typescript-eslint group with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 8.63.0 to 8.64.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.63.0 to 8.64.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.64.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint - dependency-name: "@typescript-eslint/parser" dependency-version: 8.64.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 118 ++++++++++++++++++++++++++----------------------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 336fe6f27fca..499e4e3cf549 100644 --- a/package.json +++ b/package.json @@ -169,8 +169,8 @@ "@types/module-alias": "2.0.4", "@types/node": "26.1.1", "@types/sanitize-html": "2.16.1", - "@typescript-eslint/eslint-plugin": "8.63.0", - "@typescript-eslint/parser": "8.63.0", + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", "@vercel/nft": "1.10.2", "@vitest/coverage-v8": "4.1.10", "discord-api-types": "0.38.49", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af5b302426b7..5a4e27612a13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,11 +357,11 @@ importers: specifier: 2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: 8.63.0 - version: 8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) + specifier: 8.64.0 + version: 8.64.0(@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@typescript-eslint/parser': - specifier: 8.63.0 - version: 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + specifier: 8.64.0 + version: 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@vercel/nft': specifier: 1.10.2 version: 1.10.2(rollup@4.62.2) @@ -2698,39 +2698,39 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.63.0': - resolution: {integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==} + '@typescript-eslint/eslint-plugin@8.64.0': + resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.63.0 + '@typescript-eslint/parser': ^8.64.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.63.0': - resolution: {integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==} + '@typescript-eslint/parser@8.64.0': + resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.63.0': - resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} + '@typescript-eslint/project-service@8.64.0': + resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.63.0': - resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} + '@typescript-eslint/scope-manager@8.64.0': + resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.63.0': - resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} + '@typescript-eslint/tsconfig-utils@8.64.0': + resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.63.0': - resolution: {integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==} + '@typescript-eslint/type-utils@8.64.0': + resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2740,21 +2740,25 @@ packages: resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.63.0': - resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} + '@typescript-eslint/types@8.64.0': + resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.64.0': + resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.63.0': - resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} + '@typescript-eslint/utils@8.64.0': + resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.63.0': - resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} + '@typescript-eslint/visitor-keys@8.64.0': + resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': @@ -8144,14 +8148,14 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/type-utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/parser': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/type-utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/visitor-keys': 8.64.0 eslint: 10.7.0 ignore: 7.0.6 natural-compare: 1.4.0 @@ -8160,41 +8164,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: - '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/visitor-keys': 8.64.0 debug: 4.4.3 eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.63.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.64.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/tsconfig-utils': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.64.0 debug: 4.4.3 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.63.0': + '@typescript-eslint/scope-manager@8.64.0': dependencies: - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 - '@typescript-eslint/tsconfig-utils@8.63.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/tsconfig-utils@8.64.0(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/type-utils@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) debug: 4.4.3 eslint: 10.7.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8204,12 +8208,14 @@ snapshots: '@typescript-eslint/types@8.63.0': {} - '@typescript-eslint/typescript-estree@8.63.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/types@8.64.0': {} + + '@typescript-eslint/typescript-estree@8.64.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/project-service': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/tsconfig-utils': 8.63.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/project-service': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -8219,20 +8225,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.63.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/utils@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) - '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/typescript-estree': 8.63.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.63.0': + '@typescript-eslint/visitor-keys@8.64.0': dependencies: - '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/types': 8.64.0 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': From 7daf53788aea2957ccc9ef0e85e018069ff669ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:40:04 +0800 Subject: [PATCH 0318/1190] chore(deps-dev): bump the oxc group across 1 directory with 5 updates (#22712) Bumps the oxc group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.73.0` | `1.74.0` | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.139.0` | `0.140.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.58.0` | `0.59.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.73.0` | `1.74.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.73.0` | `1.74.0` | Updates `@oxlint/plugins` from 1.73.0 to 1.74.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.74.0/npm/oxlint-plugins) Updates `oxc-parser` from 0.139.0 to 0.140.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.140.0/napi/parser) Updates `oxfmt` from 0.58.0 to 0.59.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.59.0/npm/oxfmt) Updates `oxlint` from 1.73.0 to 1.74.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.74.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.73.0 to 1.74.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.74.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.74.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.140.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.59.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.74.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.74.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +- pnpm-lock.yaml | 529 +++++++++++++++++++++++++------------------------ 2 files changed, 272 insertions(+), 267 deletions(-) diff --git a/package.json b/package.json index 499e4e3cf549..6b9f085ec27d 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "@cloudflare/workers-types": "5.20260714.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.73.0", + "@oxlint/plugins": "1.74.0", "@stylistic/eslint-plugin": "5.10.0", "@types/babel__preset-env": "7.10.0", "@types/crypto-js": "4.2.2", @@ -192,10 +192,10 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.139.0", - "oxfmt": "0.58.0", - "oxlint": "1.73.0", - "oxlint-plugin-eslint": "1.73.0", + "oxc-parser": "0.140.0", + "oxfmt": "0.59.0", + "oxlint": "1.74.0", + "oxlint-plugin-eslint": "1.74.0", "oxlint-tsgolint": "0.24.0", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a4e27612a13..c3b4000c941f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,8 +306,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.7.0) '@oxlint/plugins': - specifier: 1.73.0 - version: 1.73.0 + specifier: 1.74.0 + version: 1.74.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.7.0) @@ -426,17 +426,17 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.7.0)(utf-8-validate@5.0.10) oxc-parser: - specifier: 0.139.0 - version: 0.139.0 + specifier: 0.140.0 + version: 0.140.0 oxfmt: - specifier: 0.58.0 - version: 0.58.0 + specifier: 0.59.0 + version: 0.59.0 oxlint: - specifier: 1.73.0 - version: 1.73.0(oxlint-tsgolint@0.24.0) + specifier: 1.74.0 + version: 1.74.0(oxlint-tsgolint@0.24.0) oxlint-plugin-eslint: - specifier: 1.73.0 - version: 1.73.0 + specifier: 1.74.0 + version: 1.74.0 oxlint-tsgolint: specifier: 0.24.0 version: 0.24.0 @@ -1624,129 +1624,129 @@ packages: '@otplib/uri@13.4.1': resolution: {integrity: sha512-xaIm7bvICMhoB2rZIR5luiaMdssWR5nY5nXnR1fdezUgZuEO58D6zrGzLp7pQuBmlpmL0HagnscDQFoskp9yiA==} - '@oxc-parser/binding-android-arm-eabi@0.139.0': - resolution: {integrity: sha512-22EsXTA3Vc7OvrF4bfT48PFln2UbxkVgrp/Tm32qLw76Dv7SmcInfClJe6yPYamnli6HiqasnESZ5ezN+X4ybg==} + '@oxc-parser/binding-android-arm-eabi@0.140.0': + resolution: {integrity: sha512-ZfjDZ422mo7eo3b3VltqNsV9kmv1qt/sPEAMSl64iOSwhVfd0eIZ9LB79Mbs1xYXJnk7WSROwzBCKDIiVxPTvQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.139.0': - resolution: {integrity: sha512-uASQkZV+CUQ6xXvoMzDQyfhd8OMusdv2FyCPfAi5ZDYptesapJlw7erhpGi1Lc+U8QjQV2JUrIh7d/3su2LzmQ==} + '@oxc-parser/binding-android-arm64@0.140.0': + resolution: {integrity: sha512-Ia8jSvikUX6Sf+Ht+KOCUF/k1HpR0VlmqIYymubmWDebOEGtsyliHDR6JxsZ4IX3/c/GbrB1uh09aVGQv/LQmQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.139.0': - resolution: {integrity: sha512-vuQOv2WF5pZCkdSPEExul98o853M3MCR24DpWsGrUoPJu5KcnGE34kLXY2yFwBwZwT+eN1x40Tt4q6ZzlEdNUA==} + '@oxc-parser/binding-darwin-arm64@0.140.0': + resolution: {integrity: sha512-G6VK0nK61pH0d0mBjUqSZbVxGqqO5uzeginLDQj+gOO6ObfJjXRwgkD/ol0w1INcnFeAb6YGGO7qc3ueGHaycQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.139.0': - resolution: {integrity: sha512-zxZB8ChS+7XactZhcyxQ292P/DNiiBaPPKYiVUlb3RC0V/hme5bokNubjcmEmbW9uTfmqLTpveAdkbe66H3pGg==} + '@oxc-parser/binding-darwin-x64@0.140.0': + resolution: {integrity: sha512-HazBOuZzd2pO1C2uMmp8Gv7mhzMHqKSKDS1OZfcLEvpIcgA+48J92HEtNanVHDIzRD9PRPCV6aS6fkZIWOVl8Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.139.0': - resolution: {integrity: sha512-iw2MsoCPBQwdJqywRAGsF1jEAcGoZ+DeG2LVzt5pdUvRHqajsMF46BH0rHiWoWtMwcMSia+oQYga7fHo7u7Bcw==} + '@oxc-parser/binding-freebsd-x64@0.140.0': + resolution: {integrity: sha512-9hSUU+HmTUyOe4JzMHxNGgLWNY7rrO+6ShicZwImNJacEAACDMIkuEQQkvXSL+WJN50jaNtLYJv8s4OcBdpyUQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.139.0': - resolution: {integrity: sha512-SwjD/k3Y5bwGJWOH313VyaDrAuaYg1Pe+4AAXCgUKvSO5IHkj+mZ0oFcOWmB5nTuOM3uwdT+leL8h4OnFlI5hw==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0': + resolution: {integrity: sha512-RAEuQsYtS0KcDFqN0ABTjyyNlokS91JeuDuoW9tEbG0JTbRNXnpQUdbYc/16JoA6Z/2ALbNrE3KmxtqDiuIjCQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.139.0': - resolution: {integrity: sha512-qbeygDkcvailKFhphb2YGMMXsaZIynHlPtoOrcx4NZB/tRbUKraCCMbw8dPeFtEKasfS2qWh1VnReY+Lcys1zA==} + '@oxc-parser/binding-linux-arm-musleabihf@0.140.0': + resolution: {integrity: sha512-c4CkHvPvqfojouredJ0w3e6+jiBq0SbFyhH61kr/zPb/7XsaYTNKQ54vmlSsopfdQbNDX40ZeK9Abs2Qet6wcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.139.0': - resolution: {integrity: sha512-SrlL02KeImKlvx/9rCeopOLH7qKI3rqKKEguK6KBwVwEGLhV/A47dW4DNigf6/LFhrwoovaiayEQryjYAI86dQ==} + '@oxc-parser/binding-linux-arm64-gnu@0.140.0': + resolution: {integrity: sha512-yrjmLj8ixPB25yqvPGr28meGjb+keed7m1GqqY/0uqkhZIoT4t9zmfwUgFEtC33C7dtE+UQ7TU0IaVxf97SWJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.139.0': - resolution: {integrity: sha512-u9e884ChAVRmIZ1jr/m46S96FoDQnruFjISLi4Y0i6Wu/JUUmIiw7+umLyXILJsPfUuqnN5BJLe23t07+Y6+IA==} + '@oxc-parser/binding-linux-arm64-musl@0.140.0': + resolution: {integrity: sha512-ggGMQTN8Agwxp2WiLMpdY671dt0qTDJWiWlJeig3HnUwTnerRl0J2JdGVghWBeDcss2D9S2V2Js6dZHEiVabVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.139.0': - resolution: {integrity: sha512-Z9tU2b3GJfAXOdirQmz4gZQkQkVjy53i77gf91l0733MQKa/qtk73KQQE2GzDtMqim+HyjpzvemmqzBtH2IJUA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.140.0': + resolution: {integrity: sha512-IgTs8xYAFgAUGNmR65tIqjlJ8vKgrfXzC515e9goSdfMyKQV4aJpd2pUUudU4u51G64H0/DSEJEXKOraxm9ZCA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.139.0': - resolution: {integrity: sha512-RyUbr7hzPK84YDWKs77PRYk9VBwWbsbuYsQzQWiSmLnARXTg2zntLPGfCH/1wpfUYdmGkp/6SsqTXSsYdw9Jgw==} + '@oxc-parser/binding-linux-riscv64-gnu@0.140.0': + resolution: {integrity: sha512-A1x+PMWZmSGaFVOx2YeNTFau8uD+QO14/vLP4GrcuvUPs3+nBkUOjy9Lus86ftHsDojjYMbvBelmKc3F7Rv08g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.139.0': - resolution: {integrity: sha512-dcQhjtcDvtR8BgkUpt03Yz5SzxdzYvTigenIJOEsiSX6G2t6yybEMxGWjp0dOuYGror0BaqcZfTyXR58amCEig==} + '@oxc-parser/binding-linux-riscv64-musl@0.140.0': + resolution: {integrity: sha512-zBqpfRo2myWPrPo5xUjeZqlnPXPXsX8BcWtWff66/eGRQdbPjhzPgXa/F+AtxT2afUViPxbuDlwscMKzQ5tg+g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.139.0': - resolution: {integrity: sha512-iuGrxysV4rGUymdKpn7bgZQ6Vix8Bi/6D/rp71HYIzphq6NKrsBhsGOYsSZte+uBFL43tXh7Xr7TM72sGliJNA==} + '@oxc-parser/binding-linux-s390x-gnu@0.140.0': + resolution: {integrity: sha512-2M1DPm/8w9I//YzFlFC9qXw+r2tJFh5CYwRlYTq2vUJQS7qoQftEDeCZ8EnN7KHtvSiXvYj8mZI5pR7DpXmcEw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.139.0': - resolution: {integrity: sha512-NxJdZZyaa2JLLvNfH/iJQXfCNfKcPMylwY4ObMpBVmW4Nq+RyUpVVQXrMMelcQ6rwx3nuhF1Iga8n+eoAKGCIA==} + '@oxc-parser/binding-linux-x64-gnu@0.140.0': + resolution: {integrity: sha512-8aRDbZ/U/jO8N7go1MO72jtbpb4uswV8d7vOkMvt/BPgZiyEYvl1VIWK4ESxZZhnJ4tqwVldgX7dNiP/eB1Jdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.139.0': - resolution: {integrity: sha512-ePxBvvtzISmSsJ0RIj8FNikSCn58i1jtccj7XR4U9Li4iSzhkFyYlnJ51cQTUwkairz8WMTD4SpKoot8RyTnQA==} + '@oxc-parser/binding-linux-x64-musl@0.140.0': + resolution: {integrity: sha512-xRqpeI8U2sQQS1W5BMWRyMTxtagkuLG2dEWruet5lFsWHTvBth11/TpSaJatHdqVVwHN0q3uuoS9zRsGinq8hg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.139.0': - resolution: {integrity: sha512-b/c2+mPXMOxG5x16n8yf9cjor/ntQQScmYnSmLEWIWJ4rfXd5dokMxx0kliSLA+YAGq6DD3K9BWi+aFXHiiV1w==} + '@oxc-parser/binding-openharmony-arm64@0.140.0': + resolution: {integrity: sha512-GbGRe26MqAKciFRvXeHNQJ6VAHYs9R4miP89sEAncysM3n+f4lnyLWgsa9kklJNpfnxdq2yRoNYHFqwBckVimw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.139.0': - resolution: {integrity: sha512-rNU0pO+/CVPC2qZ+xtX5R2cOje9Q75q3UkfgwUCPlplqxMv6Iffd5tMPGVMCLGnPINxPlmi0WPsW94HltbYvfQ==} + '@oxc-parser/binding-wasm32-wasi@0.140.0': + resolution: {integrity: sha512-vFiC1hqys+hkX1GnQkIoiTQJNiUm43Z0lO35ETKXTw0YtpW7+cN58YRRXFAQQ+TgpkIi3lrhcxdlnqz+Oi3ptQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.139.0': - resolution: {integrity: sha512-JPEmfncZfqAFiGsAN6UZSMIBqnG8wn8buywRacFOWjP+9dZwcs03SsBp4tmyjM/4l/VoH/IuThrW1Jof3OyQUw==} + '@oxc-parser/binding-win32-arm64-msvc@0.140.0': + resolution: {integrity: sha512-fGSQldwEYKhM+H8uLt76Op8hh5+FYaR6lvvQ1Txw3Mhn86DyQXLcI0fi1EkFlTK7F+46OCk/j0AJMzZQm6g5Xg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.139.0': - resolution: {integrity: sha512-c06dWBwEnFHof5JN7T9/ts23uATXS9czPEBO60d4xnjH2Am29Bo7OGKdVHRmcWQnw0OBxlTg9fIEXAvtcwOBfw==} + '@oxc-parser/binding-win32-ia32-msvc@0.140.0': + resolution: {integrity: sha512-sDS2Bai+g3ZWYwfZqmosiSuFDBcVnZ3Ta6pszzsiJoLMqsJEWKcxXXbGa7b7yXr++W2lQNPb3ZRJ8czseqL7RA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.139.0': - resolution: {integrity: sha512-bI3/44urQjW/D495JPXe+c9d+4Q+ONi3DvDNnzwRZYTWHZ3hAlFdmirYK4mzhTfCZiAly02EbMYir7QGU+9O2Q==} + '@oxc-parser/binding-win32-x64-msvc@0.140.0': + resolution: {integrity: sha512-kHbE1zWyb5OQgJA6/5P4WjiuB01sYdQwtZnSSyE58FQEXDAMnyeeq4vj7KgN75i5SlBzOs8A5MrtlD3gOlDKqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1754,6 +1754,9 @@ packages: '@oxc-project/types@0.139.0': resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + '@oxc-project/types@0.140.0': + resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] @@ -1857,124 +1860,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.58.0': - resolution: {integrity: sha512-Uz62sHduGGPftXtILGyxdSW4PX82rUg+rfdNqhsgxe881g4rIoXlIqmZQ6HVKcF4f+F8qMhdD03Bx5u7gmeTdg==} + '@oxfmt/binding-android-arm-eabi@0.59.0': + resolution: {integrity: sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.58.0': - resolution: {integrity: sha512-rD0lRaJp1b+9vw6X4A2dJWKukd6X8yxiicN4JxXcXayolmUypRZxk+lKR+fVOu5q/iYc0fh5fR4bgmfOfVlbaA==} + '@oxfmt/binding-android-arm64@0.59.0': + resolution: {integrity: sha512-R/Sn7z52QtdAKNqQLLY0EK7hVMjXiz3XUlvoCFCm/60jgIzAnQtiqLKBCFaBkimCQL5rs2ezPMcicpjCsrl54Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.58.0': - resolution: {integrity: sha512-uzbPPk7O6M+w2K65vcQ1woga3wgP8zghjL1KOG5b6qJ8dvYHZJ1VShaslg2KOK6yQIwCQtcMCXqLBM6sqXUNTg==} + '@oxfmt/binding-darwin-arm64@0.59.0': + resolution: {integrity: sha512-vm/ynUqE4HjC0ZIEjmXv1UJu1/GngccQ+T+TJudTMxUxm6r+GQTg1TO3E5jJfI71pBaXxSzs1+vWHIwuilGHhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.58.0': - resolution: {integrity: sha512-L0nKYDxU32oxeQqJj21W9SlIMnf81VZEhyah6iDvFhf5q0oynq498Fopth7blErUJVBpVtxQ98RMCfMPqpJX6w==} + '@oxfmt/binding-darwin-x64@0.59.0': + resolution: {integrity: sha512-uTtYDpLN/obfKVWGpgEc8BqYlLZBQTPz2uYEvLRy3HPZxjZ34wiFzukUBU2bf64JuCYZI//GTV1EOMmWlPjf/w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.58.0': - resolution: {integrity: sha512-woNwfD58dC5PGS9LSLSD5JYfo/EFK5iG9vhDWkcCg3q78ag7KC8bpDqgvPHrMoXpx83OLXxoSOhu6z8FsVTHlg==} + '@oxfmt/binding-freebsd-x64@0.59.0': + resolution: {integrity: sha512-e2UnxL/ifStSPy8ffBCDbdy595SYsGy+U1pur4G65TuMmWxAMBzYGG7atZo/3mp515p8rZdsflxVD/E1FAdPLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.58.0': - resolution: {integrity: sha512-Sqs8nMLxuQpY21NKJ1u4stPDmO5hskBCNNh2E3AdCfI1QqWtf4m+Qn4mGEIUO4KGmuq3SWc/SZ80uy5IiwTCDw==} + '@oxfmt/binding-linux-arm-gnueabihf@0.59.0': + resolution: {integrity: sha512-LtdeZ1l0urxte3VNi3g8cocZwv1xGM1NKHSgF/fJEEVhyQmlgGh7WFWKFd/pNuO7djfvPNtNO1+MS+FEWkgVSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.58.0': - resolution: {integrity: sha512-Vd4exzBI5B5hB9m22JiTQzIL23WvHo/Pe+sNXPNeBLXSP9swCBPKCEBRwKpmpQzYhlgYaCgfPcGXPKAJBRIiZQ==} + '@oxfmt/binding-linux-arm-musleabihf@0.59.0': + resolution: {integrity: sha512-dBTciSsj9GTMl7p+h2gMSI0hoPn2ijfc/dUsbnWsP0RbwgPl2r0C/5zkMb3Pb+gGj17LH7f1o4qLo9aes/pAvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.58.0': - resolution: {integrity: sha512-bUWi5mHV+4Vi56RLHE1h6q/HHfwAIT3XoB9vJAVeRzfu5NriXM8y6eeJu0vlKa0C9kq2rq1sOWRClhdLHPocrg==} + '@oxfmt/binding-linux-arm64-gnu@0.59.0': + resolution: {integrity: sha512-tXVdJ/JINsNWdponPHN0OuKHtC+HdpyoS9sd6IDPNiiEYsRki8b7tefRZ1iMnRkdbyT4SEbguWsr6o+5awvbPQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.58.0': - resolution: {integrity: sha512-2ZHxemzgHcjtktAuVUwSoyXmGo/t+aF5tS1ciPpPei4rhSyrz3JOqDosXXrmhN/yLUSzJjtuW7ToTWqfQpCj2w==} + '@oxfmt/binding-linux-arm64-musl@0.59.0': + resolution: {integrity: sha512-RRTq38i2zT5fnw6XGHjvT6w2mh6x/G3m6AZcAZ56OTDTT/lsOeYnG3SVjwmH40z5kPqF+lf+o35e6m6PpKy9Dw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.58.0': - resolution: {integrity: sha512-AwKkVwjVmFQ3bcO7j0McGYAqCKH2a326fswfofng/E8VewCT/raeeGQr4huVhY704deK8AWASSTlxzMj0eZc6Q==} + '@oxfmt/binding-linux-ppc64-gnu@0.59.0': + resolution: {integrity: sha512-lD3k7glAJSaXW0D6xzu8VOZbYbosvy+0ktOVkfLEoQF5HJlMSxTQ2KNW0JO+08ccP/1ElOKktVEMI0fqRbVB4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.58.0': - resolution: {integrity: sha512-xsRpTxfUnJF8D3AUKko/qyWdjw4GZVHlCVFuGlzSCTeewLmykKINW8em1+wx+axsDVtJJcMtvsiaXggXxrlHgw==} + '@oxfmt/binding-linux-riscv64-gnu@0.59.0': + resolution: {integrity: sha512-WH5ZP1RbuHKBO/yfPRQKpNO/ijHcEDNbnmC4VPf/Bcd3+mbMAZpRiJWRa1PL5bREdIZZHo343mk3sqlc9x7Usw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.58.0': - resolution: {integrity: sha512-Z4AYOTcy7nYEIiXwD62PlerimyYRcfJOgUbQAEBjXz098kxKuERBlRntofGy69HHhe9E0TLVNMl1yspVNu+efw==} + '@oxfmt/binding-linux-riscv64-musl@0.59.0': + resolution: {integrity: sha512-743wOiaI9RZY4QVGkWkfGRavD5ZJUJ6gscFjVrVu1dP8AZh9jM+a6v3NhlR+OIzHdS6DhLM96w+gcVskskz7rw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.58.0': - resolution: {integrity: sha512-A3nhhtZPC/TKVWOPj9q/H3p2znJDCcHWYlJBhWL8hGq/bFmBaNBHC8Np6E581yVq1w9Mi3rMDNzDalWvtUfJtQ==} + '@oxfmt/binding-linux-s390x-gnu@0.59.0': + resolution: {integrity: sha512-xjRXQsRnrRZCcCkIEnbd2lmsQNobtwwkJxdy2bWXhZ1lIN0ouZwsBXRsoovW3yATuziAYwr9HMiQuR/Cc75NIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.58.0': - resolution: {integrity: sha512-2g+tVkgwqphw8R4hgo+kF4oz8+P5RwVOtr9+irsC7uwEp0e9j7Crw8kDGKL20uYlLPD7g02DqA61mC/UNYx98A==} + '@oxfmt/binding-linux-x64-gnu@0.59.0': + resolution: {integrity: sha512-4hNjqq/Rbr9B+StY9zMMAfm72+mtM4v80xYL5Qkb59Qd72g2vJMI0iFlPj3kf6miMsie/yJ7rt4urJT292HBgA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.58.0': - resolution: {integrity: sha512-rc15P6AbyyB7426aN8AakLd02Trb3a6ML/mmfAQeVHJEfVofWLcWIrBdy6zDEY+DIaL/s8E4GGPboVw+oP3+EA==} + '@oxfmt/binding-linux-x64-musl@0.59.0': + resolution: {integrity: sha512-NH579iN8EVQYsWowUB8B5vFchcylJtwPVJ7NmUAqEQHNLfhPbDT3K56KrECNAkUN4QpF4qiMgN2vsfZwVvjm7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.58.0': - resolution: {integrity: sha512-ZWoTM27/HYPOh9iq86DAbhPu9nXb8qKvvGU/h8OfliyVUFAMMNTLDkGsWDKKnDqIkqvZ9+dXlgUOsH1LYO3O7g==} + '@oxfmt/binding-openharmony-arm64@0.59.0': + resolution: {integrity: sha512-mzZy3Z5Aj1D75Aq9FVlmoRQH5ei8Ga4o/NZmlXkKyeZ5EmPrUXRR7c6BMBteV1ZuZ/356UYDuLRLjAMxTDTiBA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.58.0': - resolution: {integrity: sha512-LHZnqFXe2dEfkRI4XdZS/57nEOT/I4UCRX5IyM9v4GYW9XwQCjGe1IUK59SuKw3POwvcgWQ4pme2cYXmNqTNPg==} + '@oxfmt/binding-win32-arm64-msvc@0.59.0': + resolution: {integrity: sha512-0CpDJ1gE3jN1Gk6xms1Ie6LPfPcOtY4FAtoOmVLHQoAf8DvO2wd0DW2dIX2f7YTp5dxrr0ND8JeUEjm3DP3k5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.58.0': - resolution: {integrity: sha512-mZKpg20TpheCJym1rarcZCUJeW1sSruw8zAAaCYWvuVfwIUDN1CXdrPU/JgCWReXTCTrEfCB8Wyo3hh9jSZ2EA==} + '@oxfmt/binding-win32-ia32-msvc@0.59.0': + resolution: {integrity: sha512-zwdKBu3pt87uW0bRcywZb0oGMS7C6n87qogwRYFUgmk44T90ZzYlPjtlFYXs/DnBFrgNCvlHwCuWKfVWLeE7kw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.58.0': - resolution: {integrity: sha512-N/wUU4N5PZ2orBtI+Ko7MnMfYLfE7K91UrGMY/c/pYyHR3lA9kwst1XugkZx+92YcRh/Eo+iv2eTESSWXfiZPA==} + '@oxfmt/binding-win32-x64-msvc@0.59.0': + resolution: {integrity: sha512-dUUbZkKgWrmAeI/puzv4bxN8lzcYaFnQVwFTFtwO2Gp8M7lZGSE2qJjC58g518+1bltJ8mizjYwD0BGHym0l/w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2009,130 +2012,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.73.0': - resolution: {integrity: sha512-HZQRN/UMBu+Ut+/9MiAChkbP4qZqrNOWBcNI45vOT40GVhbGR0JgHB87L48D4iAqFQIdVmeQYtV9RF89AjTKkg==} + '@oxlint/binding-android-arm-eabi@1.74.0': + resolution: {integrity: sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.73.0': - resolution: {integrity: sha512-Gp+KJRylv2aW7thRpG5p1KTxZq4ZJFbWowrKzufNq9d3ssl3r3JviYV45/+p+7CN1Nv0zDd1e8Ex0b/HUDq4TQ==} + '@oxlint/binding-android-arm64@1.74.0': + resolution: {integrity: sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.73.0': - resolution: {integrity: sha512-3de96NdtXhxERMjIz7wsp2HYMY6pMQycGxFWac2mFecAx6VeARF/IqFb1QIaqiCRIdfzBwzTed+pCTCoiS+CYA==} + '@oxlint/binding-darwin-arm64@1.74.0': + resolution: {integrity: sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.73.0': - resolution: {integrity: sha512-5zx/uPW32TiaOeVY1dQ/H5iOf0K1HOdFKOJhLqGl4o63+i1fpzoqqu/mKtd7OFgFjNCdhlyTGgjVkQTZm1ELcg==} + '@oxlint/binding-darwin-x64@1.74.0': + resolution: {integrity: sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.73.0': - resolution: {integrity: sha512-qNe4gKHaGnLuZJ8toUg90JAa0S2vTVvDw+0bRi3q1avXZXDT4u5mMeECf3nD4HYrbdn1O7dXqWut4onY/yx/Xg==} + '@oxlint/binding-freebsd-x64@1.74.0': + resolution: {integrity: sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.73.0': - resolution: {integrity: sha512-cCehYh5hTbfShm/fxTD6wwrGUWIpvX+N5OxmAMhFhDeTGXvw+BeNj889tpxsFQ9ZLatQ6wImuY8tsKLZ+FMz7w==} + '@oxlint/binding-linux-arm-gnueabihf@1.74.0': + resolution: {integrity: sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.73.0': - resolution: {integrity: sha512-d5j5GDU/2dMgjVhw7TQT9ITrsIr1Y02KEXKyVGIXUkD+KiaxE9TP65FS2ZdgTBemQvoRL+gSBdbrIm3cQIeacg==} + '@oxlint/binding-linux-arm-musleabihf@1.74.0': + resolution: {integrity: sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.73.0': - resolution: {integrity: sha512-Eyf1SrP3+yR1DI3OJgOY2Pvrr9dWP9TK37xPaDYycwTtlGlI45erJAVIfH5/m/xosDt6BupJYEFi47bvbTuuyw==} + '@oxlint/binding-linux-arm64-gnu@1.74.0': + resolution: {integrity: sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.73.0': - resolution: {integrity: sha512-IlT/OJApEDKaMmCooHuncgJZbbCe7T5QIWmTZBEtYscWvzPQuuEinVcid6kwQRVQOUdb7PUCz4jQHnaYXdfJXw==} + '@oxlint/binding-linux-arm64-musl@1.74.0': + resolution: {integrity: sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.73.0': - resolution: {integrity: sha512-L+JYcb/vdg5fmcH08V6o0YYLU28cTH1SPNulwJdvK9NK49aXSkYy6oNpKBmddArVOXYqNepriDGiZ04G54kh1Q==} + '@oxlint/binding-linux-ppc64-gnu@1.74.0': + resolution: {integrity: sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.73.0': - resolution: {integrity: sha512-Qtk0g3bKV6OwWjIm7R8kQN1uOZRKQt/MODK2a8QfkwhTpXBD53ozx5XLVWLGDQAVyp2otLW4D2wB98XfAfMPGA==} + '@oxlint/binding-linux-riscv64-gnu@1.74.0': + resolution: {integrity: sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.73.0': - resolution: {integrity: sha512-wX0NQKZVxltkAOVmzFcpOaMpdaUvsq1Eqpx9tkAfl71UdkTlSo1R4AdAnGccR1Fm2+TzFgZ22CyyGuZ41RDr/A==} + '@oxlint/binding-linux-riscv64-musl@1.74.0': + resolution: {integrity: sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.73.0': - resolution: {integrity: sha512-vPe7UGBMWyiLTtnqS4xxgMQFSFGmtQwhwCxuiw6lXygaO6bVt0D8dFVg8Xv05eaiN3ybC0HXXHUAohFMFvqoCQ==} + '@oxlint/binding-linux-s390x-gnu@1.74.0': + resolution: {integrity: sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.73.0': - resolution: {integrity: sha512-2CwIWr9cemFC/CbRBWZvuk5mffz6ObmfFkfcC/9rTQ7f+icNhYr2kOjf9Rt8lLvugvkdGDOmkoVoFFHh6ClCTw==} + '@oxlint/binding-linux-x64-gnu@1.74.0': + resolution: {integrity: sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.73.0': - resolution: {integrity: sha512-nDadfJgg7NBBxG0N560wOe7LLX5QiYp6qBaI7viuk5EUORFBktU/NfV0MbTqU3gTqQDCh4VyxKdo5VADxk9w8Q==} + '@oxlint/binding-linux-x64-musl@1.74.0': + resolution: {integrity: sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.73.0': - resolution: {integrity: sha512-wGjJC+NLH9xP+IKGn9RDW94ojJR/wPbg5WCnQjj/oReaOtCQthr8ws1zICe77JFmo4ouUdeTHHZL/ESGiF6Pmw==} + '@oxlint/binding-openharmony-arm64@1.74.0': + resolution: {integrity: sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.73.0': - resolution: {integrity: sha512-I7X47GPGljw225YUQ5SbC/rb1Kkdrd0yQf0x+hYxeKS6DpfjMbo9ccQPQ6LNY6BoJQ1sHhgDUGuMn5Vg5gHT6w==} + '@oxlint/binding-win32-arm64-msvc@1.74.0': + resolution: {integrity: sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.73.0': - resolution: {integrity: sha512-5lWj+3h+74Fm1jYOO9qkJA4xkAlZA099DkXppuXsk7UpnpZLttsefrZU469vChGaG6hcSqrkKXQOvMTZtbjeNg==} + '@oxlint/binding-win32-ia32-msvc@1.74.0': + resolution: {integrity: sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.73.0': - resolution: {integrity: sha512-WaNRvh4f6zY9CvUQk2YoA1O90ieWrIklI84+HXFr9Isjz9CSESrdqo/RtIYt4Dll/cAchqGDMehfaZd0vqEFZw==} + '@oxlint/binding-win32-x64-msvc@1.74.0': + resolution: {integrity: sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.73.0': - resolution: {integrity: sha512-OhgMQeMmZA0dcFcX4/priaJZWdFECxiClgq6mRX6aatZEcV9PbKC3P3/v8U1hVjviT1i5U+vR8lAtBV6m4FXAA==} + '@oxlint/plugins@1.74.0': + resolution: {integrity: sha512-3tQlMDPt5hrRYedKl+M+Xq+fRjAEocMCRJaXH6ypLWu8+Oui5GMj51vjaYf/rzj6HT+JzQoUlY/I7Im2VNXzbw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -5090,15 +5093,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.139.0: - resolution: {integrity: sha512-cf1TKZN+zc0lwqigeyXKzKVk5+vNRe99Or2+wVJsXLdlhJgC+gsIniYDfj/ZEBzCJ8Xm21ZG6YtMbR262CcS2w==} + oxc-parser@0.140.0: + resolution: {integrity: sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.58.0: - resolution: {integrity: sha512-8feG/7NVEHDVwc1OUpP6Pks+TnaDFUw2jLLFIMi5bcmmwxAX2wBQvjSzj62RRTYBf2Op1Wt8xbkmagmPTR5ETg==} + oxfmt@0.59.0: + resolution: {integrity: sha512-Xqk6cPZS1yMvVa7OAuenaDZUsgMDutvvbZ9/L5gSvAfW64+WN4HVhgipLj5rVERbYQt8fLs9TopyZ1rU1XEG/w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5110,16 +5113,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.73.0: - resolution: {integrity: sha512-ZHGk1YDMwxnz2OSxHPysJwgXE7oVIDGWmhlVFeQuvfBDY5AlMfcUlYvixJkjc0sGYNhegYN8we10ZU5UydFLvQ==} + oxlint-plugin-eslint@1.74.0: + resolution: {integrity: sha512-AtZ4w3owo4xoY5fe/Juy9hmzEnekVPd/DDiPlOVGfR2linDTp/XVu0xK7ODNK4CCLvnnfCob/UeWN6kSiqm2CQ==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@0.24.0: resolution: {integrity: sha512-giCk5sEvG02d5tzPmFMX3hem8ndzEEu1xvGYS5OwNfO2WGl6ZVxt5LjE0yiMDoz94INI7XkXwgFAQiydPvVHDw==} hasBin: true - oxlint@1.73.0: - resolution: {integrity: sha512-u91G9TJzU6yqKWNZUYprQB07W7YvntZXaRxQ6CkoytepYhLWUXWsr1M8zUJ34VatNPuUAr3Z8GH+O2A331CluQ==} + oxlint@1.74.0: + resolution: {integrity: sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7404,72 +7407,74 @@ snapshots: dependencies: '@otplib/core': 13.4.1 - '@oxc-parser/binding-android-arm-eabi@0.139.0': + '@oxc-parser/binding-android-arm-eabi@0.140.0': optional: true - '@oxc-parser/binding-android-arm64@0.139.0': + '@oxc-parser/binding-android-arm64@0.140.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.139.0': + '@oxc-parser/binding-darwin-arm64@0.140.0': optional: true - '@oxc-parser/binding-darwin-x64@0.139.0': + '@oxc-parser/binding-darwin-x64@0.140.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.139.0': + '@oxc-parser/binding-freebsd-x64@0.140.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.139.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.139.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.140.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.139.0': + '@oxc-parser/binding-linux-arm64-gnu@0.140.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.139.0': + '@oxc-parser/binding-linux-arm64-musl@0.140.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.139.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.140.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.139.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.140.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.139.0': + '@oxc-parser/binding-linux-riscv64-musl@0.140.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.139.0': + '@oxc-parser/binding-linux-s390x-gnu@0.140.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.139.0': + '@oxc-parser/binding-linux-x64-gnu@0.140.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.139.0': + '@oxc-parser/binding-linux-x64-musl@0.140.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.139.0': + '@oxc-parser/binding-openharmony-arm64@0.140.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.139.0': + '@oxc-parser/binding-wasm32-wasi@0.140.0': dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.139.0': + '@oxc-parser/binding-win32-arm64-msvc@0.140.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.139.0': + '@oxc-parser/binding-win32-ia32-msvc@0.140.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.139.0': + '@oxc-parser/binding-win32-x64-msvc@0.140.0': optional: true '@oxc-project/types@0.139.0': {} + '@oxc-project/types@0.140.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7531,61 +7536,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.58.0': + '@oxfmt/binding-android-arm-eabi@0.59.0': optional: true - '@oxfmt/binding-android-arm64@0.58.0': + '@oxfmt/binding-android-arm64@0.59.0': optional: true - '@oxfmt/binding-darwin-arm64@0.58.0': + '@oxfmt/binding-darwin-arm64@0.59.0': optional: true - '@oxfmt/binding-darwin-x64@0.58.0': + '@oxfmt/binding-darwin-x64@0.59.0': optional: true - '@oxfmt/binding-freebsd-x64@0.58.0': + '@oxfmt/binding-freebsd-x64@0.59.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.58.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.59.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.58.0': + '@oxfmt/binding-linux-arm-musleabihf@0.59.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.58.0': + '@oxfmt/binding-linux-arm64-gnu@0.59.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.58.0': + '@oxfmt/binding-linux-arm64-musl@0.59.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.58.0': + '@oxfmt/binding-linux-ppc64-gnu@0.59.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.58.0': + '@oxfmt/binding-linux-riscv64-gnu@0.59.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.58.0': + '@oxfmt/binding-linux-riscv64-musl@0.59.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.58.0': + '@oxfmt/binding-linux-s390x-gnu@0.59.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.58.0': + '@oxfmt/binding-linux-x64-gnu@0.59.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.58.0': + '@oxfmt/binding-linux-x64-musl@0.59.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.58.0': + '@oxfmt/binding-openharmony-arm64@0.59.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.58.0': + '@oxfmt/binding-win32-arm64-msvc@0.59.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.58.0': + '@oxfmt/binding-win32-ia32-msvc@0.59.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.58.0': + '@oxfmt/binding-win32-x64-msvc@0.59.0': optional: true '@oxlint-tsgolint/darwin-arm64@0.24.0': @@ -7606,64 +7611,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@0.24.0': optional: true - '@oxlint/binding-android-arm-eabi@1.73.0': + '@oxlint/binding-android-arm-eabi@1.74.0': optional: true - '@oxlint/binding-android-arm64@1.73.0': + '@oxlint/binding-android-arm64@1.74.0': optional: true - '@oxlint/binding-darwin-arm64@1.73.0': + '@oxlint/binding-darwin-arm64@1.74.0': optional: true - '@oxlint/binding-darwin-x64@1.73.0': + '@oxlint/binding-darwin-x64@1.74.0': optional: true - '@oxlint/binding-freebsd-x64@1.73.0': + '@oxlint/binding-freebsd-x64@1.74.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.73.0': + '@oxlint/binding-linux-arm-gnueabihf@1.74.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.73.0': + '@oxlint/binding-linux-arm-musleabihf@1.74.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.73.0': + '@oxlint/binding-linux-arm64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.73.0': + '@oxlint/binding-linux-arm64-musl@1.74.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.73.0': + '@oxlint/binding-linux-ppc64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.73.0': + '@oxlint/binding-linux-riscv64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.73.0': + '@oxlint/binding-linux-riscv64-musl@1.74.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.73.0': + '@oxlint/binding-linux-s390x-gnu@1.74.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.73.0': + '@oxlint/binding-linux-x64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-x64-musl@1.73.0': + '@oxlint/binding-linux-x64-musl@1.74.0': optional: true - '@oxlint/binding-openharmony-arm64@1.73.0': + '@oxlint/binding-openharmony-arm64@1.74.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.73.0': + '@oxlint/binding-win32-arm64-msvc@1.74.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.73.0': + '@oxlint/binding-win32-ia32-msvc@1.74.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.73.0': + '@oxlint/binding-win32-x64-msvc@1.74.0': optional: true - '@oxlint/plugins@1.73.0': {} + '@oxlint/plugins@1.74.0': {} '@pinojs/redact@0.4.0': {} @@ -10750,30 +10755,30 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.139.0: + oxc-parser@0.140.0: dependencies: - '@oxc-project/types': 0.139.0 + '@oxc-project/types': 0.140.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.139.0 - '@oxc-parser/binding-android-arm64': 0.139.0 - '@oxc-parser/binding-darwin-arm64': 0.139.0 - '@oxc-parser/binding-darwin-x64': 0.139.0 - '@oxc-parser/binding-freebsd-x64': 0.139.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.139.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.139.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.139.0 - '@oxc-parser/binding-linux-arm64-musl': 0.139.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.139.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.139.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.139.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.139.0 - '@oxc-parser/binding-linux-x64-gnu': 0.139.0 - '@oxc-parser/binding-linux-x64-musl': 0.139.0 - '@oxc-parser/binding-openharmony-arm64': 0.139.0 - '@oxc-parser/binding-wasm32-wasi': 0.139.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.139.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.139.0 - '@oxc-parser/binding-win32-x64-msvc': 0.139.0 + '@oxc-parser/binding-android-arm-eabi': 0.140.0 + '@oxc-parser/binding-android-arm64': 0.140.0 + '@oxc-parser/binding-darwin-arm64': 0.140.0 + '@oxc-parser/binding-darwin-x64': 0.140.0 + '@oxc-parser/binding-freebsd-x64': 0.140.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.140.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.140.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.140.0 + '@oxc-parser/binding-linux-arm64-musl': 0.140.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.140.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.140.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.140.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.140.0 + '@oxc-parser/binding-linux-x64-gnu': 0.140.0 + '@oxc-parser/binding-linux-x64-musl': 0.140.0 + '@oxc-parser/binding-openharmony-arm64': 0.140.0 + '@oxc-parser/binding-wasm32-wasi': 0.140.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.140.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.140.0 + '@oxc-parser/binding-win32-x64-msvc': 0.140.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10797,31 +10802,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.58.0: + oxfmt@0.59.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.58.0 - '@oxfmt/binding-android-arm64': 0.58.0 - '@oxfmt/binding-darwin-arm64': 0.58.0 - '@oxfmt/binding-darwin-x64': 0.58.0 - '@oxfmt/binding-freebsd-x64': 0.58.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.58.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.58.0 - '@oxfmt/binding-linux-arm64-gnu': 0.58.0 - '@oxfmt/binding-linux-arm64-musl': 0.58.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.58.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.58.0 - '@oxfmt/binding-linux-riscv64-musl': 0.58.0 - '@oxfmt/binding-linux-s390x-gnu': 0.58.0 - '@oxfmt/binding-linux-x64-gnu': 0.58.0 - '@oxfmt/binding-linux-x64-musl': 0.58.0 - '@oxfmt/binding-openharmony-arm64': 0.58.0 - '@oxfmt/binding-win32-arm64-msvc': 0.58.0 - '@oxfmt/binding-win32-ia32-msvc': 0.58.0 - '@oxfmt/binding-win32-x64-msvc': 0.58.0 - - oxlint-plugin-eslint@1.73.0: {} + '@oxfmt/binding-android-arm-eabi': 0.59.0 + '@oxfmt/binding-android-arm64': 0.59.0 + '@oxfmt/binding-darwin-arm64': 0.59.0 + '@oxfmt/binding-darwin-x64': 0.59.0 + '@oxfmt/binding-freebsd-x64': 0.59.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.59.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.59.0 + '@oxfmt/binding-linux-arm64-gnu': 0.59.0 + '@oxfmt/binding-linux-arm64-musl': 0.59.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.59.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.59.0 + '@oxfmt/binding-linux-riscv64-musl': 0.59.0 + '@oxfmt/binding-linux-s390x-gnu': 0.59.0 + '@oxfmt/binding-linux-x64-gnu': 0.59.0 + '@oxfmt/binding-linux-x64-musl': 0.59.0 + '@oxfmt/binding-openharmony-arm64': 0.59.0 + '@oxfmt/binding-win32-arm64-msvc': 0.59.0 + '@oxfmt/binding-win32-ia32-msvc': 0.59.0 + '@oxfmt/binding-win32-x64-msvc': 0.59.0 + + oxlint-plugin-eslint@1.74.0: {} oxlint-tsgolint@0.24.0: optionalDependencies: @@ -10832,27 +10837,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 0.24.0 '@oxlint-tsgolint/win32-x64': 0.24.0 - oxlint@1.73.0(oxlint-tsgolint@0.24.0): + oxlint@1.74.0(oxlint-tsgolint@0.24.0): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.73.0 - '@oxlint/binding-android-arm64': 1.73.0 - '@oxlint/binding-darwin-arm64': 1.73.0 - '@oxlint/binding-darwin-x64': 1.73.0 - '@oxlint/binding-freebsd-x64': 1.73.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.73.0 - '@oxlint/binding-linux-arm-musleabihf': 1.73.0 - '@oxlint/binding-linux-arm64-gnu': 1.73.0 - '@oxlint/binding-linux-arm64-musl': 1.73.0 - '@oxlint/binding-linux-ppc64-gnu': 1.73.0 - '@oxlint/binding-linux-riscv64-gnu': 1.73.0 - '@oxlint/binding-linux-riscv64-musl': 1.73.0 - '@oxlint/binding-linux-s390x-gnu': 1.73.0 - '@oxlint/binding-linux-x64-gnu': 1.73.0 - '@oxlint/binding-linux-x64-musl': 1.73.0 - '@oxlint/binding-openharmony-arm64': 1.73.0 - '@oxlint/binding-win32-arm64-msvc': 1.73.0 - '@oxlint/binding-win32-ia32-msvc': 1.73.0 - '@oxlint/binding-win32-x64-msvc': 1.73.0 + '@oxlint/binding-android-arm-eabi': 1.74.0 + '@oxlint/binding-android-arm64': 1.74.0 + '@oxlint/binding-darwin-arm64': 1.74.0 + '@oxlint/binding-darwin-x64': 1.74.0 + '@oxlint/binding-freebsd-x64': 1.74.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.74.0 + '@oxlint/binding-linux-arm-musleabihf': 1.74.0 + '@oxlint/binding-linux-arm64-gnu': 1.74.0 + '@oxlint/binding-linux-arm64-musl': 1.74.0 + '@oxlint/binding-linux-ppc64-gnu': 1.74.0 + '@oxlint/binding-linux-riscv64-gnu': 1.74.0 + '@oxlint/binding-linux-riscv64-musl': 1.74.0 + '@oxlint/binding-linux-s390x-gnu': 1.74.0 + '@oxlint/binding-linux-x64-gnu': 1.74.0 + '@oxlint/binding-linux-x64-musl': 1.74.0 + '@oxlint/binding-openharmony-arm64': 1.74.0 + '@oxlint/binding-win32-arm64-msvc': 1.74.0 + '@oxlint/binding-win32-ia32-msvc': 1.74.0 + '@oxlint/binding-win32-x64-msvc': 1.74.0 oxlint-tsgolint: 0.24.0 p-cancelable@4.0.1: {} From 7379888d01499277adabb863f15593a2b9314407 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:44:01 +0800 Subject: [PATCH 0319/1190] chore(deps): bump actions/setup-node from 6.4.0 to 7.0.0 (#22711) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e...820762786026740c76f36085b0efc47a31fe5020) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-assets.yml | 2 +- .github/workflows/comment-on-issue.yml | 4 ++-- .github/workflows/docker-test-cont.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/issue-command.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/npm-publish.yml | 2 +- .github/workflows/test-full-routes.yml | 2 +- .github/workflows/test.yml | 6 +++--- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 5b03c411b6fd..3158d9791682 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -22,7 +22,7 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js Active LTS - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 2b973468ca6d..d2952e479323 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index 66404d2b89a7..7f20ab28316c 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -45,7 +45,7 @@ jobs: - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 6f6d483e15cd..e60ab6ad85be 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 48832e6346e9..732ff9563d89 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -114,7 +114,7 @@ jobs: uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js Active LTS - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d5912c5d9a35..9f315d2c15cc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 769e6593544c..34117cde510f 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index 7d6d41c0332e..fd36f0f40a60 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -18,7 +18,7 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js Active LTS - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a5c54105e35..02bda0de9aff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -79,7 +79,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -128,7 +128,7 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' From 2266ac7ec8e812d42020fbbaf6b28ceeccbf3eb8 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 14 Jul 2026 20:37:11 +0800 Subject: [PATCH 0320/1190] fix(route/telegram): workaround t.me issue (#22717) --- lib/routes/telegram/channel.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/routes/telegram/channel.ts b/lib/routes/telegram/channel.ts index 094690c33c7b..c387f3c7b302 100644 --- a/lib/routes/telegram/channel.ts +++ b/lib/routes/telegram/channel.ts @@ -1,6 +1,7 @@ import querystring from 'node:querystring'; import { load } from 'cheerio'; +import { FetchError } from 'ofetch'; import { config } from '@/config'; import type { Route } from '@/types'; @@ -195,8 +196,14 @@ async function handler(ctx) { const data = await cache.tryGet( resourceUrl, async () => { - const _r = await ofetch(resourceUrl); - return _r; + try { + return await ofetch(resourceUrl); + } catch (error) { + if (error instanceof FetchError && error.statusCode) { + throw error; + } + return await ofetch(resourceUrl.replace('https://t.me/', 'https://telegram.me/')); + } }, config.cache.routeExpire, false From 097a56187f6b5b799b6e609485585b568aa26c92 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 14 Jul 2026 21:52:50 +0800 Subject: [PATCH 0321/1190] fix(utils/ofetch): surface root causes in fetch errors (#22718) * fix(ofetch): enhance error handling to surface root causes in fetch errors * fix(errors): remove duplicated error name on dev --- lib/errors/index.tsx | 8 +++----- lib/utils/ofetch.test.ts | 9 +++++++++ lib/utils/ofetch.ts | 5 +++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/errors/index.tsx b/lib/errors/index.tsx index cd79d2ce705b..4366f574fe52 100644 --- a/lib/errors/index.tsx +++ b/lib/errors/index.tsx @@ -53,7 +53,7 @@ export const errorHandler: ErrorHandler = (error, ctx) => { }); } - let errorMessage = (process.env.NODE_ENV || process.env.VERCEL_ENV) === 'production' ? error.message : error.stack || error.message; + let errorMessage = (process.env.NODE_ENV || process.env.VERCEL_ENV) === 'production' || !error.stack ? `${error.name}: ${error.message}` : error.stack; switch (error.name) { case 'HTTPError': case 'RequestError': @@ -75,9 +75,7 @@ export const errorHandler: ErrorHandler = (error, ctx) => { ctx.status(503); break; } - const message = `${error.name}: ${errorMessage}`; - - logger.error(`Error in ${requestPath}: ${message}`); + logger.error(`Error in ${requestPath}: ${errorMessage}`); requestMetric.error({ path: matchedRoute, method: ctx.req.method, status: ctx.res.status }); return config.isPackage || ctx.req.query('format') === 'json' @@ -86,7 +84,7 @@ export const errorHandler: ErrorHandler = (error, ctx) => { message: error.message ?? error, }, }) - : ctx.html(); + : ctx.html(); }; export const notFoundHandler: NotFoundHandler = (ctx) => errorHandler(new NotFoundError(), ctx); diff --git a/lib/utils/ofetch.test.ts b/lib/utils/ofetch.test.ts index bbd5e0eba085..c944d6a4ee4a 100644 --- a/lib/utils/ofetch.test.ts +++ b/lib/utils/ofetch.test.ts @@ -36,6 +36,15 @@ describe('ofetch', () => { expect(warnSpy).toHaveBeenCalled(); }); + it('surfaces the root cause in fetch error messages', async () => { + const { logger, ofetch } = await loadOfetchWithLogger(); + vi.spyOn(logger, 'error').mockImplementation(() => logger); + const networkError = new TypeError('fetch failed', { cause: new Error('getaddrinfo ENOTFOUND t.me') }); + vi.stubGlobal('fetch', vi.fn().mockRejectedValue(networkError)); + + await expect(ofetch('https://t.me/s/telegram', { retry: 0 })).rejects.toThrow('fetch failed (getaddrinfo ENOTFOUND t.me)'); + }); + it('logs redirected responses', async () => { const { logger, ofetch } = await loadOfetchWithLogger(); const httpSpy = vi.spyOn(logger, 'http').mockImplementation(() => logger); diff --git a/lib/utils/ofetch.ts b/lib/utils/ofetch.ts index 3d572308a616..f90fa8c28022 100644 --- a/lib/utils/ofetch.ts +++ b/lib/utils/ofetch.ts @@ -35,6 +35,11 @@ const rofetch = createFetch({ fetch: (input: Parameters[0], init?: }, onRequestError({ request, error }) { logger.error(`Request ${request} fail: ${error.cause} ${error}`); + for (let cause: unknown = error.cause; cause instanceof Error; cause = cause.cause) { + if (cause.message) { + error.message += ` (${cause.message})`; + } + } }, onResponse({ request, response }) { if (response.redirected) { From f06106e32b3d44187dcfb0ceddf1ac1279074b60 Mon Sep 17 00:00:00 2001 From: TonyRL Date: Tue, 14 Jul 2026 23:53:46 +0800 Subject: [PATCH 0322/1190] chore: update allowed and disallowed tools for WebFetch based on event type --- .github/workflows/pr-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index 176dd65bdf75..ba8cee0b2720 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -90,8 +90,8 @@ jobs: display_report: 'true' claude_args: | --model ${{ vars.OPENCODE_MODEL }} - --allowedTools "Bash(base64:*),Bash(cat:*),Bash(echo:*),Bash(gh api:*),Bash(gh auth:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh repo view:*),Bash(gh search:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git status:*),Bash(grep:*),Bash(head:*),Bash(ls:*),Bash(rg:*),Bash(sed:*),Bash(tail:*),Bash(wc:*),WebFetch(domain:docs.rsshub.app)" - --disallowedTools "WebSearch" + --allowedTools "Bash(base64:*),Bash(cat:*),Bash(echo:*),Bash(gh api:*),Bash(gh auth:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh repo view:*),Bash(gh search:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git status:*),Bash(grep:*),Bash(head:*),Bash(ls:*),Bash(rg:*),Bash(sed:*),Bash(tail:*),Bash(wc:*),${{ github.event_name == 'workflow_dispatch' && 'WebFetch,WebSearch' || 'WebFetch(domain:docs.rsshub.app)' }}" + ${{ github.event_name != 'workflow_dispatch' && '--disallowedTools "WebSearch"' || '' }} prompt: | A pull request has been created or updated in this repository. From 2155313b44b1e074bac6216448a9f743b8e36e91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 08:14:42 +0000 Subject: [PATCH 0323/1190] chore(deps): bump @hono/node-server from 2.0.8 to 2.0.9 (#22724) Bumps [@hono/node-server](https://github.com/honojs/node-server) from 2.0.8 to 2.0.9. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v2.0.8...v2.0.9) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 2.0.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6b9f085ec27d..2b86a5a8d1b4 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.14.0", - "@hono/node-server": "2.0.8", + "@hono/node-server": "2.0.9", "@hono/zod-openapi": "1.5.0", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3b4000c941f..2905148d513e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 6.14.0 version: 6.14.0 '@hono/node-server': - specifier: 2.0.8 - version: 2.0.8(hono@4.12.30) + specifier: 2.0.9 + version: 2.0.9(hono@4.12.30) '@hono/zod-openapi': specifier: 1.5.0 version: 1.5.0(hono@4.12.30)(zod@4.4.3) @@ -1092,8 +1092,8 @@ packages: engines: {node: '>=14'} hasBin: true - '@hono/node-server@2.0.8': - resolution: {integrity: sha512-GuCWzLxwg218fy1JaHculFsdcuY12hxit83V+algozTPnwhNjLrRL/Alg9OYjLZLoUZ1rw/S4CdTMsnkSKCmFA==} + '@hono/node-server@2.0.9': + resolution: {integrity: sha512-cNMw3ziCdDcx0+ldta60zvUL5XO0OLt521n2hjPp0PB0ugczDCEczXsf33qQbkKIXWGFQ03f0LC85u6YK8pCLA==} engines: {node: '>=20'} peerDependencies: hono: ^4 @@ -3585,7 +3585,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.49: @@ -5757,6 +5757,7 @@ packages: telegram@2.26.22: resolution: {integrity: sha512-EIj7Yrjiu0Yosa3FZ/7EyPg9s6UiTi/zDQrFmR/2Mg7pIUU+XjAit1n1u9OU9h2oRnRM5M+67/fxzQluZpaJJg==} + deprecated: This package is archived and no longer maintained. Development continues in teleproto (https://npmjs.com/package/teleproto), a largely compatible, actively maintained fork. See the migration guide at https://docs.teleproto.dev/migrating-from-gramjs text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -6911,7 +6912,7 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.8(hono@4.12.30)': + '@hono/node-server@2.0.9(hono@4.12.30)': dependencies: hono: 4.12.30 From a20aa2c2a678bc9100e8267bc944297bf71fba9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 08:21:16 +0000 Subject: [PATCH 0324/1190] chore(deps): bump @hono/zod-openapi from 1.5.0 to 1.5.1 (#22725) Bumps [@hono/zod-openapi](https://github.com/honojs/middleware/tree/HEAD/packages/zod-openapi) from 1.5.0 to 1.5.1. - [Release notes](https://github.com/honojs/middleware/releases) - [Changelog](https://github.com/honojs/middleware/blob/main/packages/zod-openapi/CHANGELOG.md) - [Commits](https://github.com/honojs/middleware/commits/@hono/zod-openapi@1.5.1/packages/zod-openapi) --- updated-dependencies: - dependency-name: "@hono/zod-openapi" dependency-version: 1.5.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 2b86a5a8d1b4..be080f2ec3a6 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.14.0", "@hono/node-server": "2.0.9", - "@hono/zod-openapi": "1.5.0", + "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.1", "@opentelemetry/api": "1.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2905148d513e..cf49b36b1cc9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,8 +50,8 @@ importers: specifier: 2.0.9 version: 2.0.9(hono@4.12.30) '@hono/zod-openapi': - specifier: 1.5.0 - version: 1.5.0(hono@4.12.30)(zod@4.4.3) + specifier: 1.5.1 + version: 1.5.1(hono@4.12.30)(zod@4.4.3) '@jocmp/mercury-parser': specifier: 3.0.9 version: 3.0.9 @@ -1098,17 +1098,17 @@ packages: peerDependencies: hono: ^4 - '@hono/zod-openapi@1.5.0': - resolution: {integrity: sha512-/clSBZWBht7G4SItlTo+4ElbdIUw+987kZYttaUUJWksJoqK905z1pBNTwoJm/P1ibA2ka8nNbqb39eOlreH2g==} + '@hono/zod-openapi@1.5.1': + resolution: {integrity: sha512-ZaDdEIkn6PEGjIYHXJeyByg6yhvLzI+UXn968pWtahpwcQ6HMjQYXI3zNffTl0Wl9QZ79nUnGqiN1erEIu23fA==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.10.0' zod: ^4.0.0 - '@hono/zod-validator@0.8.0': - resolution: {integrity: sha512-5uS4S1/LKtZQYvD4BtpPUFkOv8d1wNxHHrChm26buMiEYc1FrHWvDUaKVBwkiVtvSExHSpLGDvcnpI2Copyj9w==} + '@hono/zod-validator@0.9.0': + resolution: {integrity: sha512-n0ZSXmCiHVIp4Y5wlOOyZCeTd/rsawA/qW1cipB8QOYKZ9N8Tk0nZUZCXho9cu374AN4JpDNKioNKBJ/W+LBug==} peerDependencies: - hono: '>=4.10.0' + hono: '>=4.11.2' zod: ^3.25.0 || ^4.0.0 '@humanfs/core@0.19.2': @@ -6916,15 +6916,15 @@ snapshots: dependencies: hono: 4.12.30 - '@hono/zod-openapi@1.5.0(hono@4.12.30)(zod@4.4.3)': + '@hono/zod-openapi@1.5.1(hono@4.12.30)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) - '@hono/zod-validator': 0.8.0(hono@4.12.30)(zod@4.4.3) + '@hono/zod-validator': 0.9.0(hono@4.12.30)(zod@4.4.3) hono: 4.12.30 openapi3-ts: 4.6.0 zod: 4.4.3 - '@hono/zod-validator@0.8.0(hono@4.12.30)(zod@4.4.3)': + '@hono/zod-validator@0.9.0(hono@4.12.30)(zod@4.4.3)': dependencies: hono: 4.12.30 zod: 4.4.3 From c70be07dd47419af77a5fed7bb620e4ecebd75a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:29:02 +0800 Subject: [PATCH 0325/1190] chore(deps-dev): bump the cloudflare group with 3 updates (#22721) Bumps the cloudflare group with 3 updates: [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-pool-workers` from 0.18.4 to 0.18.5 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.18.5/packages/vitest-pool-workers) Updates `@cloudflare/workers-types` from 5.20260714.1 to 5.20260715.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.110.0 to 4.111.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.111.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-pool-workers" dependency-version: 0.18.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260715.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.111.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 102 ++++++++++++++++++++++++------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index be080f2ec3a6..dbfff02ba356 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", - "@cloudflare/vitest-pool-workers": "0.18.4", - "@cloudflare/workers-types": "5.20260714.1", + "@cloudflare/vitest-pool-workers": "0.18.5", + "@cloudflare/workers-types": "5.20260715.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", @@ -208,7 +208,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", - "wrangler": "4.110.0", + "wrangler": "4.111.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf49b36b1cc9..d694622ea6bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,11 +294,11 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/vitest-pool-workers': - specifier: 0.18.4 - version: 0.18.4(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + specifier: 0.18.5 + version: 0.18.5(@cloudflare/workers-types@5.20260715.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260714.1 - version: 5.20260714.1 + specifier: 5.20260715.1 + version: 5.20260715.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -474,8 +474,8 @@ importers: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: - specifier: 4.110.0 - version: 4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.111.0 + version: 4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -607,45 +607,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.18.4': - resolution: {integrity: sha512-jOXvyLoR2b8jFvo3tX+mWxXXwcZ+PbId3d7vGFtZsuKakmDjKSeIq4o/sQjkqNv6q3XacIKSCAvfM8TfkPyrEw==} + '@cloudflare/vitest-pool-workers@0.18.5': + resolution: {integrity: sha512-Qe00zuHDRyAsOO7DmHnPcOhCiShdkt/NlRf/GrnKHzuXuysmt5YVyU/WaMVPqEkPYrgO+H9bSEJ1c9/7Nbw1Hg==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260708.1': - resolution: {integrity: sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg==} + '@cloudflare/workerd-darwin-64@1.20260710.1': + resolution: {integrity: sha512-OqJl2eWF5+y9jarMm3YqqCTUe7Hd4ihogX5jyRU8iaAgOVyDr/Bk6aXpPCVUi1/MHzO93a18R/TmSTtzmB0sQw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260708.1': - resolution: {integrity: sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA==} + '@cloudflare/workerd-darwin-arm64@1.20260710.1': + resolution: {integrity: sha512-MYBqWgUblO+VlGvO73zYsH3hB9tdRj+yLyt5IHDFWryipb2l1efmNiWtAOkIhSRfypqLYGFrfpaDm2Hg00XVKw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260708.1': - resolution: {integrity: sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA==} + '@cloudflare/workerd-linux-64@1.20260710.1': + resolution: {integrity: sha512-lVWUgqI8qrkqvaCBGElu1kdaUFdAvaS2RD8K4qkCFP9hI3f5TCXumEs5qWSeZkvKum0+X/uJZ5hBFWsYI5SmoQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260708.1': - resolution: {integrity: sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA==} + '@cloudflare/workerd-linux-arm64@1.20260710.1': + resolution: {integrity: sha512-kDwDPItBjAI4JL0df9Fma2N+Qggbm77IB/DnroAkEGQ79fpR80sYMyuB/ZQKyjEk9f48Ocq7HCCLq59qVSyNqA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260708.1': - resolution: {integrity: sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA==} + '@cloudflare/workerd-windows-64@1.20260710.1': + resolution: {integrity: sha512-GcLHy1oN1dfK6g1Z7UDV9f5xMGyTfPwcjWQ0sfWKH31IsoEVCRapnj3IC0PoIrDbnoo6irGPP0CwVs3WzdTajw==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260714.1': - resolution: {integrity: sha512-HGCTQVIQwzqAMLrZBCgLDrWKMgOdi6yn+S0Do1rF6z7t8tVvNprQfa53V6EDRRwsVO92uN5gviX2SxASDINZfA==} + '@cloudflare/workers-types@5.20260715.1': + resolution: {integrity: sha512-saxo/nMqQJ1dKDUXp1a2y/+IjKENFVD9+QRefHg5EjJZY20OG3xcEge4PGljbqZiF3AiU4o5ZLS3Vm7cayQIxg==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4883,8 +4883,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260708.1: - resolution: {integrity: sha512-c94O9zRDISdqO18EHt6l0iF/fWgWt8p18PJvRsA/L/NJZ9Cfke3s/F5Blg1XXF7WDutVRzWVWy8Vy4LaT5ifsA==} + miniflare@4.20260710.0: + resolution: {integrity: sha512-x1LLRkU6o1p7hiKrB0TRnL0MJn6xFOT+/vrlEQINz5cRDKLP8ru4hBqWTIvXAetzr1acKAnmAaG84pQ4W/K14g==} engines: {node: '>=22.0.0'} hasBin: true @@ -6278,17 +6278,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260708.1: - resolution: {integrity: sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w==} + workerd@1.20260710.1: + resolution: {integrity: sha512-U2sBPPrb9U97sBKnnMN6Kv8p65903P35nwMkPE9vSH/bRuRqkZ3a1EjUw3jV28RhiyXpkLF77Evzw8XimFxyTw==} engines: {node: '>=16'} hasBin: true - wrangler@4.110.0: - resolution: {integrity: sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg==} + wrangler@4.111.0: + resolution: {integrity: sha512-bffpI9EyrnpKkF/1S+RaIv8oRD93GtbsA7TlfWwOsGJGB7VO3jVbdGzpC9TU7Bqom3z7jUxcte4Z9MPhaQ4HoQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260708.1 + '@cloudflare/workers-types': ^5.20260710.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6582,43 +6582,43 @@ snapshots: '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260708.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260708.1 + workerd: 1.20260710.1 - '@cloudflare/vitest-pool-workers@0.18.4(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260715.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260708.1': + '@cloudflare/workerd-darwin-64@1.20260710.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260708.1': + '@cloudflare/workerd-darwin-arm64@1.20260710.1': optional: true - '@cloudflare/workerd-linux-64@1.20260708.1': + '@cloudflare/workerd-linux-64@1.20260710.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260708.1': + '@cloudflare/workerd-linux-arm64@1.20260710.1': optional: true - '@cloudflare/workerd-windows-64@1.20260708.1': + '@cloudflare/workerd-windows-64@1.20260710.1': optional: true - '@cloudflare/workers-types@5.20260714.1': {} + '@cloudflare/workers-types@5.20260715.1': {} '@colors/colors@1.6.0': {} @@ -10538,12 +10538,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.28.0 - workerd: 1.20260708.1 + workerd: 1.20260710.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -12048,26 +12048,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260708.1: + workerd@1.20260710.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260708.1 - '@cloudflare/workerd-darwin-arm64': 1.20260708.1 - '@cloudflare/workerd-linux-64': 1.20260708.1 - '@cloudflare/workerd-linux-arm64': 1.20260708.1 - '@cloudflare/workerd-windows-64': 1.20260708.1 + '@cloudflare/workerd-darwin-64': 1.20260710.1 + '@cloudflare/workerd-darwin-arm64': 1.20260710.1 + '@cloudflare/workerd-linux-64': 1.20260710.1 + '@cloudflare/workerd-linux-arm64': 1.20260710.1 + '@cloudflare/workerd-windows-64': 1.20260710.1 - wrangler@4.110.0(@cloudflare/workers-types@5.20260714.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260708.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260708.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260708.1 + workerd: 1.20260710.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260714.1 + '@cloudflare/workers-types': 5.20260715.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 5b4a2d18c6395d568730b0b3a9dfa418f8f69b1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:42:08 +0800 Subject: [PATCH 0326/1190] chore(deps-dev): bump tsdown from 0.22.7 to 0.22.8 (#22722) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.7 to 0.22.8. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.7...v0.22.8) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 220 ++++++++++++++++++++++++------------------------- 2 files changed, 111 insertions(+), 111 deletions(-) diff --git a/package.json b/package.json index dbfff02ba356..ed71abf92ac9 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.7", + "tsdown": "0.22.8", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d694622ea6bb..c9fe6de5f791 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -453,8 +453,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.7 - version: 0.22.7(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) + specifier: 0.22.8 + version: 0.22.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -2931,125 +2931,125 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.6.1': - resolution: {integrity: sha512-LDJtpOKtcv9f3V0eDUwFmmy47t2VC+DAuN+gq80R1IA+fa0d408i6sHsVtt6n+g5rf8f86ySoPSAe94lHt6Ixw==} + '@yuku-codegen/binding-darwin-arm64@0.6.3': + resolution: {integrity: sha512-pbDcFygFmbvo0jGFq5U0m5Sa9U8aVttVJWbBHZDZ68w/X48HdDWS1V4XvBacs8XkmWbTr/ef5fMGG7HsngqTmg==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.6.1': - resolution: {integrity: sha512-fBwpBOh33W7N87F94SVNExwm2KUV3ROhk51okr3Oy2ost1/JJuBWINVjcgwd2WPKZEFzUXgCzj/03UR/G+WIrQ==} + '@yuku-codegen/binding-darwin-x64@0.6.3': + resolution: {integrity: sha512-qZMrnA4i8OfqL3NMGoOvLdh1vby8cCGsmNo+tJQIIezXO557m3fdQLenz/57GqtBnnGWzWqd/aDp+faNxWlLwQ==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.6.1': - resolution: {integrity: sha512-UpMkskQV3a5oPnJV+GFFupIqnLwSBD4ZsZAVUZuNVkrqct433FHKqTwuG+P5JhHZbmhf+++3Ie/V2sgduyXrAQ==} + '@yuku-codegen/binding-freebsd-x64@0.6.3': + resolution: {integrity: sha512-2+SFpfem2GBH6BlCTAq6R44bZwuieduwRWHkCQnSgbK8tdEjMwB0Ix0IchryVBn6hdiWCZSTkSE3UILliRXsRQ==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.6.1': - resolution: {integrity: sha512-HICjDelfEDeD6TD8OEz/Dvt8KHxJiETR+paI/Fr7eVTQbjMfRrXJz8O1qV1qGH5SHZUGl2SAw2Rp+MLtXOjCrQ==} + '@yuku-codegen/binding-linux-arm-gnu@0.6.3': + resolution: {integrity: sha512-fbxg3cBPdJ++36DXtdzcoKw2xzFov91Wxvmn1khX9MXQbDqJQLJmITZhtokcZsj4uGJe32sUmxAZnKbUtZLjmA==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.6.1': - resolution: {integrity: sha512-pUswnwa+WOmtH2ZGOWL05kFLMNY7/TnEAryfIv1yVFzKQnmSy9TKYi3oIOxGZL3w+cdUKCZ6Q+jaD0oI10ztzA==} + '@yuku-codegen/binding-linux-arm-musl@0.6.3': + resolution: {integrity: sha512-Jk4P7kocGEisSvUFIm1VuHO3hC01LvS3sYAAmVVu1/ve5TuZ0iXyl9kIGtd1ZrgUvchgvZWNOaB+/Kq/RO63FA==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.6.1': - resolution: {integrity: sha512-Zro0FOu9clLCmqCnUKzEWHAu30tss0iEfhs+KDXm9Dpm1FkIHAKu43tF6FQU2hsTA7a8xd93NGddzc2EOJFKUg==} + '@yuku-codegen/binding-linux-arm64-gnu@0.6.3': + resolution: {integrity: sha512-i1xE8Bx1YZLheWtBZHD0Mq3nAIDrhgiH7o8VB4GiCbHufKb4XKj4CqSDMWSC0RYPmn//E+UEd1NsE2NbOux1tQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.6.1': - resolution: {integrity: sha512-NZaT+mp9toqWuFEA4MYW5HMRxgIa8DCqnTTnM5SrrojZgm4QoMI/mJfifVet1ZHgl/Dly5m6H6GPpq43uXVj8g==} + '@yuku-codegen/binding-linux-arm64-musl@0.6.3': + resolution: {integrity: sha512-ZeLkC6xZrlDoIJTadHfqTABTmsj2f6wCCtYBYx/RPGgdmQcGLA0NALRl7m0tnK2CT45eNRuOYzsfEZyF0XWM/A==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.6.1': - resolution: {integrity: sha512-M5macseSCBPvJ4yfKNyQpMc7nBQBtj39MNfMt0r+8UkTnR5qJE00JJx06puHgPxT5hnGxMAuAWZ+3a9H2ngqAw==} + '@yuku-codegen/binding-linux-x64-gnu@0.6.3': + resolution: {integrity: sha512-HNYt7zjIChPcnjZRG42CZq3Zn5mqaRo4UcFLr4mIbmGqdhX5hDDE8O8US8YgYyOUosfbBTBFdsbvFwVAx1TOkQ==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.6.1': - resolution: {integrity: sha512-liAyBZI5AbazZGeeNfWj0jCD/TE2L84hgVYh4KkjJA/N9bNzFQCDf4BvWP76nEO89r2tIGEUjbXdM4mM26riHg==} + '@yuku-codegen/binding-linux-x64-musl@0.6.3': + resolution: {integrity: sha512-/1ttT31dAQc7hGtXWSEYEgzGtakAyO2C+/GqAzIuKXlGLpNPZgXdR8LZ0iDHDalbEr3AuHIPRV43sWLUrHWdsA==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.6.1': - resolution: {integrity: sha512-qItzfH3x6MYChPeGfvh22rHD92WLgXQRSuwvspRVSnLvpnubEfZd+9REPRQVT2l9fIuETDCEkDNRqkDROQTkgA==} + '@yuku-codegen/binding-win32-arm64@0.6.3': + resolution: {integrity: sha512-oAArRDU1lkKg+xFEtiQ7C+/wghpqrkBrFWM05W73S+3sZz8JIHH79Q+Qh5gWls3i8vccitUgCnvln5V7xKn3XQ==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.6.1': - resolution: {integrity: sha512-DFFkKROZ9ZAHmFMUFRtRTkosZds1KH8BOx5t3UpNULIjT3iuUmEx9V5pWR0xOi66sANY38Ap77nz1kOZraQxEg==} + '@yuku-codegen/binding-win32-x64@0.6.3': + resolution: {integrity: sha512-bhFDcDsvmp5KuBfWTt4carNo1E4LXH7++S8qqZgDtXVqJxs0xMm7gbuqPihA8kBbphM+NzAUm5qmq6ocfqM6kg==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.6.1': - resolution: {integrity: sha512-jORysyRZg5zGDgVw15LGMsjZDh7jwjpUIJRBHgFt0ir15O5pEazfvuF2dnwvrJiTF0IT1EgHAVbTAYJWwQLCjg==} + '@yuku-parser/binding-darwin-arm64@0.6.3': + resolution: {integrity: sha512-Xate6yyZgvi7da/gdnZy+Vu5jlFB0LRlb5m4MY6Y98KSQeJPZIhoVXMK2Vsl48XOmPlDIS8lge414HUVUEo+hg==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.6.1': - resolution: {integrity: sha512-dTeYFkkFlbP/WCB2DtezXas3NApOPtFlXSdssB7wGtY9wpNp4HAkVo1KBwI5mcHK0e2joyUcqTSf44mFE+q7vg==} + '@yuku-parser/binding-darwin-x64@0.6.3': + resolution: {integrity: sha512-WKMZ5UU2HBdGZDEpQoLq+21f1FlS+BjroH1FaVE6zCSeqKmZ7xRP5jIRGtQ4vCYj/k2KHgyABZ16lgK9mTe2Sg==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.6.1': - resolution: {integrity: sha512-GExDp3rebo28mt3EAjvKQs0ZC3gkznAErV0I9TUNDa9muuhvD35kft61Mpsc6+NWeE+BG+kUyKbm6iO5B6ZMMA==} + '@yuku-parser/binding-freebsd-x64@0.6.3': + resolution: {integrity: sha512-OWX8V2k4bmtl/DNwU3yz3PZa2QXiSqWN3Xk7pNB5IPt7FcJBDlnpkOcX6h3tjMS7CyKE0lMvPUwwcmWSdbjkyA==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.6.1': - resolution: {integrity: sha512-a9MjABj4J0VE3Z2oROGhmeddZZrhwwrnl4ZWZOuHUhD/smDtDiNtr0LpCbKB7rEYaQ29snopOPdZ/0T3YgLglQ==} + '@yuku-parser/binding-linux-arm-gnu@0.6.3': + resolution: {integrity: sha512-/4LzmPXPaCWqIpY1j3+XVb8rbXIratqCte3A4sGEjua6aVhvVxEVAqeKlBsoGkORWLeqbpcxhgxKwOGM17eexA==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.6.1': - resolution: {integrity: sha512-ctuvXJgDRKKlmJfHxT4RsTvcAcHEFNJHTCsGbtt4rluQpVDc+ezk9JvQ534ehoIfZ9T0eIHSBgqYAZ4xCatNmQ==} + '@yuku-parser/binding-linux-arm-musl@0.6.3': + resolution: {integrity: sha512-Df4jk0M/eNKKQfYzXBBKhKkmJBpB+XoX2LkMxmlK3GN+fxUdeb8EM78wX+1+eLVl5dZNo6f7gOd6oDV0gChevw==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.6.1': - resolution: {integrity: sha512-vRtyoTtT0Ltowuh9LWOl/ZNU9h79J89ilOz5SEGspcw0jfhoUt19i07VNitll4jjfg5p2EN00q+MqX0pAobrFw==} + '@yuku-parser/binding-linux-arm64-gnu@0.6.3': + resolution: {integrity: sha512-sRCtDktUgIbbV78SYX3wdGVVm1Hz/nSUS24JgXB4MzUeGNwBNB+eQAWMtBxCGriyJNXK3zwfj+SSgvTmUdPf/A==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.6.1': - resolution: {integrity: sha512-vCsc3GOe1ylmRyfo/WLjIhjiCmaTtJbWNF4ZtgjNegDjpsRsFuCcP9duXB41QcfnJK38repKVFqFh0LR3l48FA==} + '@yuku-parser/binding-linux-arm64-musl@0.6.3': + resolution: {integrity: sha512-3J/jV3ROSqlhLyB/6i5EUHxjkom5i59iPvrtiAsnAjHzMsZJJPEke9LSaOsB0rf4MJFH9AmjvsK8gDahTjZy+A==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.6.1': - resolution: {integrity: sha512-gw3d81RdUHSYwjDW2IG6gEtm4VDoPP4ZOqpuC6Nc8+UBfos+4gTWOgzmuxIOVhkSV2fJCcUDpSJIlPzEU0FLZw==} + '@yuku-parser/binding-linux-x64-gnu@0.6.3': + resolution: {integrity: sha512-a5mPn/OMSq2Aa2i7eJXcc37Jtw0b89gDO2mDpXN769b06IirEiqOzLNNJd6R7R8DxoadHzY0nLFhYNChE+jyAg==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.6.1': - resolution: {integrity: sha512-nzU+Doq9UgZvYYvald36lZJ2Neeyheije6WE/YpoFt/oJiNmnjArRgr2CMtb/7gWBl80YSMwcHK4Ju0E+7wfWg==} + '@yuku-parser/binding-linux-x64-musl@0.6.3': + resolution: {integrity: sha512-kQSjfa6zdvotuXEGNKQ9vZZxE+lcEEbTUMSuvY/5+0crlwBCjEqrf9/W9ViFDoQAEt8WJiu/mtVNYkKAOkjLmA==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.6.1': - resolution: {integrity: sha512-r3tXFVDliWCPe7TL6DVxUkT4rkqnXyeFVSEDf+V9My6Gztq99/gIe3POQqFbshTRuSrpEYMGMbGxeFh+m+stxA==} + '@yuku-parser/binding-win32-arm64@0.6.3': + resolution: {integrity: sha512-ZawdN3R0YKr48BeXCpbax+WDWbgEG6nWyDosVeZasrT5TjgfF4XMP5SfuyMNvRJ4gbTitrcYHZkENSXZ9ncqcg==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.6.1': - resolution: {integrity: sha512-FqYMOqeCS2XTdn5yvaKlOhtSQ84mVO3aTXp6LGfMd9Zq8RsV4H8qLWv+sxJsgPCXfuBV64u8/f+CTr3uIwNLWA==} + '@yuku-parser/binding-win32-x64@0.6.3': + resolution: {integrity: sha512-NcqZwBXQhKyfC0Eb+yBxXQcPjZoUstD1Dbr3X4UlOD1QiYfnvAYRKCZJ6nQ6KQ5p30dGaKkQeBL4t3qQtDQNWA==} cpu: [x64] os: [win32] @@ -5491,14 +5491,14 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.8: - resolution: {integrity: sha512-IjBTFpkrYYJPRUmIjUJFsZdR1zeHskFcEwFDzSfFDoC2pZcSNgLrBUcDFY9K0Q+A1TZJR3q9MlVomxMDP8AuLQ==} + rolldown-plugin-dts@0.27.9: + resolution: {integrity: sha512-d54yt65+ZF/Mk8H6P36As02PAMdaiWRSzVNtJRc1h7nCgUFjuRI4cN2DyTfJyfVpPH6pgy7/2D7YQH1/Rh75Yg==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@ts-macro/tsc': ^0.3.6 - '@typescript/native-preview': '>=7.0.0-dev.20260325.1' + '@typescript/native-preview': '*' rolldown: ^1.0.0 - typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: '@ts-macro/tsc': @@ -5882,14 +5882,14 @@ packages: typescript: optional: true - tsdown@0.22.7: - resolution: {integrity: sha512-4egbOzc9dxVv/QS+gDV75FIxDIjQQeOnXBlUuikyjmn0ozuc6FW11djJjEEo3vqkuJRygpnKHurnj+Iwftk4VA==} + tsdown@0.22.8: + resolution: {integrity: sha512-6FOLlr1iLcE3LheqQt13hVUWtTduJNwF2akPskPe8Tf1hr+N5UULHzrNZYTMNwL6lr2UyQ8iefVBB6tdqp1PCQ==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.7 - '@tsdown/exe': 0.22.7 + '@tsdown/css': 0.22.8 + '@tsdown/exe': 0.22.8 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6414,11 +6414,11 @@ packages: yuku-ast@0.1.7: resolution: {integrity: sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA==} - yuku-codegen@0.6.1: - resolution: {integrity: sha512-6RJqqON2xYhMEp/sZv5oOSI3uOpWwRwzAi2fc/rMcRFjcqedAC5Fyp4AD9Vn2b8SB7hf9ESqVW+YwbDs/KvyKA==} + yuku-codegen@0.6.3: + resolution: {integrity: sha512-3c9H521tf1RRDu4cNUySfH01sKlALve4HKu2sITk33gLl5HhsvI6ngSuarpWxMPAiJEgqJc/HTvojWQRnYm9/g==} - yuku-parser@0.6.1: - resolution: {integrity: sha512-dPE3/+H2VBw9LhjoIVeW/axKidYGd+XzNtrwGGseZ0325cQFl0Dpwyh0R74XWe/WqQn4M8CR5YApsv2KF2zN1A==} + yuku-parser@0.6.3: + resolution: {integrity: sha512-iI6uABvvup9mvv8Mcpz7Tp//gehQlvcSnX4A4/0bf9i6X3RVQDuVUZel8jdpljwlF7WrbKsvD19y55Mc6+sKZw==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -8386,70 +8386,70 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.6.1': + '@yuku-codegen/binding-darwin-arm64@0.6.3': optional: true - '@yuku-codegen/binding-darwin-x64@0.6.1': + '@yuku-codegen/binding-darwin-x64@0.6.3': optional: true - '@yuku-codegen/binding-freebsd-x64@0.6.1': + '@yuku-codegen/binding-freebsd-x64@0.6.3': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.6.1': + '@yuku-codegen/binding-linux-arm-gnu@0.6.3': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.6.1': + '@yuku-codegen/binding-linux-arm-musl@0.6.3': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.6.1': + '@yuku-codegen/binding-linux-arm64-gnu@0.6.3': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.6.1': + '@yuku-codegen/binding-linux-arm64-musl@0.6.3': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.6.1': + '@yuku-codegen/binding-linux-x64-gnu@0.6.3': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.6.1': + '@yuku-codegen/binding-linux-x64-musl@0.6.3': optional: true - '@yuku-codegen/binding-win32-arm64@0.6.1': + '@yuku-codegen/binding-win32-arm64@0.6.3': optional: true - '@yuku-codegen/binding-win32-x64@0.6.1': + '@yuku-codegen/binding-win32-x64@0.6.3': optional: true - '@yuku-parser/binding-darwin-arm64@0.6.1': + '@yuku-parser/binding-darwin-arm64@0.6.3': optional: true - '@yuku-parser/binding-darwin-x64@0.6.1': + '@yuku-parser/binding-darwin-x64@0.6.3': optional: true - '@yuku-parser/binding-freebsd-x64@0.6.1': + '@yuku-parser/binding-freebsd-x64@0.6.3': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.6.1': + '@yuku-parser/binding-linux-arm-gnu@0.6.3': optional: true - '@yuku-parser/binding-linux-arm-musl@0.6.1': + '@yuku-parser/binding-linux-arm-musl@0.6.3': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.6.1': + '@yuku-parser/binding-linux-arm64-gnu@0.6.3': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.6.1': + '@yuku-parser/binding-linux-arm64-musl@0.6.3': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.6.1': + '@yuku-parser/binding-linux-x64-gnu@0.6.3': optional: true - '@yuku-parser/binding-linux-x64-musl@0.6.1': + '@yuku-parser/binding-linux-x64-musl@0.6.3': optional: true - '@yuku-parser/binding-win32-arm64@0.6.1': + '@yuku-parser/binding-win32-arm64@0.6.3': optional: true - '@yuku-parser/binding-win32-x64@0.6.1': + '@yuku-parser/binding-win32-x64@0.6.3': optional: true '@yuku-toolchain/types@0.5.43': {} @@ -11251,15 +11251,15 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5): + rolldown-plugin-dts@0.27.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.5 obug: 2.1.3 rolldown: 1.1.5 yuku-ast: 0.1.7 - yuku-codegen: 0.6.1 - yuku-parser: 0.6.1 + yuku-codegen: 0.6.3 + yuku-parser: 0.6.3 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11683,7 +11683,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.7(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): + tsdown@0.22.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11694,7 +11694,7 @@ snapshots: obug: 2.1.3 picomatch: 4.0.5 rolldown: 1.1.5 - rolldown-plugin-dts: 0.27.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5) + rolldown-plugin-dts: 0.27.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5) semver: 7.8.5 tinyexec: 1.2.4 tinyglobby: 0.2.17 @@ -12200,37 +12200,37 @@ snapshots: dependencies: '@yuku-toolchain/types': 0.5.43 - yuku-codegen@0.6.1: + yuku-codegen@0.6.3: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.6.1 - '@yuku-codegen/binding-darwin-x64': 0.6.1 - '@yuku-codegen/binding-freebsd-x64': 0.6.1 - '@yuku-codegen/binding-linux-arm-gnu': 0.6.1 - '@yuku-codegen/binding-linux-arm-musl': 0.6.1 - '@yuku-codegen/binding-linux-arm64-gnu': 0.6.1 - '@yuku-codegen/binding-linux-arm64-musl': 0.6.1 - '@yuku-codegen/binding-linux-x64-gnu': 0.6.1 - '@yuku-codegen/binding-linux-x64-musl': 0.6.1 - '@yuku-codegen/binding-win32-arm64': 0.6.1 - '@yuku-codegen/binding-win32-x64': 0.6.1 - - yuku-parser@0.6.1: + '@yuku-codegen/binding-darwin-arm64': 0.6.3 + '@yuku-codegen/binding-darwin-x64': 0.6.3 + '@yuku-codegen/binding-freebsd-x64': 0.6.3 + '@yuku-codegen/binding-linux-arm-gnu': 0.6.3 + '@yuku-codegen/binding-linux-arm-musl': 0.6.3 + '@yuku-codegen/binding-linux-arm64-gnu': 0.6.3 + '@yuku-codegen/binding-linux-arm64-musl': 0.6.3 + '@yuku-codegen/binding-linux-x64-gnu': 0.6.3 + '@yuku-codegen/binding-linux-x64-musl': 0.6.3 + '@yuku-codegen/binding-win32-arm64': 0.6.3 + '@yuku-codegen/binding-win32-x64': 0.6.3 + + yuku-parser@0.6.3: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.6.1 - '@yuku-parser/binding-darwin-x64': 0.6.1 - '@yuku-parser/binding-freebsd-x64': 0.6.1 - '@yuku-parser/binding-linux-arm-gnu': 0.6.1 - '@yuku-parser/binding-linux-arm-musl': 0.6.1 - '@yuku-parser/binding-linux-arm64-gnu': 0.6.1 - '@yuku-parser/binding-linux-arm64-musl': 0.6.1 - '@yuku-parser/binding-linux-x64-gnu': 0.6.1 - '@yuku-parser/binding-linux-x64-musl': 0.6.1 - '@yuku-parser/binding-win32-arm64': 0.6.1 - '@yuku-parser/binding-win32-x64': 0.6.1 + '@yuku-parser/binding-darwin-arm64': 0.6.3 + '@yuku-parser/binding-darwin-x64': 0.6.3 + '@yuku-parser/binding-freebsd-x64': 0.6.3 + '@yuku-parser/binding-linux-arm-gnu': 0.6.3 + '@yuku-parser/binding-linux-arm-musl': 0.6.3 + '@yuku-parser/binding-linux-arm64-gnu': 0.6.3 + '@yuku-parser/binding-linux-arm64-musl': 0.6.3 + '@yuku-parser/binding-linux-x64-gnu': 0.6.3 + '@yuku-parser/binding-linux-x64-musl': 0.6.3 + '@yuku-parser/binding-win32-arm64': 0.6.3 + '@yuku-parser/binding-win32-x64': 0.6.3 zod@3.25.76: {} From 267db2e768208207b7d1962908409ee12ab18954 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:50:30 +0800 Subject: [PATCH 0327/1190] chore(deps): bump nixpkgs from `e7a3ca8` to `18b9261` (#22727) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `e7a3ca8` to `18b9261`. - [Commits](https://github.com/NixOS/nixpkgs/compare/e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3...18b9261cb3294b6d2a06d03f96872827b8fe2698) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: 18b9261cb3294b6d2a06d03f96872827b8fe2698 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 25853472584b..37797bbc0ce4 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1783776592, - "narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=", + "lastModified": 1784007870, + "narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3", + "rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698", "type": "github" }, "original": { From 65c2e34402c8f0e964135ef6747bf5d0ab049960 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 19:17:07 +0800 Subject: [PATCH 0328/1190] chore(deps): bump devenv from `32f6747` to `83e8d7d` (#22726) Bumps [devenv](https://github.com/cachix/devenv) from `32f6747` to `83e8d7d`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/32f6747aabbd5aeb7413bae53d7e01e224ec77bc...83e8d7d34bdebad98ab936b6af53d57ae67af420) --- updated-dependencies: - dependency-name: devenv dependency-version: 83e8d7d34bdebad98ab936b6af53d57ae67af420 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 37797bbc0ce4..db12d9d22846 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1783972995, - "narHash": "sha256-BIti2dNiCWjOpemU6z0sKaSa214XwdynXckvX5Sirkk=", + "lastModified": 1784066496, + "narHash": "sha256-0ULUc0h8q6wAuvNZE89opjnhKUMMiC4GGKM8d+9dh/8=", "owner": "cachix", "repo": "devenv", - "rev": "32f6747aabbd5aeb7413bae53d7e01e224ec77bc", + "rev": "83e8d7d34bdebad98ab936b6af53d57ae67af420", "type": "github" }, "original": { From 6bce9493b5b90e7330e38cb7547cc9871d7e04a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:11:15 +0800 Subject: [PATCH 0329/1190] chore(deps-dev): bump eslint-plugin-unicorn from 71.1.0 to 72.0.0 (#22723) * chore(deps-dev): bump eslint-plugin-unicorn from 71.1.0 to 72.0.0 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 71.1.0 to 72.0.0. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v71.1.0...v72.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-version: 72.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * style: fix prefer-dom-node-html-methods * style: fix prefer-split-limit * style: fix no-unnecessary-string-trim * style: fix no-multiple-promise-resolver-calls * style: add v72 preset --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: TonyRL --- .oxlintrc.json | 9 ++++- lib/api/namespace/one.test.ts | 2 +- lib/routes/acs/journal.tsx | 2 +- lib/routes/aip/utils.tsx | 2 +- lib/routes/alternativeto/utils.ts | 2 +- lib/routes/apkpure/versions.ts | 2 +- lib/routes/bestofjs/monthly.tsx | 4 +-- lib/routes/bluestacks/release.ts | 4 +-- lib/routes/chinadegrees/province.tsx | 2 +- lib/routes/cmde/index.ts | 4 +-- lib/routes/cw/utils.ts | 4 +-- lib/routes/domp4/detail.ts | 2 +- lib/routes/gdut/oa-news.ts | 2 +- lib/routes/github/pulse.tsx | 2 +- lib/routes/github/trending.tsx | 2 +- lib/routes/google/developers.ts | 2 +- lib/routes/gov/customs/utils.ts | 2 +- lib/routes/gov/pbc/goutongjiaoliu.ts | 4 +-- lib/routes/gov/pbc/trade-announcement.ts | 4 +-- lib/routes/hitcon/zeroday.tsx | 2 +- lib/routes/ielts/index.ts | 2 +- lib/routes/iknowwhatyoudownload/daily.tsx | 2 +- lib/routes/j-test/news.ts | 2 +- lib/routes/lancedb/blog.ts | 2 +- lib/routes/makerworld/utils.ts | 2 +- lib/routes/missav/new.tsx | 2 +- lib/routes/nga/post.ts | 2 +- lib/routes/nytimes/utils.ts | 2 +- lib/routes/parliament.uk/commonslibrary.ts | 2 +- lib/routes/parliament.uk/lordslibrary.ts | 2 +- lib/routes/parliament/section77.ts | 4 +-- lib/routes/perplexity/blog.ts | 4 +-- lib/routes/perplexity/changelog.ts | 4 +-- lib/routes/pincong/utils.ts | 2 +- lib/routes/pnas/index.tsx | 2 +- lib/routes/questn/util.ts | 2 +- lib/routes/researchgate/publications.ts | 4 +-- lib/routes/science/utils.tsx | 2 +- lib/routes/sse/convert.ts | 2 +- lib/routes/sse/disclosure.ts | 2 +- lib/routes/swjtu/scai.ts | 2 +- lib/routes/syosetu/ranking-isekai.ts | 2 +- lib/routes/syosetu/ranking-r18.ts | 2 +- lib/routes/syosetu/ranking.ts | 4 +-- lib/routes/szse/disclosure/listed-notice.ts | 2 +- lib/routes/telegram/channel-media.ts | 2 +- lib/routes/twitter/api/web-api/login.ts | 2 +- lib/routes/uchicago/current.ts | 4 +-- lib/routes/uraaka-joshi/uraaka-joshi-user.ts | 2 +- lib/routes/uraaka-joshi/uraaka-joshi.ts | 2 +- lib/routes/xsijishe/utils.ts | 4 +-- lib/routes/xueqiu/cookies.ts | 2 +- lib/utils/playwright-utils.ts | 2 +- lib/utils/playwright.test.ts | 4 +-- package.json | 2 +- pnpm-lock.yaml | 35 +++++++++++++++----- 56 files changed, 102 insertions(+), 78 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index b937c76587cd..5936fe34c6d1 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -237,6 +237,7 @@ "unicorn/no-magic-array-flat-depth": "error", "unicorn-js/no-mismatched-map-key": "error", "unicorn-js/no-misrefactored-assignment": "error", + "unicorn-js/no-multiple-promise-resolver-calls": "error", // "unicorn/no-named-default": "error", -> use import/no-named-default "unicorn-js/no-negated-array-predicate": "error", "unicorn-js/no-negated-comparison": "error", @@ -257,12 +258,14 @@ "unicorn-js/no-redundant-comparison": "error", "unicorn-js/no-return-array-push": "error", "unicorn-js/no-selector-as-dom-name": "error", + // "unicorn-js/no-shorthand-property-overrides": "error", // css "unicorn/no-single-promise-in-promise-methods": "error", "unicorn/no-static-only-class": "error", "unicorn-js/no-subtraction-comparison": "error", "unicorn/no-thenable": "error", "unicorn/no-this-assignment": "error", "unicorn-js/no-this-outside-of-class": "error", + // "unicorn-js/no-transition-all": "error", // css // "unicorn-js/no-top-level-assignment-in-function": "error", // unopinionated // "unicorn-js/no-top-level-side-effects": "error", // allows dayjs.extend() "unicorn/no-typeof-undefined": "error", @@ -279,6 +282,7 @@ "unicorn-js/no-unnecessary-polyfills": "error", "unicorn/no-unnecessary-slice-end": "error", "unicorn-js/no-unnecessary-splice": "error", + "unicorn-js/no-unnecessary-string-trim": "error", "unicorn/no-unreadable-array-destructuring": "error", "unicorn-js/no-unreadable-for-of-expression": "error", "unicorn/no-unreadable-iife": "error", @@ -303,6 +307,7 @@ "unicorn-js/no-useless-logical-operand": "error", "unicorn-js/no-useless-override": "error", "unicorn/no-useless-promise-resolve-reject": "error", + "unicorn-js/no-useless-re-export": "error", // "unicorn-js/no-useless-recursion": "error", unopinionated "unicorn/no-useless-spread": "error", // "unicorn/no-useless-switch-case": "error", @@ -344,6 +349,7 @@ "unicorn-js/prefer-direct-iteration": "error", "unicorn/prefer-dom-node-append": "error", "unicorn/prefer-dom-node-dataset": "error", + "unicorn-js/prefer-dom-node-html-methods": "error", "unicorn/prefer-dom-node-remove": "error", "unicorn-js/prefer-dom-node-replace-children": "error", "unicorn/prefer-dom-node-text-content": "error", @@ -405,7 +411,7 @@ "unicorn-js/prefer-set-methods": "error", "unicorn/prefer-set-size": "error", "unicorn-js/prefer-short-arrow-method": "warn", - "unicorn-js/prefer-simple-condition-first": "error", + // "unicorn-js/prefer-simple-condition-first": "error", // unopinionated "unicorn-js/prefer-simple-sort-comparator": "error", "unicorn-js/prefer-simplified-conditions": "error", "unicorn-js/prefer-single-array-predicate": "error", @@ -426,6 +432,7 @@ "unicorn/prefer-structured-clone": "error", // "unicorn/prefer-switch": "error", "unicorn/prefer-ternary": "error", + "unicorn-js/prefer-then-catch": "error", "unicorn-js/prefer-toggle-attribute": "error", // "unicorn/prefer-top-level-await": "error", "unicorn/prefer-type-error": "error", diff --git a/lib/api/namespace/one.test.ts b/lib/api/namespace/one.test.ts index 20501ab01c96..65ce85bcb443 100644 --- a/lib/api/namespace/one.test.ts +++ b/lib/api/namespace/one.test.ts @@ -25,7 +25,7 @@ describe('api/namespace/one', () => { it('returns a nested namespace', async () => { expect(nestedKey).toBeDefined(); - const [namespace, sub] = nestedKey.split('/'); + const [namespace, sub] = nestedKey.split('/', 2); const result = await oneHandler(createCtx({ namespace, sub }), noopNext); expect(result).toBe(namespaces[nestedKey]); }); diff --git a/lib/routes/acs/journal.tsx b/lib/routes/acs/journal.tsx index d5f2b847817d..24d1278bc3cc 100644 --- a/lib/routes/acs/journal.tsx +++ b/lib/routes/acs/journal.tsx @@ -42,7 +42,7 @@ async function handler(ctx) { }); await page.waitForSelector('.toc'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(html); diff --git a/lib/routes/aip/utils.tsx b/lib/routes/aip/utils.tsx index 00826b987be3..c28a7d9ca875 100644 --- a/lib/routes/aip/utils.tsx +++ b/lib/routes/aip/utils.tsx @@ -10,7 +10,7 @@ const playwrightGet = async (url, context) => { await page.goto(url, { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); return html; }; diff --git a/lib/routes/alternativeto/utils.ts b/lib/routes/alternativeto/utils.ts index 8160f19c5471..77e9b4f3c65f 100644 --- a/lib/routes/alternativeto/utils.ts +++ b/lib/routes/alternativeto/utils.ts @@ -13,7 +13,7 @@ const playwrightGet = (url, cache) => await page.goto(url, { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); return html; }); diff --git a/lib/routes/apkpure/versions.ts b/lib/routes/apkpure/versions.ts index bce6bb781d1e..93f2e5b6db83 100644 --- a/lib/routes/apkpure/versions.ts +++ b/lib/routes/apkpure/versions.ts @@ -39,7 +39,7 @@ async function handler(ctx) { waitUntil: 'domcontentloaded', }); - const r = await page.evaluate(() => document.documentElement.innerHTML); + const r = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); const $ = load(r); diff --git a/lib/routes/bestofjs/monthly.tsx b/lib/routes/bestofjs/monthly.tsx index 6c9d063f7427..7b247b44c199 100644 --- a/lib/routes/bestofjs/monthly.tsx +++ b/lib/routes/bestofjs/monthly.tsx @@ -34,12 +34,12 @@ export const route: Route = { const targetMonths = getLastSixMonths(); const allNeededMonthlyRankings = await Promise.all( targetMonths.map((data) => { - const [year, month] = data.split('-'); + const [year, month] = data.split('-', 2); return getMonthlyRankings(year, month); }) ); const items = allNeededMonthlyRankings.flatMap((oneMonthlyRankings, i) => { - const [year, month] = targetMonths[i].split('-'); + const [year, month] = targetMonths[i].split('-', 2); const description = renderToString(
      {oneMonthlyRankings.map((item, index) => ( diff --git a/lib/routes/bluestacks/release.ts b/lib/routes/bluestacks/release.ts index d76a1a7b1ee7..127979f387a3 100644 --- a/lib/routes/bluestacks/release.ts +++ b/lib/routes/bluestacks/release.ts @@ -41,7 +41,7 @@ async function handler() { await page.goto(pageUrl, { waitUntil: 'domcontentloaded', }); - const res = await page.evaluate(() => document.documentElement.innerHTML); + const res = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(res); @@ -67,7 +67,7 @@ async function handler() { await page.goto(item.link, { waitUntil: 'domcontentloaded', }); - const res = await page.evaluate(() => document.documentElement.innerHTML); + const res = await page.evaluate(() => document.documentElement.getHTML()); const $ = load(res); await page.close(); diff --git a/lib/routes/chinadegrees/province.tsx b/lib/routes/chinadegrees/province.tsx index e129324fe2f2..f627575ca911 100644 --- a/lib/routes/chinadegrees/province.tsx +++ b/lib/routes/chinadegrees/province.tsx @@ -93,7 +93,7 @@ async function handler(ctx) { }); await page.waitForSelector('.datalist'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); const $ = load(html); diff --git a/lib/routes/cmde/index.ts b/lib/routes/cmde/index.ts index 910b5b16bd7d..aa71731617a6 100644 --- a/lib/routes/cmde/index.ts +++ b/lib/routes/cmde/index.ts @@ -29,7 +29,7 @@ async function handler(ctx) { waitUntil: 'domcontentloaded', }); await page.waitForSelector('.list'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(html); @@ -62,7 +62,7 @@ async function handler(ctx) { }); await page.waitForSelector('.text'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(html); item.description = $('.text').html(); diff --git a/lib/routes/cw/utils.ts b/lib/routes/cw/utils.ts index 2e68825e1d13..0cf4b8e2934a 100644 --- a/lib/routes/cw/utils.ts +++ b/lib/routes/cw/utils.ts @@ -65,7 +65,7 @@ const parsePage = async (path, context: BrowserContext, ctx) => { }); await page.waitForSelector('.caption'); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(response); @@ -100,7 +100,7 @@ const parseItems = (list, context: BrowserContext) => await page.goto(item.link, { waitUntil: 'domcontentloaded', }); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(response); diff --git a/lib/routes/domp4/detail.ts b/lib/routes/domp4/detail.ts index a8500872b358..8ab39562a9d6 100644 --- a/lib/routes/domp4/detail.ts +++ b/lib/routes/domp4/detail.ts @@ -45,7 +45,7 @@ export function getItemList($, detailUrl, second) { const { downurls } = second && data.Data.length > 1 ? data.Data[1] : data.Data[0]; return downurls.map((item) => { - const [title, downurl] = item.split('$'); + const [title, downurl] = item.split('$', 2); const urlType = getUrlType(downurl); // only magnet need compose trackers const enclosureUrl = urlType === 'magnet' ? composeMagnetUrl(downurl) : downurl; diff --git a/lib/routes/gdut/oa-news.ts b/lib/routes/gdut/oa-news.ts index 60ee8c13f954..1410152fa9b3 100644 --- a/lib/routes/gdut/oa-news.ts +++ b/lib/routes/gdut/oa-news.ts @@ -177,7 +177,7 @@ async function handler(ctx) { delete el.attribs.style; } } - if (el.attribs.class && el.attribs.class.trim().startsWith('Mso')) { + if (el.attribs.class && el.attribs.class.trimStart().startsWith('Mso')) { delete el.attribs.class; } if (el.attribs.lang) { diff --git a/lib/routes/github/pulse.tsx b/lib/routes/github/pulse.tsx index 77ddf9ce29df..c32d6fd142c2 100644 --- a/lib/routes/github/pulse.tsx +++ b/lib/routes/github/pulse.tsx @@ -47,7 +47,7 @@ async function handler(ctx) { const $mainSections = $('main .Layout-main').children(); const $subheading = $mainSections.eq(0); - const [periodFrom, periodTo] = $subheading.find('h2').text().split('–'); + const [periodFrom, periodTo] = $subheading.find('h2').text().split('–', 2); const $overview = $mainSections.eq(1); const overviewItems = $overview diff --git a/lib/routes/github/trending.tsx b/lib/routes/github/trending.tsx index c3d4ec092c32..d91f45e33a0a 100644 --- a/lib/routes/github/trending.tsx +++ b/lib/routes/github/trending.tsx @@ -84,7 +84,7 @@ async function handler(ctx) { const articles = $('article'); const trendingRepos = articles.toArray().map((item) => { - const [owner, name] = $(item).find('h2').text().split('/'); + const [owner, name] = $(item).find('h2').text().split('/', 2); return { name: name.trim(), owner: owner.trim(), diff --git a/lib/routes/google/developers.ts b/lib/routes/google/developers.ts index 4322f5f7d45a..58ae3c49d4e2 100644 --- a/lib/routes/google/developers.ts +++ b/lib/routes/google/developers.ts @@ -48,7 +48,7 @@ async function handler(ctx: Context) { .toArray() .map((element) => { const dateCategory = $(element).find('.search-result__eyebrow').text().trim(); - const [date, category] = dateCategory.split(' / '); + const [date, category] = dateCategory.split(' / ', 2); const titleElement = $(element).find('.search-result__title a'); const title = titleElement.text().trim(); const link = titleElement.attr('href'); diff --git a/lib/routes/gov/customs/utils.ts b/lib/routes/gov/customs/utils.ts index 9a2d92359f13..9ede6372e1ad 100644 --- a/lib/routes/gov/customs/utils.ts +++ b/lib/routes/gov/customs/utils.ts @@ -11,7 +11,7 @@ const playwrightGet = async (url, context) => { waitUntil: 'domcontentloaded', }); await page.waitForSelector('.pubCon'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); return html; }; diff --git a/lib/routes/gov/pbc/goutongjiaoliu.ts b/lib/routes/gov/pbc/goutongjiaoliu.ts index 6331b2f84eea..4fc2721ce33d 100644 --- a/lib/routes/gov/pbc/goutongjiaoliu.ts +++ b/lib/routes/gov/pbc/goutongjiaoliu.ts @@ -42,7 +42,7 @@ async function handler() { await page.goto(link, { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); const $ = load(html); const list = $('font.newslist_style') @@ -67,7 +67,7 @@ async function handler() { await detailPage.goto(item.link, { waitUntil: 'domcontentloaded', }); - const detailHtml = await detailPage.evaluate(() => document.documentElement.innerHTML); + const detailHtml = await detailPage.evaluate(() => document.documentElement.getHTML()); const content = load(detailHtml); item.description = content('#zoom').html(); item.pubDate = timezone(parseDate(content('.hui12').eq(5).text()), 8); diff --git a/lib/routes/gov/pbc/trade-announcement.ts b/lib/routes/gov/pbc/trade-announcement.ts index 90bb55fa9ac9..8a803dd9343b 100644 --- a/lib/routes/gov/pbc/trade-announcement.ts +++ b/lib/routes/gov/pbc/trade-announcement.ts @@ -36,7 +36,7 @@ async function handler() { await page.goto(link, { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); const $ = load(html); const list = $('font.newslist_style') .toArray() @@ -60,7 +60,7 @@ async function handler() { await detailPage.goto(item.link, { waitUntil: 'domcontentloaded', }); - const detailHtml = await detailPage.evaluate(() => document.documentElement.innerHTML); + const detailHtml = await detailPage.evaluate(() => document.documentElement.getHTML()); const content = load(detailHtml); item.description = content('#zoom').html(); item.pubDate = timezone(parseDate(content('#shijian').text()), 8); diff --git a/lib/routes/hitcon/zeroday.tsx b/lib/routes/hitcon/zeroday.tsx index 6979fcb4dff0..0719c2883d75 100644 --- a/lib/routes/hitcon/zeroday.tsx +++ b/lib/routes/hitcon/zeroday.tsx @@ -58,7 +58,7 @@ async function handler(ctx: Context): Promise { waitUntil: 'domcontentloaded', }); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); const $ = load(response); diff --git a/lib/routes/ielts/index.ts b/lib/routes/ielts/index.ts index fe782218a21b..52ca7ed59b5d 100644 --- a/lib/routes/ielts/index.ts +++ b/lib/routes/ielts/index.ts @@ -39,7 +39,7 @@ async function handler() { }); await page.waitForSelector('div.container'); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); return html; }, diff --git a/lib/routes/iknowwhatyoudownload/daily.tsx b/lib/routes/iknowwhatyoudownload/daily.tsx index fee7539da98a..06994384867c 100644 --- a/lib/routes/iknowwhatyoudownload/daily.tsx +++ b/lib/routes/iknowwhatyoudownload/daily.tsx @@ -69,7 +69,7 @@ async function handler(ctx) { for (const index in labelsList) { const label = labelsList[index]; const count = dataList[index]; - const [key, percent] = label.split(' '); + const [key, percent] = label.split(' ', 2); tableData.push({ key, count, diff --git a/lib/routes/j-test/news.ts b/lib/routes/j-test/news.ts index be477b443095..f8b7088a9a9c 100644 --- a/lib/routes/j-test/news.ts +++ b/lib/routes/j-test/news.ts @@ -35,7 +35,7 @@ async function handler() { const list = $('#content1 > .center > .col_box1 > .col_body1 > ul > li') .toArray() .map((item) => { - const [title, date] = $(item).text().trim().replaceAll(']', '').split(' ['); + const [title, date] = $(item).text().trim().replaceAll(']', '').split(' [', 2); const link = new URL($(item).children('a').attr('href')!, baseUrl).href; const pubDate = timezone(parseDate(date), 8); return { diff --git a/lib/routes/lancedb/blog.ts b/lib/routes/lancedb/blog.ts index 07bb9b9829fd..0b0dcd11cca9 100644 --- a/lib/routes/lancedb/blog.ts +++ b/lib/routes/lancedb/blog.ts @@ -22,7 +22,7 @@ export const handler = async (ctx: Context): Promise => { const description = $el.find('p.post-description').text().trim(); const meta = $el.find('.post-meta').text().trim().replaceAll(/\s+/g, ' '); - const [cat, author, dateStr] = meta.split(' / '); + const [cat, author, dateStr] = meta.split(' / ', 3); const pubDate = dateStr ? parseDate(dateStr) : undefined; return { diff --git a/lib/routes/makerworld/utils.ts b/lib/routes/makerworld/utils.ts index f6b0afb7789e..c061f2d86137 100644 --- a/lib/routes/makerworld/utils.ts +++ b/lib/routes/makerworld/utils.ts @@ -19,7 +19,7 @@ const fetchViaBrowser = async (url: string, type: 'html' | 'json' = 'html') => { }, }); try { - return (await page.evaluate(type === 'html' ? () => document.documentElement.innerHTML : () => document.documentElement.textContent)) ?? ''; + return (await page.evaluate(type === 'html' ? () => document.documentElement.getHTML() : () => document.documentElement.textContent)) ?? ''; } finally { await destroy(); } diff --git a/lib/routes/missav/new.tsx b/lib/routes/missav/new.tsx index c45a12df3a5d..646ca3670f83 100644 --- a/lib/routes/missav/new.tsx +++ b/lib/routes/missav/new.tsx @@ -48,7 +48,7 @@ async function handler() { await page.goto(url, { waitUntil: 'domcontentloaded', }); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); // const response = await ofetch(`${baseUrl}/dm397/new`, { diff --git a/lib/routes/nga/post.ts b/lib/routes/nga/post.ts index 42954b247515..c80bd75a3e9b 100644 --- a/lib/routes/nga/post.ts +++ b/lib/routes/nga/post.ts @@ -31,7 +31,7 @@ const customPreset: PresetFactory = presetHTML5.extend((tags) => ({ uid: (node) => ({ tag: 'a', attrs: { href: `https://nga.178.com/nuke.php?func=ucp&uid=${attrValue(node)}` }, content: ['@', ...childrenOf(node)] }), tid: (node) => ({ tag: 'a', attrs: { href: `https://nga.178.com/read.php?tid=${attrValue(node)}` }, content: node.content }), pid: (node) => { - const [pid, tid, page] = attrValue(node).split(','); + const [pid, tid, page] = attrValue(node).split(',', 3); return { tag: 'a', attrs: { href: `https://nga.178.com/read.php?tid=${tid}&page=${page}#pid${pid}Anchor` }, content: node.content }; }, // 分割线 diff --git a/lib/routes/nytimes/utils.ts b/lib/routes/nytimes/utils.ts index 58b173906efd..d350eef555df 100644 --- a/lib/routes/nytimes/utils.ts +++ b/lib/routes/nytimes/utils.ts @@ -25,7 +25,7 @@ const PuppeterGetter = async (ctx, context, link) => { await page.goto(link, { waitUntil: 'domcontentloaded', }); - const response = await page.evaluate(() => document.querySelector('body').innerHTML); + const response = await page.evaluate(() => document.querySelector('body').getHTML()); return response; }); return result; diff --git a/lib/routes/parliament.uk/commonslibrary.ts b/lib/routes/parliament.uk/commonslibrary.ts index 5d8dd7c2e741..cde8d382d401 100644 --- a/lib/routes/parliament.uk/commonslibrary.ts +++ b/lib/routes/parliament.uk/commonslibrary.ts @@ -36,7 +36,7 @@ async function handler(ctx) { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(html); const items = $('div.l-box.l-box--no-border.card__text') diff --git a/lib/routes/parliament.uk/lordslibrary.ts b/lib/routes/parliament.uk/lordslibrary.ts index 17e2c4ed09a9..0b351228d4a7 100644 --- a/lib/routes/parliament.uk/lordslibrary.ts +++ b/lib/routes/parliament.uk/lordslibrary.ts @@ -36,7 +36,7 @@ async function handler(ctx) { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(html); const items = $('div.l-box.l-box--no-border.card__text') diff --git a/lib/routes/parliament/section77.ts b/lib/routes/parliament/section77.ts index f3dbcbe62edf..cb651001096c 100644 --- a/lib/routes/parliament/section77.ts +++ b/lib/routes/parliament/section77.ts @@ -41,7 +41,7 @@ async function handler(ctx) { let title = 'ร่างพระราชบัญญัติที่เปิดรับฟังความคิดเห็นตามมาตรา 77 ของรัฐธรรมนูญ'; if (type) { - const [presenter, isMonetaryAct = ''] = type.split('-'); + const [presenter, isMonetaryAct = ''] = type.split('-', 2); title += { @@ -125,7 +125,7 @@ async function handler(ctx) { item.description = $('.des').first().html(); // Act draft status - const [, presenter, monetaryType] = $('.type77 h5').text().split(' '); + const [, presenter, monetaryType] = $('.type77 h5').text().split(' ', 3); item.category = [ ...item.category, $('.container-fluid .bg-status .col-md-8.p-0 h5 span,a') diff --git a/lib/routes/perplexity/blog.ts b/lib/routes/perplexity/blog.ts index 08335cd41de5..edacc7dde33d 100644 --- a/lib/routes/perplexity/blog.ts +++ b/lib/routes/perplexity/blog.ts @@ -47,7 +47,7 @@ async function handler(ctx: Context) { }, }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); const $ = load(html); const items: DataItem[] = []; @@ -123,7 +123,7 @@ async function handler(ctx: Context) { waitUntil: 'domcontentloaded', }); - const contentHtml = await contentPage.evaluate(() => document.documentElement.innerHTML); + const contentHtml = await contentPage.evaluate(() => document.documentElement.getHTML()); await contentPage.close(); const $content = load(contentHtml); diff --git a/lib/routes/perplexity/changelog.ts b/lib/routes/perplexity/changelog.ts index a21df78036d0..81d06e3b7fca 100644 --- a/lib/routes/perplexity/changelog.ts +++ b/lib/routes/perplexity/changelog.ts @@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise => { }, }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); const $ = load(html); const language = $('html').attr('lang') ?? 'en'; @@ -112,7 +112,7 @@ export const handler = async (ctx: Context): Promise => { // Navigate to the item link await contentPage.goto(item.link!, { waitUntil: 'domcontentloaded' }); - const contentHtml = await contentPage.evaluate(() => document.documentElement.innerHTML); + const contentHtml = await contentPage.evaluate(() => document.documentElement.getHTML()); await contentPage.close(); const $content = load(contentHtml); diff --git a/lib/routes/pincong/utils.ts b/lib/routes/pincong/utils.ts index 410dd018bfa5..137b4fce0483 100644 --- a/lib/routes/pincong/utils.ts +++ b/lib/routes/pincong/utils.ts @@ -13,7 +13,7 @@ const playwrightGet = (url, cache) => await page.goto(url, { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.documentElement.innerHTML); + const html = await page.evaluate(() => document.documentElement.getHTML()); await context.close(); return html; }); diff --git a/lib/routes/pnas/index.tsx b/lib/routes/pnas/index.tsx index 74d873b9bcd5..101e0a22a37a 100644 --- a/lib/routes/pnas/index.tsx +++ b/lib/routes/pnas/index.tsx @@ -71,7 +71,7 @@ async function handler(ctx) { }); await page.waitForSelector('.core-container'); - const res = await page.evaluate(() => document.documentElement.innerHTML); + const res = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(res); diff --git a/lib/routes/questn/util.ts b/lib/routes/questn/util.ts index 1a09f1de7938..449bfc7c6e51 100644 --- a/lib/routes/questn/util.ts +++ b/lib/routes/questn/util.ts @@ -6,7 +6,7 @@ const parseFilterStr = (filterStr) => { const filterPairs = filterStr.split('&'); // Split by '&' for (const pair of filterPairs) { - const [key, value] = pair.split('='); // Split by '=' + const [key, value] = pair.split('=', 2); // Split by '=' filters[key] = value; } diff --git a/lib/routes/researchgate/publications.ts b/lib/routes/researchgate/publications.ts index 094d1378a88d..3d34f586f323 100644 --- a/lib/routes/researchgate/publications.ts +++ b/lib/routes/researchgate/publications.ts @@ -30,7 +30,7 @@ async function handler(ctx) { request.resourceType() === 'document' || request.resourceType() === 'script' ? route.continue() : route.abort(); }); await page.goto(currentUrl); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(response); @@ -55,7 +55,7 @@ async function handler(ctx) { request.resourceType() === 'document' || request.resourceType() === 'script' ? route.continue() : route.abort(); }); await page.goto(item.link); - const detailResponse = await page.evaluate(() => document.documentElement.innerHTML); + const detailResponse = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const content = load(detailResponse); diff --git a/lib/routes/science/utils.tsx b/lib/routes/science/utils.tsx index 156cfce65d36..6f7afeda24b2 100644 --- a/lib/routes/science/utils.tsx +++ b/lib/routes/science/utils.tsx @@ -21,7 +21,7 @@ const fetchDesc = (list, context) => waitUntil: 'domcontentloaded', }); await page.waitForSelector('section#bodymatter, .news-article-content, .news-article-content--featured'); - const res = await page.evaluate(() => document.documentElement.innerHTML); + const res = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(res); diff --git a/lib/routes/sse/convert.ts b/lib/routes/sse/convert.ts index 2311bffd84cb..7db463a13ce9 100644 --- a/lib/routes/sse/convert.ts +++ b/lib/routes/sse/convert.ts @@ -28,7 +28,7 @@ async function handler(ctx) { if (query) { const pairs = query.split('&'); for (const pair of pairs) { - const [key, value] = pair.split('='); + const [key, value] = pair.split('=', 2); if (key) { queries[key] = value; } diff --git a/lib/routes/sse/disclosure.ts b/lib/routes/sse/disclosure.ts index 960953fd2b89..cdba9a91bed5 100644 --- a/lib/routes/sse/disclosure.ts +++ b/lib/routes/sse/disclosure.ts @@ -25,7 +25,7 @@ async function handler(ctx) { const queries: Record = {}; if (query) { for (const pair of query.split('&')) { - const [key, value] = pair.split('='); + const [key, value] = pair.split('=', 2); if (key) { queries[key] = value; } diff --git a/lib/routes/swjtu/scai.ts b/lib/routes/swjtu/scai.ts index 45056316b18b..9adb79a62c2c 100644 --- a/lib/routes/swjtu/scai.ts +++ b/lib/routes/swjtu/scai.ts @@ -76,7 +76,7 @@ const getItem = (item, cache) => { const dateItem = item.find('.calendar'); // 注意 .calendar 是 class const day = dateItem.find('.day').text().trim(); // "31" (文本需 trim 去空格) const ymd = dateItem.find('.date').text().trim(); // "2025/03" - const [year, month] = ymd.split('/'); // ["2025", "03"] + const [year, month] = ymd.split('/', 2); // ["2025", "03"] const dateText = `${year}-${month}-${day.padStart(2, '0')}`; pubDate = new Date(dateText); } diff --git a/lib/routes/syosetu/ranking-isekai.ts b/lib/routes/syosetu/ranking-isekai.ts index 7bba60dc138b..ed37bc9a3410 100644 --- a/lib/routes/syosetu/ranking-isekai.ts +++ b/lib/routes/syosetu/ranking-isekai.ts @@ -9,7 +9,7 @@ import { renderDescription } from './templates/description'; import { IsekaiCategory, isekaiCategoryToJapanese, NovelType, novelTypeToJapanese, periodToJapanese, periodToOrder, periodToPointField, RankingPeriod } from './types/ranking'; export function parseIsekaiRankingType(type: string): { period: RankingPeriod; category: IsekaiCategory; novelType: NovelType } { - const [periodStr, categoryStr, novelTypeStr = NovelType.TOTAL] = type.split('_'); + const [periodStr, categoryStr, novelTypeStr = NovelType.TOTAL] = type.split('_', 3); const period = periodStr as RankingPeriod; const category = categoryStr as IsekaiCategory; diff --git a/lib/routes/syosetu/ranking-r18.ts b/lib/routes/syosetu/ranking-r18.ts index ac933ba02bec..a8eee74e6edf 100644 --- a/lib/routes/syosetu/ranking-r18.ts +++ b/lib/routes/syosetu/ranking-r18.ts @@ -121,7 +121,7 @@ For example: \`daily_total\`, \`weekly_r\`, \`monthly_er\` }; function parseRankingType(type: string): { period: RankingPeriod; novelType: NovelType } { - const [periodStr, novelTypeStr] = type.split('_'); + const [periodStr, novelTypeStr] = type.split('_', 2); const period = periodStr as RankingPeriod; const novelType = novelTypeStr as NovelType; diff --git a/lib/routes/syosetu/ranking.ts b/lib/routes/syosetu/ranking.ts index 46293c0afced..0828ea88bca8 100644 --- a/lib/routes/syosetu/ranking.ts +++ b/lib/routes/syosetu/ranking.ts @@ -181,7 +181,7 @@ When multiple works have the same points, their order may differ from syosetu's }; function parseGeneralRankingType(type: string): { period: RankingPeriod; novelType: NovelType } { - const [periodStr, novelTypeStr] = type.split('_'); + const [periodStr, novelTypeStr] = type.split('_', 2); const period = periodStr as RankingPeriod; const novelType = novelTypeStr as NovelType; @@ -196,7 +196,7 @@ function parseGeneralRankingType(type: string): { period: RankingPeriod; novelTy } function parseGenreRankingType(type: string): { period: RankingPeriod; genre: number; novelType: NovelType } { - const [periodStr, genreStr, novelTypeStr = NovelType.TOTAL] = type.split('_'); + const [periodStr, genreStr, novelTypeStr = NovelType.TOTAL] = type.split('_', 3); const period = periodStr as RankingPeriod; const genre = Number(genreStr) as Genre; diff --git a/lib/routes/szse/disclosure/listed-notice.ts b/lib/routes/szse/disclosure/listed-notice.ts index b3ee6d8d70a3..c615e478f964 100644 --- a/lib/routes/szse/disclosure/listed-notice.ts +++ b/lib/routes/szse/disclosure/listed-notice.ts @@ -34,7 +34,7 @@ export const handler = async (ctx: Context): Promise => { }; if (query) { for (const pair of query.split('&')) { - const [key, value] = pair.split('='); + const [key, value] = pair.split('=', 2); if (key) { queries[key] = value; } diff --git a/lib/routes/telegram/channel-media.ts b/lib/routes/telegram/channel-media.ts index c4f8bccdd1d4..a2bc7bf553cf 100644 --- a/lib/routes/telegram/channel-media.ts +++ b/lib/routes/telegram/channel-media.ts @@ -116,7 +116,7 @@ function parseRange(range: string, length: bigInt.BigInteger) { if (!range) { return []; } - const [typ, segstr] = range.split('='); + const [typ, segstr] = range.split('=', 2); if (typ !== 'bytes') { throw new InvalidParameterError(`unsupported range: ${typ}`); } diff --git a/lib/routes/twitter/api/web-api/login.ts b/lib/routes/twitter/api/web-api/login.ts index 0c02dfa3025b..357091f9fb1c 100644 --- a/lib/routes/twitter/api/web-api/login.ts +++ b/lib/routes/twitter/api/web-api/login.ts @@ -54,7 +54,7 @@ async function login({ username, password, authenticationSecret }) { const message = data?.data?.home?.home_timeline_urt?.instructions?.[0]?.entries?.[0]?.entryId; if (message === 'messageprompt-suspended-prompt') { logger.error(`twitter debug: twitter username ${username} login failed: messageprompt-suspended-prompt`); - resolve(''); + return resolve(''); } const cookies = await page.context().cookies(); for (const cookie of cookies) { diff --git a/lib/routes/uchicago/current.ts b/lib/routes/uchicago/current.ts index 3912f2904b37..1dc5523769dd 100644 --- a/lib/routes/uchicago/current.ts +++ b/lib/routes/uchicago/current.ts @@ -45,7 +45,7 @@ async function handler(ctx) { await page.goto(link, { waitUntil: 'domcontentloaded', }); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); const cookies = await getCookies(page); await page.close(); const $ = load(response); @@ -68,7 +68,7 @@ async function handler(ctx) { waitUntil: 'domcontentloaded', referer: link, }); - const response = await page.evaluate(() => document.documentElement.innerHTML); + const response = await page.evaluate(() => document.documentElement.getHTML()); await page.close(); const $ = load(response); diff --git a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts index f461fcb1c2c6..827d446d2b8b 100644 --- a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts +++ b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts @@ -63,7 +63,7 @@ async function handler(ctx) { await page.waitForSelector('#pickup04 .grid-cell'); await page.waitForSelector('#main-block .grid-cell'); - html = await page.evaluate(() => document.documentElement.innerHTML); + html = await page.evaluate(() => document.documentElement.getHTML()); } catch { throw new Error('Access denied (403)'); } diff --git a/lib/routes/uraaka-joshi/uraaka-joshi.ts b/lib/routes/uraaka-joshi/uraaka-joshi.ts index 615c6e621a27..98ed55ae74a9 100644 --- a/lib/routes/uraaka-joshi/uraaka-joshi.ts +++ b/lib/routes/uraaka-joshi/uraaka-joshi.ts @@ -53,7 +53,7 @@ async function handler() { await page.waitForSelector('#main-block .grid-cell'); const bodyHandle = await page.$('body'); - html = await page.evaluate((body) => body.innerHTML, bodyHandle); + html = await page.evaluate((body) => body.getHTML(), bodyHandle); } catch { throw new Error('Access denied (403)'); } diff --git a/lib/routes/xsijishe/utils.ts b/lib/routes/xsijishe/utils.ts index 7fe22b3d8279..5eac8c3de3e0 100644 --- a/lib/routes/xsijishe/utils.ts +++ b/lib/routes/xsijishe/utils.ts @@ -28,7 +28,7 @@ const playwrightGet = async (url: string, context: BrowserContext, waitForSelect waitUntil: 'domcontentloaded', }); - let html = await page.evaluate(() => document.documentElement.innerHTML); + let html = await page.evaluate(() => document.documentElement.getHTML()); if (html.includes('抱歉,您尚未登录,没有权限访问该版块')) { return html; } @@ -39,7 +39,7 @@ const playwrightGet = async (url: string, context: BrowserContext, waitForSelect // Return the loaded HTML even when the expected selector is missing. } - html = await page.evaluate(() => document.documentElement.innerHTML); + html = await page.evaluate(() => document.documentElement.getHTML()); return html; } finally { await page.close(); diff --git a/lib/routes/xueqiu/cookies.ts b/lib/routes/xueqiu/cookies.ts index eb6c690c456d..5bc15737b9ca 100644 --- a/lib/routes/xueqiu/cookies.ts +++ b/lib/routes/xueqiu/cookies.ts @@ -17,7 +17,7 @@ export const parseToken = (link: string) => await page.goto(link, { waitUntil: 'domcontentloaded', }); - await page.evaluate(() => document.documentElement.innerHTML); + await page.evaluate(() => document.documentElement.getHTML()); const cookies = await getCookies(page); return cookies; }, diff --git a/lib/utils/playwright-utils.ts b/lib/utils/playwright-utils.ts index 5451e1e66d18..949a894e16fd 100644 --- a/lib/utils/playwright-utils.ts +++ b/lib/utils/playwright-utils.ts @@ -26,7 +26,7 @@ const parseCookieArray = (cookies, domainFilter?: string | RegExp) => { */ const constructCookieArray = (cookieStr, domain) => cookieStr.split('; ').map((item) => { - const [name, value] = item.split('='); + const [name, value] = item.split('=', 2); return value === undefined ? { name: '', value: name, domain, path: '/' } : { name, value, domain, path: '/' }; }); diff --git a/lib/utils/playwright.test.ts b/lib/utils/playwright.test.ts index 2d6adf68039e..6fbc10f3c8f0 100644 --- a/lib/utils/playwright.test.ts +++ b/lib/utils/playwright.test.ts @@ -112,7 +112,7 @@ describe('playwright', () => { waitUntil: 'domcontentloaded', }); - const html = await page.evaluate(() => document.body.innerHTML); + const html = await page.evaluate(() => document.body.getHTML()); expect(html.length).toBeGreaterThan(0); expect(browser?.isConnected()).toBe(true); @@ -128,7 +128,7 @@ describe('playwright', () => { const browser = context.browser(); const startTime = Date.now(); - const html = await page.evaluate(() => document.body.innerHTML); + const html = await page.evaluate(() => document.body.getHTML()); expect(html.length).toBeGreaterThan(0); expect(browser?.isConnected()).toBe(true); diff --git a/package.json b/package.json index ed71abf92ac9..8a38b5372ea2 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "eslint-plugin-n": "18.2.2", "eslint-plugin-regexp": "3.1.1", "eslint-plugin-simple-import-sort": "13.0.0", - "eslint-plugin-unicorn": "71.1.0", + "eslint-plugin-unicorn": "72.0.0", "eslint-plugin-yml": "3.6.0", "fast-string-width": "3.0.2", "fs-extra": "11.3.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c9fe6de5f791..55c6d4276747 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,8 +390,8 @@ importers: specifier: 13.0.0 version: 13.0.0(eslint@10.7.0) eslint-plugin-unicorn: - specifier: 71.1.0 - version: 71.1.0(eslint@10.7.0) + specifier: 72.0.0 + version: 72.0.0(eslint@10.7.0) eslint-plugin-yml: specifier: 3.6.0 version: 3.6.0(eslint@10.7.0) @@ -1049,6 +1049,10 @@ packages: resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/css-tree@4.0.4': + resolution: {integrity: sha512-nxMparyhqVWQvadx9x8dIfubfIPOE+X2b2waua8fzdnM9vdp9rgVtwEZlG0TmCwEUz/d/f40fzvO/eqBwdxz0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/eslintrc@3.3.6': resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3661,8 +3665,8 @@ packages: engines: {node: '>=20'} hasBin: true - electron-to-chromium@1.5.389: - resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + electron-to-chromium@1.5.392: + resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3817,8 +3821,8 @@ packages: peerDependencies: eslint: '>=5.0.0' - eslint-plugin-unicorn@71.1.0: - resolution: {integrity: sha512-dn3YmR3qLLUeYyo/os3ubZ7UHQJ1WbBAgC9cIhnLTyMj9J6kivuc2U1fCmYetLexUlTDVYtBqhjSj/VaebTe6Q==} + eslint-plugin-unicorn@72.0.0: + resolution: {integrity: sha512-hqO6ksoOHO+ZhdseTuKRVQbx9U7PRO/cv8qAR1mctwzdVO2hYud8uS9luAhp43RJgziYgHAph8eHyipT8GL0ng==} engines: {node: '>=22'} peerDependencies: eslint: '>=10.4' @@ -4760,6 +4764,9 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + mdn-data@2.28.1: + resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -6866,6 +6873,11 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/css-tree@4.0.4': + dependencies: + mdn-data: 2.28.1 + source-map-js: 1.2.1 + '@eslint/eslintrc@3.3.6': dependencies: ajv: 6.15.0 @@ -8601,7 +8613,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.10.43 caniuse-lite: 1.0.30001805 - electron-to-chromium: 1.5.389 + electron-to-chromium: 1.5.392 node-releases: 2.0.51 update-browserslist-db: 1.2.3(browserslist@4.28.6) @@ -8987,7 +8999,7 @@ snapshots: minimatch: 10.2.5 semver: 7.8.5 - electron-to-chromium@1.5.389: {} + electron-to-chromium@1.5.392: {} emoji-regex@10.6.0: {} @@ -9190,14 +9202,16 @@ snapshots: dependencies: eslint: 10.7.0 - eslint-plugin-unicorn@71.1.0(eslint@10.7.0): + eslint-plugin-unicorn@72.0.0(eslint@10.7.0): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint/css-tree': 4.0.4 browserslist: 4.28.6 change-case: 5.4.4 ci-info: 4.4.0 core-js-compat: 3.49.0 detect-indent: 7.0.2 + entities: 4.5.0 eslint: 10.7.0 find-up-simple: 1.0.1 globals: 17.7.0 @@ -9210,6 +9224,7 @@ snapshots: reserved-identifiers: 1.2.0 semver: 7.8.5 strip-indent: 4.1.1 + yaml: 2.9.0 eslint-plugin-yml@3.6.0(eslint@10.7.0): dependencies: @@ -10316,6 +10331,8 @@ snapshots: mdn-data@2.27.1: {} + mdn-data@2.28.1: {} + mdurl@2.0.0: {} memoizee@0.4.17: From ec6cbe125562c75819b20cbe42a37786cdfc3c20 Mon Sep 17 00:00:00 2001 From: Vanilla Date: Thu, 16 Jul 2026 06:30:38 +0800 Subject: [PATCH 0330/1190] fix(twitter): Allows cookies with socks ProxyAgent. (#22728) --- lib/routes/twitter/api/web-api/utils.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/routes/twitter/api/web-api/utils.ts b/lib/routes/twitter/api/web-api/utils.ts index 1f5a70d5c799..f3d921f96f7b 100644 --- a/lib/routes/twitter/api/web-api/utils.ts +++ b/lib/routes/twitter/api/web-api/utils.ts @@ -1,7 +1,7 @@ import { cookie as HttpCookieAgentCookie, CookieAgent } from 'http-cookie-agent/undici'; import queryString from 'query-string'; import { Cookie, CookieJar } from 'tough-cookie'; -import undici, { Client, ProxyAgent } from 'undici'; +import undici, { ProxyAgent } from 'undici'; import { config } from '@/config'; import ConfigNotFoundError from '@/errors/types/config-not-found'; @@ -25,9 +25,8 @@ const token2Cookie = async (token) => { try { const agent = proxy.proxyUri ? new ProxyAgent({ - factory: (origin, opts) => new Client(origin as string, opts).compose(HttpCookieAgentCookie({ jar })), uri: proxy.proxyUri, - }) + }).compose(HttpCookieAgentCookie({ jar })) : new CookieAgent({ cookies: { jar } }); if (token) { await ofetch('https://x.com', { @@ -112,9 +111,8 @@ export const twitterGot = async ( const jar = CookieJar.deserializeSync(cookie as any); const agent = proxy.proxyUri ? new ProxyAgent({ - factory: (origin, opts) => new Client(origin as string, opts).compose(HttpCookieAgentCookie({ jar })), uri: proxy.proxyUri, - }) + }).compose(HttpCookieAgentCookie({ jar })) : new CookieAgent({ cookies: { jar } }); if (proxy.proxyUri) { logger.debug(`twitter debug: Proxying request: ${requestUrl}`); From 6d3fa789760f0479543f4de23e5e224365ae0c85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 08:13:03 +0000 Subject: [PATCH 0331/1190] chore(deps): bump @scalar/hono-api-reference from 0.11.9 to 0.11.10 (#22734) Bumps [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono) from 0.11.9 to 0.11.10. - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) --- updated-dependencies: - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.11.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 8a38b5372ea2..8d321cc54342 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@opentelemetry/sdk-trace-base": "2.9.0", "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.11.9", + "@scalar/hono-api-reference": "0.11.10", "@sentry/node": "10.65.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55c6d4276747..25ed2a6379fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,8 +83,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.11.9 - version: 0.11.9(hono@4.12.30) + specifier: 0.11.10 + version: 0.11.10(hono@4.12.30) '@sentry/node': specifier: 10.65.0 version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) @@ -2453,30 +2453,30 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.3.2': - resolution: {integrity: sha512-HXgfSnSQSLaTZupSmN2w4O1Gv0wXdFl+GtrZHQEoNQ4HSzgCtk9fKcmSVxaZiPjyx6eon/x3Ic+Ka5T4tXskyA==} + '@scalar/client-side-rendering@0.3.3': + resolution: {integrity: sha512-QMTKZdwtSSV619jV1jKeRCOd358cgJqRBTHQ663CF7EUCZ354qH4js883K3RYg5/eO4oWxOuka308KIj0sZ/Kg==} engines: {node: '>=22'} - '@scalar/helpers@0.9.0': - resolution: {integrity: sha512-M34CLRCttqC1bXthI/QSzQj0s5C6nrU2PFWf/vOT3RpycbiGDGQbqR+5RfFzpOIQvRqbHfNdcRbeiZBw+vCbkQ==} + '@scalar/helpers@0.9.1': + resolution: {integrity: sha512-UKSLIPfN++f+zzbsZ+F6I0lNrR4yX4PtokjHgGwGiHapxT5VJqAF4zFTIP2KCd27XG7KnAIA7qq62O4xRBxc6w==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.11.9': - resolution: {integrity: sha512-/Rx/kozi18sfWLP6y9qK/xhjO68i667o2aFXWnRfxW0TKL2D6A+LGtWCHQTgTONPKZe0TX9iSMSWf0vy0UOsbA==} + '@scalar/hono-api-reference@0.11.10': + resolution: {integrity: sha512-LluYDOie3F8NqLiAuZ0ESWmtWotSMV9A2/AxIB3NwQk/3JJ+5S9rnIWVY61o7QIDWlwwy6uZ8KqRSV83qmnhZA==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/schemas@0.7.2': - resolution: {integrity: sha512-6Ble6WcbiKgD3ypIva+wgZv1qIClmeEjXA6jrOo1bKpYUu6sh4e89LZxDK+LULa2jQl/2O5GEaa6aZ0ImlpkHg==} + '@scalar/schemas@0.7.3': + resolution: {integrity: sha512-B6S/zUptiRfMsmMy92u/LefpULus1h0q3od9l5xgZc+pslkfFhfFns+QsU0UJX3Lqp3tTsf64c3tIRqH7cZgVA==} engines: {node: '>=22'} - '@scalar/types@0.16.2': - resolution: {integrity: sha512-QAlCtDVRsX/UV1N9ctLqPsKQy84eceFf4dMnTZM0JhaRfwS/Ovwp8oFf3POSpqQILNiqLFZLE6IV6p/5rNoAag==} + '@scalar/types@0.16.3': + resolution: {integrity: sha512-8TVNlK8AdvIekrapAoEVwy+IVRbMTSDYnpprskAJV4XanmZ52Kru6RmS6d+tbEBfMo2dTgQ9cV9P0kqsAu/AuA==} engines: {node: '>=22'} - '@scalar/validation@0.6.0': - resolution: {integrity: sha512-tpmmG+/xRE2Kn9RpflU3AIyZv08v10+E1ZrJCx7z6+/91zHVxy0M73kC1LT4/8PbYNt85ywyC8+n+D99JdMcGA==} + '@scalar/validation@0.6.1': + resolution: {integrity: sha512-XeJ+pvxag0rguuRT6hxNSXIsxleB8Gcs6WQ55vFRkB4qo2CCO+wIli+uipzM3pILu5cP7agcL3pgADiZzd1ZNg==} engines: {node: '>=20'} '@scure/base@2.2.0': @@ -7890,32 +7890,32 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.3.2': + '@scalar/client-side-rendering@0.3.3': dependencies: - '@scalar/schemas': 0.7.2 - '@scalar/types': 0.16.2 - '@scalar/validation': 0.6.0 + '@scalar/schemas': 0.7.3 + '@scalar/types': 0.16.3 + '@scalar/validation': 0.6.1 - '@scalar/helpers@0.9.0': {} + '@scalar/helpers@0.9.1': {} - '@scalar/hono-api-reference@0.11.9(hono@4.12.30)': + '@scalar/hono-api-reference@0.11.10(hono@4.12.30)': dependencies: - '@scalar/client-side-rendering': 0.3.2 + '@scalar/client-side-rendering': 0.3.3 hono: 4.12.30 - '@scalar/schemas@0.7.2': + '@scalar/schemas@0.7.3': dependencies: - '@scalar/helpers': 0.9.0 - '@scalar/validation': 0.6.0 + '@scalar/helpers': 0.9.1 + '@scalar/validation': 0.6.1 - '@scalar/types@0.16.2': + '@scalar/types@0.16.3': dependencies: - '@scalar/helpers': 0.9.0 + '@scalar/helpers': 0.9.1 nanoid: 5.1.16 type-fest: 5.8.0 zod: 4.4.3 - '@scalar/validation@0.6.0': {} + '@scalar/validation@0.6.1': {} '@scure/base@2.2.0': {} From 45264a3e2d1b039c7ca6f44322f5a844e8a527cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 08:13:21 +0000 Subject: [PATCH 0332/1190] chore(deps-dev): bump discord-api-types from 0.38.49 to 0.38.50 (#22732) Bumps [discord-api-types](https://github.com/discordjs/discord-api-types) from 0.38.49 to 0.38.50. - [Release notes](https://github.com/discordjs/discord-api-types/releases) - [Changelog](https://github.com/discordjs/discord-api-types/blob/main/CHANGELOG.md) - [Commits](https://github.com/discordjs/discord-api-types/compare/0.38.49...0.38.50) --- updated-dependencies: - dependency-name: discord-api-types dependency-version: 0.38.50 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8d321cc54342..4df99db52f88 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,7 @@ "@typescript-eslint/parser": "8.64.0", "@vercel/nft": "1.10.2", "@vitest/coverage-v8": "4.1.10", - "discord-api-types": "0.38.49", + "discord-api-types": "0.38.50", "domhandler": "6.0.1", "eslint": "10.7.0", "eslint-nibble": "9.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25ed2a6379fd..0dacc8f6f417 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -369,8 +369,8 @@ importers: specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) discord-api-types: - specifier: 0.38.49 - version: 0.38.49 + specifier: 0.38.50 + version: 0.38.50 domhandler: specifier: 6.0.1 version: 6.0.1 @@ -3592,8 +3592,8 @@ packages: resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 - discord-api-types@0.38.49: - resolution: {integrity: sha512-XnqcWmnFZFAE8ZM8SHAw9DIV8D3Or00rMQ8iQLotrEA2PmXhl+ykaf6L6q4l474hrSUH1JaYcv+iOMRWp2p6Tg==} + discord-api-types@0.38.50: + resolution: {integrity: sha512-J2n/bpIETX3DQ6AJ7/0xbsTLmYiJQtO/LKcXKC1YDbB56OUwtDbdXOFE8Q4g8jVGHBR2VAy1+D4ngaIgkMNV9w==} dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -8917,7 +8917,7 @@ snapshots: dependencies: heap: 0.2.7 - discord-api-types@0.38.49: {} + discord-api-types@0.38.50: {} dom-serializer@1.4.1: dependencies: From 32d03b9a72103462e1015624cf8f4f19fbbf6f0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 08:15:31 +0000 Subject: [PATCH 0333/1190] chore(deps): bump @hono/node-server from 2.0.9 to 2.0.10 (#22735) Bumps [@hono/node-server](https://github.com/honojs/node-server) from 2.0.9 to 2.0.10. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v2.0.9...v2.0.10) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 2.0.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4df99db52f88..62fddf16fe5a 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.14.0", - "@hono/node-server": "2.0.9", + "@hono/node-server": "2.0.10", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0dacc8f6f417..5918f1d9c01a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 6.14.0 version: 6.14.0 '@hono/node-server': - specifier: 2.0.9 - version: 2.0.9(hono@4.12.30) + specifier: 2.0.10 + version: 2.0.10(hono@4.12.30) '@hono/zod-openapi': specifier: 1.5.1 version: 1.5.1(hono@4.12.30)(zod@4.4.3) @@ -1096,8 +1096,8 @@ packages: engines: {node: '>=14'} hasBin: true - '@hono/node-server@2.0.9': - resolution: {integrity: sha512-cNMw3ziCdDcx0+ldta60zvUL5XO0OLt521n2hjPp0PB0ugczDCEczXsf33qQbkKIXWGFQ03f0LC85u6YK8pCLA==} + '@hono/node-server@2.0.10': + resolution: {integrity: sha512-ZcnNVhKTmyDJeg0UlnZjvM73JBsTAuhrH/J4fjwGOw59PwOW51r4J+p6CsKZWXdKSme4MFqU62CZMOsdDrU4CA==} engines: {node: '>=20'} peerDependencies: hono: ^4 @@ -6924,7 +6924,7 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.9(hono@4.12.30)': + '@hono/node-server@2.0.10(hono@4.12.30)': dependencies: hono: 4.12.30 From 69f6ad79bf1713a2e05922ed5c48f32762df7674 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 08:15:49 +0000 Subject: [PATCH 0334/1190] chore(deps): bump @notionhq/client from 5.23.1 to 5.23.2 (#22733) Bumps [@notionhq/client](https://github.com/makenotion/notion-sdk-js) from 5.23.1 to 5.23.2. - [Release notes](https://github.com/makenotion/notion-sdk-js/releases) - [Commits](https://github.com/makenotion/notion-sdk-js/compare/v5.23.1...v5.23.2) --- updated-dependencies: - dependency-name: "@notionhq/client" dependency-version: 5.23.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 62fddf16fe5a..a0f0ea9f8c24 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@hono/node-server": "2.0.10", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", - "@notionhq/client": "5.23.1", + "@notionhq/client": "5.23.2", "@opentelemetry/api": "1.9.1", "@opentelemetry/exporter-prometheus": "0.220.0", "@opentelemetry/exporter-trace-otlp-http": "0.220.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5918f1d9c01a..a794b58e2738 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,8 +56,8 @@ importers: specifier: 3.0.9 version: 3.0.9 '@notionhq/client': - specifier: 5.23.1 - version: 5.23.1 + specifier: 5.23.2 + version: 5.23.2 '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -1467,8 +1467,8 @@ packages: resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==} engines: {node: '>=12.4.0'} - '@notionhq/client@5.23.1': - resolution: {integrity: sha512-Y0jhh0ulft1x1c3uHFzBMHHQZzdfpCUV5JNFAZxzmjKEIkephQ0fle0g0ZqxTmHl1vP1ZOjzq0v47Ao4udBevw==} + '@notionhq/client@5.23.2': + resolution: {integrity: sha512-iyD5VU9MoNxCR0GespotyrMlBpO4kLG+BaZK2JpZW1yfFqrmOYmZGb3wl4xqfq3n1Z9eekFwNcZD6t63SaESig==} engines: {node: '>=18'} '@octokit/auth-token@6.0.0': @@ -7234,7 +7234,7 @@ snapshots: '@nolyfill/side-channel@1.0.44': {} - '@notionhq/client@5.23.1': {} + '@notionhq/client@5.23.2': {} '@octokit/auth-token@6.0.0': {} From 9d08ed9890ed46e15efd43b3112b32423d5a5e9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:41:17 +0800 Subject: [PATCH 0335/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22731) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260715.1 to 5.20260716.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260716.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index a0f0ea9f8c24..b8d298202492 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.5", - "@cloudflare/workers-types": "5.20260715.1", + "@cloudflare/workers-types": "5.20260716.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a794b58e2738..a1e69cde9c94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.5 - version: 0.18.5(@cloudflare/workers-types@5.20260715.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.5(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260715.1 - version: 5.20260715.1 + specifier: 5.20260716.1 + version: 5.20260716.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -475,7 +475,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.111.0 - version: 4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -644,8 +644,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260715.1': - resolution: {integrity: sha512-saxo/nMqQJ1dKDUXp1a2y/+IjKENFVD9+QRefHg5EjJZY20OG3xcEge4PGljbqZiF3AiU4o5ZLS3Vm7cayQIxg==} + '@cloudflare/workers-types@5.20260716.1': + resolution: {integrity: sha512-LqQPmGAvdpQxzZGAMlDI6fnCsTlr8nRQibWsREaERqD0PucwFl25aXpUskSob70uSY2n6K1sp6Te9xkmzSFgaw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3589,7 +3589,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -6595,7 +6595,7 @@ snapshots: optionalDependencies: workerd: 1.20260710.1 - '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260715.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6603,7 +6603,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6625,7 +6625,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260710.1': optional: true - '@cloudflare/workers-types@5.20260715.1': {} + '@cloudflare/workers-types@5.20260716.1': {} '@colors/colors@1.6.0': {} @@ -12073,7 +12073,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260710.1 '@cloudflare/workerd-windows-64': 1.20260710.1 - wrangler@4.111.0(@cloudflare/workers-types@5.20260715.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1) @@ -12084,7 +12084,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260710.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260715.1 + '@cloudflare/workers-types': 5.20260716.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From ca59e0baab4263b85e17dc484e9573202d1fe66e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:42:14 +0800 Subject: [PATCH 0336/1190] chore(deps): bump cachix/install-nix-action from 31.10.7 to 31.11.0 (#22730) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 31.10.7 to 31.11.0. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md) - [Commits](https://github.com/cachix/install-nix-action/compare/a49548c11d9846ad46ecc0115273879b045f001c...630ae543ea3a38a9a4166f03376c02c50f408342) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-version: 31.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-nix-hash.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-nix-hash.yml b/.github/workflows/update-nix-hash.yml index 1aaae78d1df0..efbb86956d08 100644 --- a/.github/workflows/update-nix-hash.yml +++ b/.github/workflows/update-nix-hash.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Install Nix - uses: cachix/install-nix-action@a49548c11d9846ad46ecc0115273879b045f001c # v31.10.7 + uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 with: nix_path: nixpkgs=channel:nixos-unstable - name: Cache Nix store From f52757f7c60d9efee1d5f774007244ccd37446c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:42:54 +0800 Subject: [PATCH 0337/1190] chore(deps): bump devenv from `83e8d7d` to `4ed83c0` (#22736) Bumps [devenv](https://github.com/cachix/devenv) from `83e8d7d` to `4ed83c0`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/83e8d7d34bdebad98ab936b6af53d57ae67af420...4ed83c00354d5a6b4cece8aa8c55028b4e4421e4) --- updated-dependencies: - dependency-name: devenv dependency-version: 4ed83c00354d5a6b4cece8aa8c55028b4e4421e4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index db12d9d22846..342cad7627c8 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784066496, - "narHash": "sha256-0ULUc0h8q6wAuvNZE89opjnhKUMMiC4GGKM8d+9dh/8=", + "lastModified": 1784142617, + "narHash": "sha256-mN/bY/kthTv86mIVH4trz6NJAfRglO2x4wImvTGkFZ8=", "owner": "cachix", "repo": "devenv", - "rev": "83e8d7d34bdebad98ab936b6af53d57ae67af420", + "rev": "4ed83c00354d5a6b4cece8aa8c55028b4e4421e4", "type": "github" }, "original": { From fd185c3edb920730398433be3a16b1fa307722cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:50:20 +0800 Subject: [PATCH 0338/1190] chore(deps): bump nixpkgs from `18b9261` to `753cc8a` (#22737) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `18b9261` to `753cc8a`. - [Commits](https://github.com/NixOS/nixpkgs/compare/18b9261cb3294b6d2a06d03f96872827b8fe2698...753cc8a3a87467296ddd1fa93f0cc3e81120ee46) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: 753cc8a3a87467296ddd1fa93f0cc3e81120ee46 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 342cad7627c8..ea1ec7fc6861 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784007870, - "narHash": "sha256-djcLt/JJphyNt4eDY9XTly+/WbCK5lqWq9lSgCmJkkQ=", + "lastModified": 1784120854, + "narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "18b9261cb3294b6d2a06d03f96872827b8fe2698", + "rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46", "type": "github" }, "original": { From bd24a2fdcd995eb637b70bed02605ad2af7180cd Mon Sep 17 00:00:00 2001 From: Serhii Hospodarchuk <69005134+gosxrgxx@users.noreply.github.com> Date: Thu, 16 Jul 2026 18:01:26 +0300 Subject: [PATCH 0339/1190] feat(core): add a new environment variable to configure the output format (#22738) --- lib/config.ts | 3 +++ lib/middleware/cache.ts | 2 +- lib/middleware/template.tsx | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index e19ab2b508cb..077dcf19a642 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -67,6 +67,7 @@ type ConfigEnvKeys = | 'DISABLE_NSFW' | 'SUFFIX' | 'TITLE_LENGTH_LIMIT' + | 'FORMAT' // OpenAI | 'OPENAI_API_KEY' | 'OPENAI_MODEL' @@ -340,6 +341,7 @@ export type Config = { }; suffix?: string; titleLengthLimit: number; + format: string; openai: { apiKey?: string; model?: string; @@ -842,6 +844,7 @@ const calculateValue = () => { }, suffix: envs.SUFFIX, titleLengthLimit: toInt(envs.TITLE_LENGTH_LIMIT, 150), + format: envs.FORMAT || 'rss', openai: { apiKey: envs.OPENAI_API_KEY, model: envs.OPENAI_MODEL || 'gpt-3.5-turbo-16k', diff --git a/lib/middleware/cache.ts b/lib/middleware/cache.ts index fe5acf34d799..0a9d28cb8869 100644 --- a/lib/middleware/cache.ts +++ b/lib/middleware/cache.ts @@ -19,7 +19,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { } const requestPath = ctx.req.path; - const format = `:${ctx.req.query('format') || 'rss'}`; + const format = `:${ctx.req.query('format') || config.format}`; const limit = ctx.req.query('limit') ? `:${ctx.req.query('limit')}` : ''; const key = 'rsshub:koa-redis-cache:' + h64ToString(requestPath + format + limit); const controlKey = 'rsshub:path-requested:' + h64ToString(requestPath + format + limit); diff --git a/lib/middleware/template.tsx b/lib/middleware/template.tsx index ecc947ee5375..8983b8c2d8d6 100644 --- a/lib/middleware/template.tsx +++ b/lib/middleware/template.tsx @@ -20,7 +20,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { } const data: Data = ctx.get('data'); - const outputType = ctx.req.query('format') || 'rss'; + const outputType = ctx.req.query('format') || config.format; // only enable when debugInfo=true if (config.debugInfo) { From 7ad67ad56dce475d5412c7997986e41fe73b554b Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 17 Jul 2026 02:32:37 +0800 Subject: [PATCH 0340/1190] fix(route/xiaohongshu): use cover as url (#22740) --- lib/routes/xiaohongshu/user.ts | 19 ++++++++------ lib/routes/xiaohongshu/util.ts | 46 ++++++++++++++-------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/lib/routes/xiaohongshu/user.ts b/lib/routes/xiaohongshu/user.ts index 4550898e4bf0..031d54f84121 100644 --- a/lib/routes/xiaohongshu/user.ts +++ b/lib/routes/xiaohongshu/user.ts @@ -98,14 +98,17 @@ async function getUserFeeds(url: string, category: string) { const renderNote = (notes) => notes.flatMap((n) => - n.map(({ id, noteCard }) => ({ - title: noteCard.displayTitle, - link: new URL(noteCard.noteId || id, url).href, - guid: noteCard.displayTitle, - description: `
      ${noteCard.displayTitle}`, - author: noteCard.user.nickname, - upvotes: noteCard.interactInfo.likedCount, - })) + n.map(({ noteCard }) => { + const coverUrl = noteCard.cover.infoList.pop().url; + return { + title: noteCard.displayTitle, + link: coverUrl, + guid: noteCard.displayTitle, + description: `
      ${noteCard.displayTitle}`, + author: noteCard.user.nickname, + upvotes: noteCard.interactInfo.likedCount, + }; + }) ); const renderCollect = (collect) => { if (!collect) { diff --git a/lib/routes/xiaohongshu/util.ts b/lib/routes/xiaohongshu/util.ts index a3084ff28e88..cf1b40e2e1df 100644 --- a/lib/routes/xiaohongshu/util.ts +++ b/lib/routes/xiaohongshu/util.ts @@ -1,3 +1,4 @@ +import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import { config } from '@/config'; @@ -77,17 +78,22 @@ const getUser = (url, cache) => await page.goto(url, { waitUntil: 'domcontentloaded', }); - await page.waitForSelector('div.reds-tab-item:nth-child(2), #red-captcha'); + try { + await page.waitForSelector('div.reds-tab-item:nth-child(2), .fe-verify-box', { timeout: 3000 }); + } catch { + // + } - if (await page.$('#red-captcha')) { + if (await page.$('.fe-verify-box')) { throw new CaptchaError('小红书风控校验,请稍后再试'); } - const initialState = await page.evaluate(() => (window as any).__INITIAL_STATE__); + const content = await page.content(); + const initialState = JSON.parse(extractInitialState(load(content))); if (!(await page.$('.lock-icon'))) { - await page.click('div.reds-tab-item:nth-child(2)'); try { + await page.click('div.reds-tab-item:nth-child(2)', { timeout: 3000 }); const response = await page.waitForResponse( (res) => { const req = res.request(); @@ -105,6 +111,10 @@ const getUser = (url, cache) => userPageData = userPageData._rawValue || userPageData; notes = notes._rawValue || notes; + if (!userPageData.basicInfo) { + throw new Error(`小红书未返回用户数据,请稍后再试: ${JSON.stringify(userPageData.result)}`); + } + return { userPageData, notes, collect }; } finally { await destroy(); @@ -299,38 +309,20 @@ async function getUserWithCookie(url: string) { } // Add helper function to extract initial state -function extractInitialState($) { - let script = $('script') - .filter((i, script) => { - const text = script.children[0]?.data; - return text?.startsWith('window.__INITIAL_STATE__='); - }) - .text(); - script = script.slice('window.__INITIAL_STATE__='.length); +function extractInitialState($: CheerioAPI) { + let script = $('script:contains("window.__INITIAL_STATE__=")').text(); + script = script.slice(script.indexOf('window.__INITIAL_STATE__=') + 'window.__INITIAL_STATE__='.length); script = script.replaceAll('undefined', 'null'); return script; } // Add helper function to extract initial SSR state -function extractInitialSsrState($) { - let script = $('script') - .filter((i, script) => { - const text = script.children[0]?.data; - return text?.includes('window.__INITIAL_SSR_STATE__='); - }) - .text(); +function extractInitialSsrState($: CheerioAPI) { + const script = $('script:contains("window.__INITIAL_SSR_STATE__=")').text(); const match = script.match(/window\.__INITIAL_SSR_STATE__\s*=\s*(\{[\s\S]*?\})\s*(?:;|$)/); if (match) { return match[1].replaceAll('undefined', 'null'); } - // Fallback: try simple extraction - const startMarker = 'window.__INITIAL_SSR_STATE__='; - const startIndex = script.indexOf(startMarker); - if (startIndex !== -1) { - script = script.slice(startIndex + startMarker.length); - script = script.replaceAll('undefined', 'null'); - return script; - } throw new Error('Cannot extract __INITIAL_SSR_STATE__'); } From 0bc92d27e3ab00b51c5091f24e3f7430d79f280b Mon Sep 17 00:00:00 2001 From: TonyRL Date: Fri, 17 Jul 2026 03:58:25 +0800 Subject: [PATCH 0341/1190] feat(index): adaptive theme & icons --- lib/views/error.tsx | 22 +++++++----- lib/views/index.tsx | 20 +++++++---- lib/views/layout.tsx | 83 +++++++++++++++++++++++++------------------- 3 files changed, 74 insertions(+), 51 deletions(-) diff --git a/lib/views/error.tsx b/lib/views/error.tsx index f4c1dee730ac..5d38b18542cf 100644 --- a/lib/views/error.tsx +++ b/lib/views/error.tsx @@ -26,22 +26,22 @@ const Index: FC<{

      Error Message:
      - {message} + {message}

      - Route: {errorRoute} + Route: {errorRoute}

      - Full Route: {requestPath} + Full Route: {requestPath}

      - Node Version: {nodeVersion} + Node Version: {nodeVersion}

      - Git Hash: {gitHash} + Git Hash: {gitHash}

      - Git Date: {gitDate?.toUTCString()} + Git Date: {gitDate?.toUTCString()}

    @@ -107,7 +107,10 @@ const Index: FC<{

    - github + + + github + telegram group @@ -116,7 +119,10 @@ const Index: FC<{ telegram channel - X + + + X +

    diff --git a/lib/views/index.tsx b/lib/views/index.tsx index cc2a2aec7be2..ad54e0ea17cd 100644 --- a/lib/views/index.tsx +++ b/lib/views/index.tsx @@ -140,9 +140,9 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {

    Welcome to RSSHub!

    -

    The world's largest RSS Network.

    -

    If you see this page, the RSSHub is successfully installed and working.

    -

    +

    The world's largest RSS Network.

    +

    If you see this page, the RSSHub is successfully installed and working.

    +

    Pair your feeds with{' '} Folo @@ -158,10 +158,10 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => { - + - +

    {info.showDebug ? ( @@ -180,7 +180,10 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {

    - github + + + github + telegram group @@ -189,7 +192,10 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => { telegram channel - X + + + X +

    diff --git a/lib/views/layout.tsx b/lib/views/layout.tsx index 11818378e708..04c93856a825 100644 --- a/lib/views/layout.tsx +++ b/lib/views/layout.tsx @@ -4,47 +4,58 @@ export const Layout: FC = (props) => ( Welcome to RSSHub! + - {props.children} + {props.children} ); From f71d14fbcbd4fe838ee467c9123ab4c9839d9618 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 17 Jul 2026 09:31:34 +0800 Subject: [PATCH 0342/1190] feat(route): add ltn (#22741) --- lib/routes/ltn/def.ts | 109 ++++++++++++++++++++++++++++++++++++ lib/routes/ltn/namespace.ts | 7 +++ 2 files changed, 116 insertions(+) create mode 100644 lib/routes/ltn/def.ts create mode 100644 lib/routes/ltn/namespace.ts diff --git a/lib/routes/ltn/def.ts b/lib/routes/ltn/def.ts new file mode 100644 index 000000000000..f95b76277fe4 --- /dev/null +++ b/lib/routes/ltn/def.ts @@ -0,0 +1,109 @@ +import { load } from 'cheerio'; +import type { Context } from 'hono'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const channels = { + breakingnewslist: '軍情動態', + 'list/10': '國際軍情', + 'list/11': '台海軍情', + 'list/22': '軍情看板', + mitlist: '國防MIT', + 'list/12': '國機國造', + 'list/13': '國艦國造', + 'list/14': '潛艦國造', + 'list/23': '飛彈', + 'list/24': '戰甲車國造', + 'list/15': '國防產業', + 'list/25': '其他裝備', + pedialist: '軍武百科', + 'list/16': '圖解軍武', + 'list/17': '陸用裝備', + 'list/18': '海軍系統', + 'list/19': '空軍系統', + historylist: '國防祕辛', + stylelist: '軍風尚', + 'list/27': '將軍官邸故事', + 'list/28': '軍事風餐廳', + 'list/29': '軍風文創', + 'list/30': '軍風世界', + filelist: '軍武書摘', + forumlist: '自由講武堂', + 'list/20': '投書', + 'list/21': '論壇', + peoplelist: '軍情人物', +}; + +export const route: Route = { + path: '/def/:channel{.+}?', + categories: ['traditional-media'], + example: '/ltn/def/breakingnewslist', + parameters: { + channel: { + description: 'Channel, see the table below', + options: Object.entries(channels).map(([value, label]) => ({ value, label })), + default: 'breakingnewslist', + }, + }, + radar: [ + { + source: ['def.ltn.com.tw/:channel'], + target: '/def/:channel', + }, + { + source: ['def.ltn.com.tw/list/:id'], + target: '/def/list/:id', + }, + ], + name: '自由軍武頻道', + maintainers: ['TonyRL'], + handler, + url: 'def.ltn.com.tw', +}; + +async function handler(ctx: Context) { + const { channel = 'breakingnewslist' } = ctx.req.param(); + const baseUrl = 'https://def.ltn.com.tw'; + + const response = await ofetch(`${baseUrl}/ajax/${channel}/1`, { responseType: 'json' }); + + const list: DataItem[] = response.map((item) => ({ + title: item.title, + link: item.url, + pubDate: timezone(parseDate(item.createTime, 'YYYYMMDDHHmmss'), 8), + image: item.url_b, + })); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link!, async () => { + const response = await ofetch(item.link!); + const $ = load(response); + + const content = $('.whitecon:not(.template) div[data-desc="內文"]'); + content.find('.before_ir, .after_ir, [id^="ad"], .appE1121').remove(); + content.find('img[data-src]').each((_, img) => { + $(img).attr('src', $(img).attr('data-src')!).removeAttr('data-src'); + }); + content.find('iframe[src^="https://www.youtube.com/embed/"]').each((_, iframe) => { + $(iframe).attr('referrerpolicy', 'strict-origin-when-cross-origin').removeAttr('allow'); + }); + + item.description = content.html()?.trim(); + return item; + }) + ) + ); + + return { + title: `${channels[channel] ?? ''} - 自由軍武頻道`, + link: `${baseUrl}/${channel}`, + language: 'zh-TW' as const, + image: `${baseUrl}/assets/images/1200_def.png`, + item: items, + }; +} diff --git a/lib/routes/ltn/namespace.ts b/lib/routes/ltn/namespace.ts new file mode 100644 index 000000000000..7518316429de --- /dev/null +++ b/lib/routes/ltn/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '自由時報', + url: 'ltn.com.tw', + lang: 'zh-TW', +}; From 18ad0daeb676a17ab75dac2bcc8a064822ed0bca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:17:06 +0000 Subject: [PATCH 0343/1190] chore(deps-dev): bump tsdown from 0.22.8 to 0.22.9 (#22745) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.8 to 0.22.9. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.8...v0.22.9) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 410 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 294 insertions(+), 118 deletions(-) diff --git a/package.json b/package.json index b8d298202492..7dad1368f8c7 100644 --- a/package.json +++ b/package.json @@ -201,7 +201,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.8", + "tsdown": "0.22.9", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1e69cde9c94..531160dfe536 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -453,8 +453,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.8 - version: 0.22.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) + specifier: 0.22.9 + version: 0.22.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -2205,30 +2205,60 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.2.0': + resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.1.5': resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.2.0': + resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.1.5': resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.2.0': + resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.1.5': resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.2.0': + resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.1.5': resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2236,6 +2266,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.2.0': + resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.1.5': resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2243,6 +2280,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.2.0': + resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.1.5': resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2250,6 +2294,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.1.5': resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2257,6 +2308,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.0': + resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.1.5': resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2264,6 +2322,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.0': + resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.1.5': resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2271,29 +2336,59 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.2.0': + resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.1.5': resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.2.0': + resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.1.5': resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.2.0': + resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.1.5': resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.2.0': + resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.1.5': resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.0': + resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -2935,125 +3030,125 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.6.3': - resolution: {integrity: sha512-pbDcFygFmbvo0jGFq5U0m5Sa9U8aVttVJWbBHZDZ68w/X48HdDWS1V4XvBacs8XkmWbTr/ef5fMGG7HsngqTmg==} + '@yuku-codegen/binding-darwin-arm64@0.6.5': + resolution: {integrity: sha512-uR1OCnftxC89GP6ZLPbaAXU9wdycmXt5BUQAOaDUmU/b38WJefse4RcL793rsOw1rkb8UC3UqP9F8hQ0lDB5xg==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.6.3': - resolution: {integrity: sha512-qZMrnA4i8OfqL3NMGoOvLdh1vby8cCGsmNo+tJQIIezXO557m3fdQLenz/57GqtBnnGWzWqd/aDp+faNxWlLwQ==} + '@yuku-codegen/binding-darwin-x64@0.6.5': + resolution: {integrity: sha512-b5m/NymPwAV8OYmWPK0GwVeXw/D7EMIr1GkgL9fW/JCFDvkSkBTz8cbGl5Jkoxr+bamOZy4C1ySngpU///4CFQ==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.6.3': - resolution: {integrity: sha512-2+SFpfem2GBH6BlCTAq6R44bZwuieduwRWHkCQnSgbK8tdEjMwB0Ix0IchryVBn6hdiWCZSTkSE3UILliRXsRQ==} + '@yuku-codegen/binding-freebsd-x64@0.6.5': + resolution: {integrity: sha512-wLTe/QeBF37qb4bR++t/jLP3LQ7oS76lsZigk7J10IR2GwD4w8GJqhIm1NsAwho6OjWDNnfxkkw/Oadf/jex4Q==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.6.3': - resolution: {integrity: sha512-fbxg3cBPdJ++36DXtdzcoKw2xzFov91Wxvmn1khX9MXQbDqJQLJmITZhtokcZsj4uGJe32sUmxAZnKbUtZLjmA==} + '@yuku-codegen/binding-linux-arm-gnu@0.6.5': + resolution: {integrity: sha512-Clah7LByDMBFkHKeRsUrqoftiyocoYaAmDc7aM14S9qghvKPnbTAtvrG8QOLMkWviWS+rQ94YWFGqzk1cRxAnA==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.6.3': - resolution: {integrity: sha512-Jk4P7kocGEisSvUFIm1VuHO3hC01LvS3sYAAmVVu1/ve5TuZ0iXyl9kIGtd1ZrgUvchgvZWNOaB+/Kq/RO63FA==} + '@yuku-codegen/binding-linux-arm-musl@0.6.5': + resolution: {integrity: sha512-3ghZT7Dtp5tzIyfSFWcLFxtwnXjrrqLhO+MTuw3Am03K9Coo2NQICwwA9DN2HBrb8KXES1U43an1rNgzRw9xog==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.6.3': - resolution: {integrity: sha512-i1xE8Bx1YZLheWtBZHD0Mq3nAIDrhgiH7o8VB4GiCbHufKb4XKj4CqSDMWSC0RYPmn//E+UEd1NsE2NbOux1tQ==} + '@yuku-codegen/binding-linux-arm64-gnu@0.6.5': + resolution: {integrity: sha512-9zT+uVEtVJkvm0Ba5BkUgBXNqw3kcolV9RAf0kSgIe44bi5Lp6MPqCRm9JrWnJTZOySzPII3oRNRl9dtBlOdLg==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.6.3': - resolution: {integrity: sha512-ZeLkC6xZrlDoIJTadHfqTABTmsj2f6wCCtYBYx/RPGgdmQcGLA0NALRl7m0tnK2CT45eNRuOYzsfEZyF0XWM/A==} + '@yuku-codegen/binding-linux-arm64-musl@0.6.5': + resolution: {integrity: sha512-Uyi/vT++0gyVsyan3XvEii8AWbc+yFJ3M0GEloup7eBx1j4FtK7oq23F6mbQD1gNaozGKkN74fCJpIxF176hZA==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.6.3': - resolution: {integrity: sha512-HNYt7zjIChPcnjZRG42CZq3Zn5mqaRo4UcFLr4mIbmGqdhX5hDDE8O8US8YgYyOUosfbBTBFdsbvFwVAx1TOkQ==} + '@yuku-codegen/binding-linux-x64-gnu@0.6.5': + resolution: {integrity: sha512-JZzAbPDh5eQ2GRAAbo7cKcFabPQ9UEAQglk1BE0psoWUDt1wktp4ZX9ZJgDgF9EbSH3UUACo0lIkWUtSjRyRoQ==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.6.3': - resolution: {integrity: sha512-/1ttT31dAQc7hGtXWSEYEgzGtakAyO2C+/GqAzIuKXlGLpNPZgXdR8LZ0iDHDalbEr3AuHIPRV43sWLUrHWdsA==} + '@yuku-codegen/binding-linux-x64-musl@0.6.5': + resolution: {integrity: sha512-Dmh6Qhoo1UNM10eDLs/DND8E3cLfQQboFfZgnnNNIJ2RQ/G0GXGh7Kzff1QmglUNuRami42NkTrsF9VrkCttZg==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.6.3': - resolution: {integrity: sha512-oAArRDU1lkKg+xFEtiQ7C+/wghpqrkBrFWM05W73S+3sZz8JIHH79Q+Qh5gWls3i8vccitUgCnvln5V7xKn3XQ==} + '@yuku-codegen/binding-win32-arm64@0.6.5': + resolution: {integrity: sha512-UAq2wLFT0mRN2rzJfZjmaqVMJD9kYywARc6Wk8Ggnm4p8yJYigNxMkJ0gt3bpd0MOnvre4LFEDyAMr4esRubuQ==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.6.3': - resolution: {integrity: sha512-bhFDcDsvmp5KuBfWTt4carNo1E4LXH7++S8qqZgDtXVqJxs0xMm7gbuqPihA8kBbphM+NzAUm5qmq6ocfqM6kg==} + '@yuku-codegen/binding-win32-x64@0.6.5': + resolution: {integrity: sha512-hlEo+UIMHaEoLHe8woLr9eI6fz+8LRwdRrid/iegVU1N+53zkh4WMkI8N7e4vUNKKMVN2kJo6Z73J+JfskHO1g==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.6.3': - resolution: {integrity: sha512-Xate6yyZgvi7da/gdnZy+Vu5jlFB0LRlb5m4MY6Y98KSQeJPZIhoVXMK2Vsl48XOmPlDIS8lge414HUVUEo+hg==} + '@yuku-parser/binding-darwin-arm64@0.6.5': + resolution: {integrity: sha512-QVdaZzj9T3KdaprM8VYpiYIFJrcJB347P2U3bg683nPQaDNmX733CFtCqpkc1KHccFf199EXx4OazkJlnHvImA==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.6.3': - resolution: {integrity: sha512-WKMZ5UU2HBdGZDEpQoLq+21f1FlS+BjroH1FaVE6zCSeqKmZ7xRP5jIRGtQ4vCYj/k2KHgyABZ16lgK9mTe2Sg==} + '@yuku-parser/binding-darwin-x64@0.6.5': + resolution: {integrity: sha512-9omiEXwzJo3hsJiaohFek44wKRmnNy8o8acf8PXJdVo19I9O5eY7c3Nhca1DRTozoLqIJEI68CH8OoR2/Dv7ww==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.6.3': - resolution: {integrity: sha512-OWX8V2k4bmtl/DNwU3yz3PZa2QXiSqWN3Xk7pNB5IPt7FcJBDlnpkOcX6h3tjMS7CyKE0lMvPUwwcmWSdbjkyA==} + '@yuku-parser/binding-freebsd-x64@0.6.5': + resolution: {integrity: sha512-0BZ14CHc8H3EHmlAMhxTDVMYijsT1nNZUhl6JTm/xzl+Gaun/vahgdaJaFESuweLeZ1WnwVfPVPvjsdfnbTN8g==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.6.3': - resolution: {integrity: sha512-/4LzmPXPaCWqIpY1j3+XVb8rbXIratqCte3A4sGEjua6aVhvVxEVAqeKlBsoGkORWLeqbpcxhgxKwOGM17eexA==} + '@yuku-parser/binding-linux-arm-gnu@0.6.5': + resolution: {integrity: sha512-VidTdzelGosQDkVkU4X/9qrILPT5HunzL5shW9jrq860naAEjGS/41a4s3J5KBRsKRHscwOsbtSzUev1TMdgfg==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.6.3': - resolution: {integrity: sha512-Df4jk0M/eNKKQfYzXBBKhKkmJBpB+XoX2LkMxmlK3GN+fxUdeb8EM78wX+1+eLVl5dZNo6f7gOd6oDV0gChevw==} + '@yuku-parser/binding-linux-arm-musl@0.6.5': + resolution: {integrity: sha512-Xes/SwXQiPFPqLMhnwjRv0s6mlm6V7+jpw/kmCMkal9Q0UfS4hVpFo2T5NNIKNLCj5qAZ+2EPtgDBz0S1lH4xg==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.6.3': - resolution: {integrity: sha512-sRCtDktUgIbbV78SYX3wdGVVm1Hz/nSUS24JgXB4MzUeGNwBNB+eQAWMtBxCGriyJNXK3zwfj+SSgvTmUdPf/A==} + '@yuku-parser/binding-linux-arm64-gnu@0.6.5': + resolution: {integrity: sha512-MaFn3Vh+kBETYXtPYBaCQUMsUk5SqSatM+mp9Vz7COuCKXim7cG8kGcUpqq93wegLEkkEnv8pyiDFFx/gmNOiQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.6.3': - resolution: {integrity: sha512-3J/jV3ROSqlhLyB/6i5EUHxjkom5i59iPvrtiAsnAjHzMsZJJPEke9LSaOsB0rf4MJFH9AmjvsK8gDahTjZy+A==} + '@yuku-parser/binding-linux-arm64-musl@0.6.5': + resolution: {integrity: sha512-s7GAjJ7kzmnEBVAq5c5B+h/DCOSZG7WqTNpFRYUVGQU+D8wf1gMnfDQ5v8zFdByhpHiW+HNerxI8sml5dl47Rw==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.6.3': - resolution: {integrity: sha512-a5mPn/OMSq2Aa2i7eJXcc37Jtw0b89gDO2mDpXN769b06IirEiqOzLNNJd6R7R8DxoadHzY0nLFhYNChE+jyAg==} + '@yuku-parser/binding-linux-x64-gnu@0.6.5': + resolution: {integrity: sha512-5/+mLrZdCtGoKOw/zNJC0+fSy4aKI97JvPm5rr8DXui/l3+BrvU5g7czp1DVHN67I7VzTLizu2n2y5cz2CjzEg==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.6.3': - resolution: {integrity: sha512-kQSjfa6zdvotuXEGNKQ9vZZxE+lcEEbTUMSuvY/5+0crlwBCjEqrf9/W9ViFDoQAEt8WJiu/mtVNYkKAOkjLmA==} + '@yuku-parser/binding-linux-x64-musl@0.6.5': + resolution: {integrity: sha512-Svj4h/WZQ1VdAxaFJrakVi85IzUJPYWcNw15gNPoeNg+/SGGTTkTeUzODiMRY/8ioJ/k3+/K7zQwASIEBF7L3Q==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.6.3': - resolution: {integrity: sha512-ZawdN3R0YKr48BeXCpbax+WDWbgEG6nWyDosVeZasrT5TjgfF4XMP5SfuyMNvRJ4gbTitrcYHZkENSXZ9ncqcg==} + '@yuku-parser/binding-win32-arm64@0.6.5': + resolution: {integrity: sha512-N96G8zoKihVwhMaH+kLp8MFIA1i3+dRaBO4KYK0otTPVdX+3uS2wpUY5vDLj4JbkKARuQA/DUmfgjleoXL6ugA==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.6.3': - resolution: {integrity: sha512-NcqZwBXQhKyfC0Eb+yBxXQcPjZoUstD1Dbr3X4UlOD1QiYfnvAYRKCZJ6nQ6KQ5p30dGaKkQeBL4t3qQtDQNWA==} + '@yuku-parser/binding-win32-x64@0.6.5': + resolution: {integrity: sha512-RP7rz120SodZI/vUc1H4uzIMEZrxeG//d11qWUI5inMbU4YSnufJAScwjV+qQHxD3FAwoueNxsPjrVxagwAymg==} cpu: [x64] os: [win32] @@ -3589,7 +3684,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -5051,6 +5146,10 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -5498,20 +5597,20 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.9: - resolution: {integrity: sha512-d54yt65+ZF/Mk8H6P36As02PAMdaiWRSzVNtJRc1h7nCgUFjuRI4cN2DyTfJyfVpPH6pgy7/2D7YQH1/Rh75Yg==} + rolldown-plugin-dts@0.27.11: + resolution: {integrity: sha512-DnBl4USSTV/h/sgy//QjCLHVo2JypE/52P5QugGeYaEqZmjBz/FYESOld0MhyIhul2U3Vsh41K63UWGHhJU/qQ==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: - '@ts-macro/tsc': ^0.3.6 '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 rolldown: ^1.0.0 typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: - '@ts-macro/tsc': - optional: true '@typescript/native-preview': optional: true + '@volar/typescript': + optional: true typescript: optional: true vue-tsc: @@ -5522,6 +5621,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.2.0: + resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.62.2: resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5889,14 +5993,14 @@ packages: typescript: optional: true - tsdown@0.22.8: - resolution: {integrity: sha512-6FOLlr1iLcE3LheqQt13hVUWtTduJNwF2akPskPe8Tf1hr+N5UULHzrNZYTMNwL6lr2UyQ8iefVBB6tdqp1PCQ==} + tsdown@0.22.9: + resolution: {integrity: sha512-/0QEjQEhOU1t1YxOIAGzFN7bIssd8P0pBpkOmNLCQi3c5UtrcMF5bvq3f30xHJNW9QCA9aUNcNAorMr2CTd6Lg==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.8 - '@tsdown/exe': 0.22.8 + '@tsdown/css': 0.22.9 + '@tsdown/exe': 0.22.9 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6421,11 +6525,11 @@ packages: yuku-ast@0.1.7: resolution: {integrity: sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA==} - yuku-codegen@0.6.3: - resolution: {integrity: sha512-3c9H521tf1RRDu4cNUySfH01sKlALve4HKu2sITk33gLl5HhsvI6ngSuarpWxMPAiJEgqJc/HTvojWQRnYm9/g==} + yuku-codegen@0.6.5: + resolution: {integrity: sha512-g8gbp05j2NNSKe0Uu2ViBRvawzXaWggxS9YwcVJ2d3I99VxbeOeENSUseA8AXPCZj/gcLkQxl7uhTmtK36qh8Q==} - yuku-parser@0.6.3: - resolution: {integrity: sha512-iI6uABvvup9mvv8Mcpz7Tp//gehQlvcSnX4A4/0bf9i6X3RVQDuVUZel8jdpljwlF7WrbKsvD19y55Mc6+sKZw==} + yuku-parser@0.6.5: + resolution: {integrity: sha512-9A5zaOqE3X3wcPOTK97CYInpKwaUGYnwfHWasJaI2Je1OhI4pQmRA6YCSh7oKewEb2iJM4xlJdbwwj4Aydty7w==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -7744,39 +7848,75 @@ snapshots: '@rolldown/binding-android-arm64@1.1.5': optional: true + '@rolldown/binding-android-arm64@1.2.0': + optional: true + '@rolldown/binding-darwin-arm64@1.1.5': optional: true + '@rolldown/binding-darwin-arm64@1.2.0': + optional: true + '@rolldown/binding-darwin-x64@1.1.5': optional: true + '@rolldown/binding-darwin-x64@1.2.0': + optional: true + '@rolldown/binding-freebsd-x64@1.1.5': optional: true + '@rolldown/binding-freebsd-x64@1.2.0': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-musl@1.1.5': optional: true + '@rolldown/binding-linux-arm64-musl@1.2.0': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.1.5': optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-x64-gnu@1.1.5': optional: true + '@rolldown/binding-linux-x64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-x64-musl@1.1.5': optional: true + '@rolldown/binding-linux-x64-musl@1.2.0': + optional: true + '@rolldown/binding-openharmony-arm64@1.1.5': optional: true + '@rolldown/binding-openharmony-arm64@1.2.0': + optional: true + '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: '@emnapi/core': 1.11.1 @@ -7784,12 +7924,25 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true + '@rolldown/binding-wasm32-wasi@1.2.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + '@rolldown/binding-win32-arm64-msvc@1.1.5': optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.0': + optional: true + '@rolldown/binding-win32-x64-msvc@1.1.5': optional: true + '@rolldown/binding-win32-x64-msvc@1.2.0': + optional: true + '@rolldown/pluginutils@1.0.1': {} '@rollup/pluginutils@5.4.0(rollup@4.62.2)': @@ -8398,70 +8551,70 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.6.3': + '@yuku-codegen/binding-darwin-arm64@0.6.5': optional: true - '@yuku-codegen/binding-darwin-x64@0.6.3': + '@yuku-codegen/binding-darwin-x64@0.6.5': optional: true - '@yuku-codegen/binding-freebsd-x64@0.6.3': + '@yuku-codegen/binding-freebsd-x64@0.6.5': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.6.3': + '@yuku-codegen/binding-linux-arm-gnu@0.6.5': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.6.3': + '@yuku-codegen/binding-linux-arm-musl@0.6.5': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.6.3': + '@yuku-codegen/binding-linux-arm64-gnu@0.6.5': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.6.3': + '@yuku-codegen/binding-linux-arm64-musl@0.6.5': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.6.3': + '@yuku-codegen/binding-linux-x64-gnu@0.6.5': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.6.3': + '@yuku-codegen/binding-linux-x64-musl@0.6.5': optional: true - '@yuku-codegen/binding-win32-arm64@0.6.3': + '@yuku-codegen/binding-win32-arm64@0.6.5': optional: true - '@yuku-codegen/binding-win32-x64@0.6.3': + '@yuku-codegen/binding-win32-x64@0.6.5': optional: true - '@yuku-parser/binding-darwin-arm64@0.6.3': + '@yuku-parser/binding-darwin-arm64@0.6.5': optional: true - '@yuku-parser/binding-darwin-x64@0.6.3': + '@yuku-parser/binding-darwin-x64@0.6.5': optional: true - '@yuku-parser/binding-freebsd-x64@0.6.3': + '@yuku-parser/binding-freebsd-x64@0.6.5': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.6.3': + '@yuku-parser/binding-linux-arm-gnu@0.6.5': optional: true - '@yuku-parser/binding-linux-arm-musl@0.6.3': + '@yuku-parser/binding-linux-arm-musl@0.6.5': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.6.3': + '@yuku-parser/binding-linux-arm64-gnu@0.6.5': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.6.3': + '@yuku-parser/binding-linux-arm64-musl@0.6.5': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.6.3': + '@yuku-parser/binding-linux-x64-gnu@0.6.5': optional: true - '@yuku-parser/binding-linux-x64-musl@0.6.3': + '@yuku-parser/binding-linux-x64-musl@0.6.5': optional: true - '@yuku-parser/binding-win32-arm64@0.6.3': + '@yuku-parser/binding-win32-arm64@0.6.5': optional: true - '@yuku-parser/binding-win32-x64@0.6.3': + '@yuku-parser/binding-win32-x64@0.6.5': optional: true '@yuku-toolchain/types@0.5.43': {} @@ -10707,6 +10860,8 @@ snapshots: obug@2.1.3: {} + obug@2.1.4: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -11268,15 +11423,15 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5): + rolldown-plugin-dts@0.27.11(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.5 - obug: 2.1.3 - rolldown: 1.1.5 + obug: 2.1.4 + rolldown: 1.2.0 yuku-ast: 0.1.7 - yuku-codegen: 0.6.3 - yuku-parser: 0.6.3 + yuku-codegen: 0.6.5 + yuku-parser: 0.6.5 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11303,6 +11458,27 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 + rolldown@1.2.0: + dependencies: + '@oxc-project/types': 0.140.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.0 + '@rolldown/binding-darwin-arm64': 1.2.0 + '@rolldown/binding-darwin-x64': 1.2.0 + '@rolldown/binding-freebsd-x64': 1.2.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.0 + '@rolldown/binding-linux-arm64-gnu': 1.2.0 + '@rolldown/binding-linux-arm64-musl': 1.2.0 + '@rolldown/binding-linux-ppc64-gnu': 1.2.0 + '@rolldown/binding-linux-s390x-gnu': 1.2.0 + '@rolldown/binding-linux-x64-gnu': 1.2.0 + '@rolldown/binding-linux-x64-musl': 1.2.0 + '@rolldown/binding-openharmony-arm64': 1.2.0 + '@rolldown/binding-wasm32-wasi': 1.2.0 + '@rolldown/binding-win32-arm64-msvc': 1.2.0 + '@rolldown/binding-win32-x64-msvc': 1.2.0 + rollup@4.62.2: dependencies: '@types/estree': 1.0.9 @@ -11700,7 +11876,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.8(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): + tsdown@0.22.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11708,10 +11884,10 @@ snapshots: empathic: 2.0.1 hookable: 6.1.1 import-without-cache: 0.4.0 - obug: 2.1.3 + obug: 2.1.4 picomatch: 4.0.5 - rolldown: 1.1.5 - rolldown-plugin-dts: 0.27.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.1.5) + rolldown: 1.2.0 + rolldown-plugin-dts: 0.27.11(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) semver: 7.8.5 tinyexec: 1.2.4 tinyglobby: 0.2.17 @@ -11722,8 +11898,8 @@ snapshots: typescript: '@typescript/typescript6@6.0.2' unrun: 0.3.1 transitivePeerDependencies: - - '@ts-macro/tsc' - '@typescript/native-preview' + - '@volar/typescript' - oxc-resolver - vue-tsc @@ -12217,37 +12393,37 @@ snapshots: dependencies: '@yuku-toolchain/types': 0.5.43 - yuku-codegen@0.6.3: + yuku-codegen@0.6.5: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.6.3 - '@yuku-codegen/binding-darwin-x64': 0.6.3 - '@yuku-codegen/binding-freebsd-x64': 0.6.3 - '@yuku-codegen/binding-linux-arm-gnu': 0.6.3 - '@yuku-codegen/binding-linux-arm-musl': 0.6.3 - '@yuku-codegen/binding-linux-arm64-gnu': 0.6.3 - '@yuku-codegen/binding-linux-arm64-musl': 0.6.3 - '@yuku-codegen/binding-linux-x64-gnu': 0.6.3 - '@yuku-codegen/binding-linux-x64-musl': 0.6.3 - '@yuku-codegen/binding-win32-arm64': 0.6.3 - '@yuku-codegen/binding-win32-x64': 0.6.3 - - yuku-parser@0.6.3: + '@yuku-codegen/binding-darwin-arm64': 0.6.5 + '@yuku-codegen/binding-darwin-x64': 0.6.5 + '@yuku-codegen/binding-freebsd-x64': 0.6.5 + '@yuku-codegen/binding-linux-arm-gnu': 0.6.5 + '@yuku-codegen/binding-linux-arm-musl': 0.6.5 + '@yuku-codegen/binding-linux-arm64-gnu': 0.6.5 + '@yuku-codegen/binding-linux-arm64-musl': 0.6.5 + '@yuku-codegen/binding-linux-x64-gnu': 0.6.5 + '@yuku-codegen/binding-linux-x64-musl': 0.6.5 + '@yuku-codegen/binding-win32-arm64': 0.6.5 + '@yuku-codegen/binding-win32-x64': 0.6.5 + + yuku-parser@0.6.5: dependencies: '@yuku-toolchain/types': 0.5.43 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.6.3 - '@yuku-parser/binding-darwin-x64': 0.6.3 - '@yuku-parser/binding-freebsd-x64': 0.6.3 - '@yuku-parser/binding-linux-arm-gnu': 0.6.3 - '@yuku-parser/binding-linux-arm-musl': 0.6.3 - '@yuku-parser/binding-linux-arm64-gnu': 0.6.3 - '@yuku-parser/binding-linux-arm64-musl': 0.6.3 - '@yuku-parser/binding-linux-x64-gnu': 0.6.3 - '@yuku-parser/binding-linux-x64-musl': 0.6.3 - '@yuku-parser/binding-win32-arm64': 0.6.3 - '@yuku-parser/binding-win32-x64': 0.6.3 + '@yuku-parser/binding-darwin-arm64': 0.6.5 + '@yuku-parser/binding-darwin-x64': 0.6.5 + '@yuku-parser/binding-freebsd-x64': 0.6.5 + '@yuku-parser/binding-linux-arm-gnu': 0.6.5 + '@yuku-parser/binding-linux-arm-musl': 0.6.5 + '@yuku-parser/binding-linux-arm64-gnu': 0.6.5 + '@yuku-parser/binding-linux-arm64-musl': 0.6.5 + '@yuku-parser/binding-linux-x64-gnu': 0.6.5 + '@yuku-parser/binding-linux-x64-musl': 0.6.5 + '@yuku-parser/binding-win32-arm64': 0.6.5 + '@yuku-parser/binding-win32-x64': 0.6.5 zod@3.25.76: {} From d9299c695fa3e4cc36a4ef056daec96ffd2e459a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:18:35 +0000 Subject: [PATCH 0344/1190] chore(deps): bump tldts from 7.4.8 to 7.4.9 (#22750) Bumps [tldts](https://github.com/remusao/tldts) from 7.4.8 to 7.4.9. - [Release notes](https://github.com/remusao/tldts/releases) - [Changelog](https://github.com/remusao/tldts/blob/master/CHANGELOG.md) - [Commits](https://github.com/remusao/tldts/compare/v7.4.8...v7.4.9) --- updated-dependencies: - dependency-name: tldts dependency-version: 7.4.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 7dad1368f8c7..05c1c6335c82 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "source-map": "0.7.6", "telegram": "2.26.22", "title": "4.0.1", - "tldts": "7.4.8", + "tldts": "7.4.9", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", "tsx": "4.23.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 531160dfe536..4504939587de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -239,8 +239,8 @@ importers: specifier: 4.0.1 version: 4.0.1 tldts: - specifier: 7.4.8 - version: 7.4.8 + specifier: 7.4.9 + version: 7.4.9 tosource: specifier: 2.0.0-alpha.3 version: 2.0.0-alpha.3 @@ -5915,11 +5915,11 @@ packages: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true - tldts-core@7.4.8: - resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==} + tldts-core@7.4.9: + resolution: {integrity: sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==} - tldts@7.4.8: - resolution: {integrity: sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==} + tldts@7.4.9: + resolution: {integrity: sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==} hasBin: true to-no-case@1.0.2: @@ -11821,11 +11821,11 @@ snapshots: tlds@1.261.0: {} - tldts-core@7.4.8: {} + tldts-core@7.4.9: {} - tldts@7.4.8: + tldts@7.4.9: dependencies: - tldts-core: 7.4.8 + tldts-core: 7.4.9 to-no-case@1.0.2: {} @@ -11846,7 +11846,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.8 + tldts: 7.4.9 tr46@0.0.3: {} From f40e30e375750264664bb72c316d7103e8d026ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:20:26 +0000 Subject: [PATCH 0345/1190] chore(deps): bump @scalar/hono-api-reference from 0.11.10 to 0.11.11 (#22749) Bumps [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono) from 0.11.10 to 0.11.11. - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) --- updated-dependencies: - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.11.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 05c1c6335c82..d4a375b5829e 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@opentelemetry/sdk-trace-base": "2.9.0", "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.11.10", + "@scalar/hono-api-reference": "0.11.11", "@sentry/node": "10.65.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4504939587de..6d996623c147 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,8 +83,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.11.10 - version: 0.11.10(hono@4.12.30) + specifier: 0.11.11 + version: 0.11.11(hono@4.12.30) '@sentry/node': specifier: 10.65.0 version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) @@ -2548,30 +2548,30 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.3.3': - resolution: {integrity: sha512-QMTKZdwtSSV619jV1jKeRCOd358cgJqRBTHQ663CF7EUCZ354qH4js883K3RYg5/eO4oWxOuka308KIj0sZ/Kg==} + '@scalar/client-side-rendering@0.3.4': + resolution: {integrity: sha512-kb3B+FGjvAUr2DU0fe9dVKBLwot1TjOO+iHCTmY8r8FUJySmYKGQYCicRzzilOyTjvKspiZBCEr03PWaWW+1gw==} engines: {node: '>=22'} - '@scalar/helpers@0.9.1': - resolution: {integrity: sha512-UKSLIPfN++f+zzbsZ+F6I0lNrR4yX4PtokjHgGwGiHapxT5VJqAF4zFTIP2KCd27XG7KnAIA7qq62O4xRBxc6w==} + '@scalar/helpers@0.9.2': + resolution: {integrity: sha512-hjyMpMZjTBZQhyByZmz5oUgRKUQJO5V5AOiJxsVEGbUmgA7sJRQeTrXLB+BEwzaKS5nm2opJeNyMBYLFNK4hiQ==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.11.10': - resolution: {integrity: sha512-LluYDOie3F8NqLiAuZ0ESWmtWotSMV9A2/AxIB3NwQk/3JJ+5S9rnIWVY61o7QIDWlwwy6uZ8KqRSV83qmnhZA==} + '@scalar/hono-api-reference@0.11.11': + resolution: {integrity: sha512-yvJ2oqyG9MC4C55/Xvi/Jny5qBYDxDvoVleABhgNG24A93u9ar17qsdSppli94sQOUdX31Qw/yCTm89LHbBRiQ==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/schemas@0.7.3': - resolution: {integrity: sha512-B6S/zUptiRfMsmMy92u/LefpULus1h0q3od9l5xgZc+pslkfFhfFns+QsU0UJX3Lqp3tTsf64c3tIRqH7cZgVA==} + '@scalar/schemas@0.7.4': + resolution: {integrity: sha512-Or31zxR+ceGGhkVU5XBO2Zv7oyGDtjsJOjM2Rr0WRZ1/tRsQc2/FsVv+9kPmCA7sqFL8BKWlNfTXcPITI7WRHw==} engines: {node: '>=22'} - '@scalar/types@0.16.3': - resolution: {integrity: sha512-8TVNlK8AdvIekrapAoEVwy+IVRbMTSDYnpprskAJV4XanmZ52Kru6RmS6d+tbEBfMo2dTgQ9cV9P0kqsAu/AuA==} + '@scalar/types@0.16.4': + resolution: {integrity: sha512-fLf0ANAC3iQq0sIVdmH6aGM+pFSLyD8GfGBxSWcLpI0jE0iFAzX2A3p0qVZm7vqBT4TQnn0fSwg5U+h+FZ52BA==} engines: {node: '>=22'} - '@scalar/validation@0.6.1': - resolution: {integrity: sha512-XeJ+pvxag0rguuRT6hxNSXIsxleB8Gcs6WQ55vFRkB4qo2CCO+wIli+uipzM3pILu5cP7agcL3pgADiZzd1ZNg==} + '@scalar/validation@0.6.2': + resolution: {integrity: sha512-Sc1TkcwGV6aVCO51AyKeaGiP8gpwAHxEtO5d3tZzPV+KsnlC/YokQxFxwBrbIXw73k9hmcExnJyGu3k5i6n6VA==} engines: {node: '>=20'} '@scure/base@2.2.0': @@ -8043,32 +8043,32 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.3.3': + '@scalar/client-side-rendering@0.3.4': dependencies: - '@scalar/schemas': 0.7.3 - '@scalar/types': 0.16.3 - '@scalar/validation': 0.6.1 + '@scalar/schemas': 0.7.4 + '@scalar/types': 0.16.4 + '@scalar/validation': 0.6.2 - '@scalar/helpers@0.9.1': {} + '@scalar/helpers@0.9.2': {} - '@scalar/hono-api-reference@0.11.10(hono@4.12.30)': + '@scalar/hono-api-reference@0.11.11(hono@4.12.30)': dependencies: - '@scalar/client-side-rendering': 0.3.3 + '@scalar/client-side-rendering': 0.3.4 hono: 4.12.30 - '@scalar/schemas@0.7.3': + '@scalar/schemas@0.7.4': dependencies: - '@scalar/helpers': 0.9.1 - '@scalar/validation': 0.6.1 + '@scalar/helpers': 0.9.2 + '@scalar/validation': 0.6.2 - '@scalar/types@0.16.3': + '@scalar/types@0.16.4': dependencies: - '@scalar/helpers': 0.9.1 + '@scalar/helpers': 0.9.2 nanoid: 5.1.16 type-fest: 5.8.0 zod: 4.4.3 - '@scalar/validation@0.6.1': {} + '@scalar/validation@0.6.2': {} '@scure/base@2.2.0': {} From 8aab862099bf3a4020156db8db3977ffd07db2b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:57:45 +0800 Subject: [PATCH 0346/1190] chore(deps): bump devenv from `4ed83c0` to `d773046` (#22751) Bumps [devenv](https://github.com/cachix/devenv) from `4ed83c0` to `d773046`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/4ed83c00354d5a6b4cece8aa8c55028b4e4421e4...d77304696697c43672e10f29d1da401b33ab2778) --- updated-dependencies: - dependency-name: devenv dependency-version: d77304696697c43672e10f29d1da401b33ab2778 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index ea1ec7fc6861..61885b0dacea 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784142617, - "narHash": "sha256-mN/bY/kthTv86mIVH4trz6NJAfRglO2x4wImvTGkFZ8=", + "lastModified": 1784248563, + "narHash": "sha256-rRjR2qADMZbvrDkZzeYrdssLKpbTGymNXLnVdbT6XpQ=", "owner": "cachix", "repo": "devenv", - "rev": "4ed83c00354d5a6b4cece8aa8c55028b4e4421e4", + "rev": "d77304696697c43672e10f29d1da401b33ab2778", "type": "github" }, "original": { From 833397548cccbedd72d865423f2bf39c73ed4bea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:00:28 +0800 Subject: [PATCH 0347/1190] chore(deps): bump actions/attest from 4.1.1 to 4.2.0 (#22743) Bumps [actions/attest](https://github.com/actions/attest) from 4.1.1 to 4.2.0. - [Release notes](https://github.com/actions/attest/releases) - [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest/compare/a1948c3f048ba23858d222213b7c278aabede763...f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6) --- updated-dependencies: - dependency-name: actions/attest dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index ff19902be8e4..10c1227fb8ce 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -118,7 +118,7 @@ jobs: outputs: type=image,compression=zstd,force-compression=true,push-by-digest=true,name-canonical=true,push=true - name: Attest (ordinary version) - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-name: | ${{ vars.DOCKER_USERNAME }}/${{ steps.repo-name.outputs.repo-name }} @@ -173,7 +173,7 @@ jobs: outputs: type=image,compression=zstd,force-compression=true,push-by-digest=true,name-canonical=true,push=true - name: Attest (Chromium-bundled version) - uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1 + uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 with: subject-name: | ${{ vars.DOCKER_USERNAME }}/${{ steps.repo-name.outputs.repo-name }} From a35df7a169c1bccbfce88c11c44716728221d5f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:03:13 +0800 Subject: [PATCH 0348/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22742) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260716.1 to 5.20260717.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260717.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index d4a375b5829e..21df6efb6996 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.5", - "@cloudflare/workers-types": "5.20260716.1", + "@cloudflare/workers-types": "5.20260717.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d996623c147..e744687e7ce1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.5 - version: 0.18.5(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.5(@cloudflare/workers-types@5.20260717.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260716.1 - version: 5.20260716.1 + specifier: 5.20260717.1 + version: 5.20260717.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -475,7 +475,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.111.0 - version: 4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -644,8 +644,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260716.1': - resolution: {integrity: sha512-LqQPmGAvdpQxzZGAMlDI6fnCsTlr8nRQibWsREaERqD0PucwFl25aXpUskSob70uSY2n6K1sp6Te9xkmzSFgaw==} + '@cloudflare/workers-types@5.20260717.1': + resolution: {integrity: sha512-En+uoU8kbQoUcJMAPOQU+3hyQ4INEpiPO04GIyuh5b0Q5+uPB6az+icvkl+SoSzN3gbQFHZACpShRY+AK3zD1Q==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -6699,7 +6699,7 @@ snapshots: optionalDependencies: workerd: 1.20260710.1 - '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260717.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6707,7 +6707,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6729,7 +6729,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260710.1': optional: true - '@cloudflare/workers-types@5.20260716.1': {} + '@cloudflare/workers-types@5.20260717.1': {} '@colors/colors@1.6.0': {} @@ -12249,7 +12249,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260710.1 '@cloudflare/workerd-windows-64': 1.20260710.1 - wrangler@4.111.0(@cloudflare/workers-types@5.20260716.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1) @@ -12260,7 +12260,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260710.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260716.1 + '@cloudflare/workers-types': 5.20260717.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From cf725d3961bb38d3349242d7a98a9187ab03ce8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:14:29 +0800 Subject: [PATCH 0349/1190] chore(deps-dev): bump oxlint-tsgolint in the oxc group (#22744) Bumps the oxc group with 1 update: [oxlint-tsgolint](https://github.com/oxc-project/tsgolint). Updates `oxlint-tsgolint` from 0.24.0 to 0.25.0 - [Release notes](https://github.com/oxc-project/tsgolint/releases) - [Commits](https://github.com/oxc-project/tsgolint/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: oxlint-tsgolint dependency-version: 0.25.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 66 +++++++++++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 21df6efb6996..8a713518ac25 100644 --- a/package.json +++ b/package.json @@ -196,7 +196,7 @@ "oxfmt": "0.59.0", "oxlint": "1.74.0", "oxlint-plugin-eslint": "1.74.0", - "oxlint-tsgolint": "0.24.0", + "oxlint-tsgolint": "0.25.0", "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e744687e7ce1..2025c7d8f7bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -433,13 +433,13 @@ importers: version: 0.59.0 oxlint: specifier: 1.74.0 - version: 1.74.0(oxlint-tsgolint@0.24.0) + version: 1.74.0(oxlint-tsgolint@0.25.0) oxlint-plugin-eslint: specifier: 1.74.0 version: 1.74.0 oxlint-tsgolint: - specifier: 0.24.0 - version: 0.24.0 + specifier: 0.25.0 + version: 0.25.0 remark: specifier: 15.0.1 version: 15.0.1 @@ -1986,33 +1986,33 @@ packages: cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.24.0': - resolution: {integrity: sha512-C2uMmwK5Bc4ri4ysZ6sA8Rcu+A5zBQTp6ml2u0CLLbRZp4kMFPV3yWk8B5DK9Aw7y9bbjogIm75tUwGLFzlsYQ==} + '@oxlint-tsgolint/darwin-arm64@0.25.0': + resolution: {integrity: sha512-87opKlwFP8qS9WHAeETV+kA0fC9Oyj4sg7OxWdI4xQY0WC7zlN6BgG66uE5mvtN5mahkt/gL0i/AVEnX6POq2Q==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.24.0': - resolution: {integrity: sha512-Wgvt/1lRbDxmoNqWQKKcL+UIiqLmdJ+EWLpQa1qzoNVAfNB0PJpa82/8dH1twT/3rSs4zrP5TXPWl4juB71WuQ==} + '@oxlint-tsgolint/darwin-x64@0.25.0': + resolution: {integrity: sha512-HJmuZexsrhqp4WmETn+Soq7Ogt5F0jirv+cYRSniIPe+d/x5beQzLX69xOLhQRE+8FLGETe7FahWMVP8x0dW4g==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.24.0': - resolution: {integrity: sha512-PB1rxII7KV83+ASY4sSkXtqvpij6ME66+QCRL49uksi/ofs2Rf/UVboYr095n0Rkbl2wgvlsHGl6DHC361jQUQ==} + '@oxlint-tsgolint/linux-arm64@0.25.0': + resolution: {integrity: sha512-aNyYsPREvCJi3qjfBA0sQB7DhT3y/W5Ac2JI2D8IJynoTOAhVZj401Si6901oDajlBWyqJqqojudn0VgHB6+7A==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.24.0': - resolution: {integrity: sha512-xcz3CxKmjTQLREtE/UShh+ruWmm9nAb7UM9zKcD65BStiuYgOakAKkPHl4YS5DztpVcDrE0+HqbOolTlRKYWmw==} + '@oxlint-tsgolint/linux-x64@0.25.0': + resolution: {integrity: sha512-+60+VjK9Mch3uA5WlTdNHuAm5+WA7wPPjuWdPWlU0F6JJpYpGZXUpO1RPKuFEWsBpNbLcLeJ0LbCJ1doWu58NA==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.24.0': - resolution: {integrity: sha512-A2i6ZGBec3i20S7RaxkgHc6r3HYtD5Mn7j/mb22NkTz14u0JuudvTu6JggAnbGMcv8+dBKQI//EasxSPJLD8pw==} + '@oxlint-tsgolint/win32-arm64@0.25.0': + resolution: {integrity: sha512-r53TO+eHp/t53nnUkQJfrYYXODPAxmtf3RUFQG5XsE2hD21IunliOaAdZXP2UwzCx+r/fbNaEelqTaAHcDr57w==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.24.0': - resolution: {integrity: sha512-0ZbGd9qRB6zs82moekaKdEvncRANq49EAwfNX62JpTS46feXUhKAuoyVDvZMj6Rywejylrmmu79Wo6faYCo4Ew==} + '@oxlint-tsgolint/win32-x64@0.25.0': + resolution: {integrity: sha512-vqe66B+gL9HarhyHemdlfC2VWT7eoA+o/ufZ7zT6AGHv64boyDZIJS3U+rpZo+ey4O7wZtiS/vYR2fWqDoFeBw==} cpu: [x64] os: [win32] @@ -5223,8 +5223,8 @@ packages: resolution: {integrity: sha512-AtZ4w3owo4xoY5fe/Juy9hmzEnekVPd/DDiPlOVGfR2linDTp/XVu0xK7ODNK4CCLvnnfCob/UeWN6kSiqm2CQ==} engines: {node: ^20.19.0 || >=22.12.0} - oxlint-tsgolint@0.24.0: - resolution: {integrity: sha512-giCk5sEvG02d5tzPmFMX3hem8ndzEEu1xvGYS5OwNfO2WGl6ZVxt5LjE0yiMDoz94INI7XkXwgFAQiydPvVHDw==} + oxlint-tsgolint@0.25.0: + resolution: {integrity: sha512-7DBpqyLZCfyoXiivyfzt9Xmju/K1RcN+Y1W7buEwrgRCWWF11v9alypPqWGZBmh2erDkKL/kVyhKUH2Px+t13A==} hasBin: true oxlint@1.74.0: @@ -7710,22 +7710,22 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.59.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.24.0': + '@oxlint-tsgolint/darwin-arm64@0.25.0': optional: true - '@oxlint-tsgolint/darwin-x64@0.24.0': + '@oxlint-tsgolint/darwin-x64@0.25.0': optional: true - '@oxlint-tsgolint/linux-arm64@0.24.0': + '@oxlint-tsgolint/linux-arm64@0.25.0': optional: true - '@oxlint-tsgolint/linux-x64@0.24.0': + '@oxlint-tsgolint/linux-x64@0.25.0': optional: true - '@oxlint-tsgolint/win32-arm64@0.24.0': + '@oxlint-tsgolint/win32-arm64@0.25.0': optional: true - '@oxlint-tsgolint/win32-x64@0.24.0': + '@oxlint-tsgolint/win32-x64@0.25.0': optional: true '@oxlint/binding-android-arm-eabi@1.74.0': @@ -11001,16 +11001,16 @@ snapshots: oxlint-plugin-eslint@1.74.0: {} - oxlint-tsgolint@0.24.0: + oxlint-tsgolint@0.25.0: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.24.0 - '@oxlint-tsgolint/darwin-x64': 0.24.0 - '@oxlint-tsgolint/linux-arm64': 0.24.0 - '@oxlint-tsgolint/linux-x64': 0.24.0 - '@oxlint-tsgolint/win32-arm64': 0.24.0 - '@oxlint-tsgolint/win32-x64': 0.24.0 - - oxlint@1.74.0(oxlint-tsgolint@0.24.0): + '@oxlint-tsgolint/darwin-arm64': 0.25.0 + '@oxlint-tsgolint/darwin-x64': 0.25.0 + '@oxlint-tsgolint/linux-arm64': 0.25.0 + '@oxlint-tsgolint/linux-x64': 0.25.0 + '@oxlint-tsgolint/win32-arm64': 0.25.0 + '@oxlint-tsgolint/win32-x64': 0.25.0 + + oxlint@1.74.0(oxlint-tsgolint@0.25.0): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.74.0 '@oxlint/binding-android-arm64': 1.74.0 @@ -11031,7 +11031,7 @@ snapshots: '@oxlint/binding-win32-arm64-msvc': 1.74.0 '@oxlint/binding-win32-ia32-msvc': 1.74.0 '@oxlint/binding-win32-x64-msvc': 1.74.0 - oxlint-tsgolint: 0.24.0 + oxlint-tsgolint: 0.25.0 p-cancelable@4.0.1: {} From 7fc29de41761a5fa6259b03f2e2020c555bd1dc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:17:31 +0800 Subject: [PATCH 0350/1190] chore(deps): bump @sentry/node from 10.65.0 to 10.66.0 (#22746) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.65.0 to 10.66.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.65.0...10.66.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.66.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 95 +++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/package.json b/package.json index 8a713518ac25..12a217a6bc61 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.11.11", - "@sentry/node": "10.65.0", + "@sentry/node": "10.66.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2025c7d8f7bc..da1f9396eb42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,8 +86,8 @@ importers: specifier: 0.11.11 version: 0.11.11(hono@4.12.30) '@sentry/node': - specifier: 10.65.0 - version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) + specifier: 10.66.0 + version: 10.66.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -500,16 +500,16 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@apm-js-collab/code-transformer-bundler-plugins@0.5.0': - resolution: {integrity: sha512-YxLBY5nGlurL7QeJLq6e5g0ouBpAp0pwgyA/5rHXEXwhiPLn9ZHbT+Y2LlP90GT872cSocfjWRYu/fnpuBudNQ==} + '@apm-js-collab/code-transformer-bundler-plugins@0.6.2': + resolution: {integrity: sha512-5vBrtIEL+UVbO0YWWoyYG4QMgR+ZfnIL3xlteIkAmU7YaAPhc28k3md/NM14tnkfXKjKOn9yUzEA9AYUmNpvJg==} engines: {node: '>=18.0.0'} - '@apm-js-collab/code-transformer@0.15.0': - resolution: {integrity: sha512-XmXYVs8CzJ1Aj79noVbn2weUO/XWtRyURpGqx7aU7DOXlUQhR0WKOQNF0okh7PCeY37vxf7kU3v57OAkEPm3ww==} + '@apm-js-collab/code-transformer@0.18.0': + resolution: {integrity: sha512-aN3Oq8r1J3gPJtCwErP664gM0+HhM1I1lujPr9TMTCcEl/joQQbpGpeMdts9B1+W2wHMsvioDMv5F4PvMWE6gw==} hasBin: true - '@apm-js-collab/tracing-hooks@0.10.1': - resolution: {integrity: sha512-w2OWXR7FWrKqSziuE9+QclaZrStxO/8+OwbXM635s/zs0Eez1Qo3ivSPdB2WsaPY/iznKTytONPx/PitD7IXcA==} + '@apm-js-collab/tracing-hooks@0.13.0': + resolution: {integrity: sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==} '@asamuzakjp/css-color@5.1.11': resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} @@ -2585,16 +2585,16 @@ packages: peerDependencies: selderee: ~0.12.0 - '@sentry/conventions@0.15.1': - resolution: {integrity: sha512-ZLP8bRdMON3prWE2tJyImuYscCxdcJeIPIhrOs/rgyFm3C1nCh1B6gdvPj3AZ5zW08oSFFCsq7T+tYEW3h8MNA==} + '@sentry/conventions@0.16.0': + resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.65.0': - resolution: {integrity: sha512-3aqtmM5NgNGo45BNaaBzi0LPQZAw//NEL4HKS5fXm12pJMa4KEkze8DEKnkTEIrGnWaOJKamecHKlnNg/Mqf/Q==} + '@sentry/core@10.66.0': + resolution: {integrity: sha512-9UbgSvds7bMJsP561eWmeyMLcfOmnwxtnx2QuW3yLobzP2Ob7CyJCOzP4tGzlTAGDrzShkFEZhiyuBUKiEK2oQ==} engines: {node: '>=18'} - '@sentry/node-core@10.65.0': - resolution: {integrity: sha512-U01X9mPT+jZnsLPmPWfBU67Ka+t/Sdd9RGAuvGoKdrI6N47a/9PDkM9oCW+kj0fmZwogZHTgSnzJU5oi3pImgA==} + '@sentry/node-core@10.66.0': + resolution: {integrity: sha512-SUnXHROqSdSetKgZC1goDEKCuMz3OmQ1h4rxzWeexLyqan+pensZXfLouP6jzZXlA8e/HP7uQg78LnfqYDcZlQ==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2614,20 +2614,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.65.0': - resolution: {integrity: sha512-t35dcdyksysVch/m/XdLgGJqGKJhr9eMD30Ctn3TeQ8yMB0wNXySfjPR5Yg93fpjmfaHtzc6iYIXRAvgNVfrvA==} + '@sentry/node@10.66.0': + resolution: {integrity: sha512-5Ow7iQiRjaSaEOmqEIkYV368hFFzShIZCXPoj+wX3JtOmKFrsNu6lr8/VJlQs7cyjFS6tzE50PrLrD8iAPS/8w==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.65.0': - resolution: {integrity: sha512-8C6FPvm3XBvUrkM52dX3Gz0p2H0Ij8t4sahUA+GTiCz0WM0fnyPeQPGC/b6I4jamV9UXyCZRnE1UEEGCoD+c7A==} + '@sentry/opentelemetry@10.66.0': + resolution: {integrity: sha512-K5Y9IettN9yIOnpqCs40HRLGqaGUoaQ50+ZsLqX2kPCk3TzJVpKZ9icKwaJ4Nxm72QgGVT5Ovfr/9FXbgd3b/Q==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.65.0': - resolution: {integrity: sha512-80toEFD6s+0Le7jrYB6pHWLF703WSg0WyavAWqrBGWG8JkREHgedAxzFYgoY5GlMI756qk6Ea7UzhJTHd2zAXA==} + '@sentry/server-utils@10.66.0': + resolution: {integrity: sha512-h9EM9Wz9Mc6w2Vn7Fyh6ozjy4JC2UbUvWf9Ra1HYA4KMeYqjFA5/o0oB4pg4w/TF+rb+9OF/HNqxjuczxpudpA==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -6573,14 +6573,14 @@ snapshots: '@actions/io@3.0.2': {} - '@apm-js-collab/code-transformer-bundler-plugins@0.5.0': + '@apm-js-collab/code-transformer-bundler-plugins@0.6.2': dependencies: - '@apm-js-collab/code-transformer': 0.15.0 + '@apm-js-collab/code-transformer': 0.18.0 es-module-lexer: 2.3.1 magic-string: 0.30.21 module-details-from-path: 1.0.4 - '@apm-js-collab/code-transformer@0.15.0': + '@apm-js-collab/code-transformer@0.18.0': dependencies: '@types/estree': 1.0.9 astring: 1.9.0 @@ -6589,9 +6589,9 @@ snapshots: semifies: 1.0.0 source-map: 0.6.1 - '@apm-js-collab/tracing-hooks@0.10.1': + '@apm-js-collab/tracing-hooks@0.13.0': dependencies: - '@apm-js-collab/code-transformer': 0.15.0 + '@apm-js-collab/code-transformer': 0.18.0 debug: 4.4.3 module-details-from-path: 1.0.4 transitivePeerDependencies: @@ -8080,17 +8080,17 @@ snapshots: domhandler: 5.0.3 selderee: 0.12.0 - '@sentry/conventions@0.15.1': {} + '@sentry/conventions@0.16.0': {} - '@sentry/core@10.65.0': + '@sentry/core@10.66.0': dependencies: - '@sentry/conventions': 0.15.1 + '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: - '@sentry/conventions': 0.15.1 - '@sentry/core': 10.65.0 - '@sentry/opentelemetry': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.66.0 + '@sentry/opentelemetry': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.3.1 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -8099,38 +8099,37 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.66.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/conventions': 0.15.1 - '@sentry/core': 10.65.0 - '@sentry/node-core': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.65.0 + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.66.0 + '@sentry/node-core': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.66.0 import-in-the-middle: 3.3.1 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/conventions': 0.15.1 - '@sentry/core': 10.65.0 + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.66.0 - '@sentry/server-utils@10.65.0': + '@sentry/server-utils@10.66.0': dependencies: - '@apm-js-collab/code-transformer': 0.15.0 - '@apm-js-collab/code-transformer-bundler-plugins': 0.5.0 - '@apm-js-collab/tracing-hooks': 0.10.1 - '@sentry/conventions': 0.15.1 - '@sentry/core': 10.65.0 - magic-string: 0.30.21 + '@apm-js-collab/code-transformer': 0.18.0 + '@apm-js-collab/code-transformer-bundler-plugins': 0.6.2 + '@apm-js-collab/tracing-hooks': 0.13.0 + '@sentry/conventions': 0.16.0 + '@sentry/core': 10.66.0 transitivePeerDependencies: - supports-color From de7e6ff5c4144bceb115cb3b73d708f1a1aeab66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:24:18 +0800 Subject: [PATCH 0351/1190] chore(deps-dev): bump eslint-plugin-simple-import-sort (#22747) Bumps [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort) from 13.0.0 to 14.0.0. - [Changelog](https://github.com/lydell/eslint-plugin-simple-import-sort/blob/main/CHANGELOG.md) - [Commits](https://github.com/lydell/eslint-plugin-simple-import-sort/compare/v13.0.0...v14.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-simple-import-sort dependency-version: 14.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 12a217a6bc61..92cec76f167c 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.2.2", "eslint-plugin-regexp": "3.1.1", - "eslint-plugin-simple-import-sort": "13.0.0", + "eslint-plugin-simple-import-sort": "14.0.0", "eslint-plugin-unicorn": "72.0.0", "eslint-plugin-yml": "3.6.0", "fast-string-width": "3.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da1f9396eb42..07feedc776ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -387,8 +387,8 @@ importers: specifier: 3.1.1 version: 3.1.1(eslint@10.7.0) eslint-plugin-simple-import-sort: - specifier: 13.0.0 - version: 13.0.0(eslint@10.7.0) + specifier: 14.0.0 + version: 14.0.0(eslint@10.7.0) eslint-plugin-unicorn: specifier: 72.0.0 version: 72.0.0(eslint@10.7.0) @@ -3911,8 +3911,8 @@ packages: peerDependencies: eslint: '>=9.38.0' - eslint-plugin-simple-import-sort@13.0.0: - resolution: {integrity: sha512-McAc+/Nlvcg4byY/CABGH8kqnefWBj8s3JA2okEtz8ixbECQgU46p0HkTUKa4YS7wvgGceimlc34p1nXqbWqtA==} + eslint-plugin-simple-import-sort@14.0.0: + resolution: {integrity: sha512-NUJO0+XFCkk+o5EsAJruTgnfMEpeWrPWeJS15UVF60GgXmqz1BJ9/3hzlvG7lkL8Bubzos5cCLptThbFfPnSMQ==} peerDependencies: eslint: '>=5.0.0' @@ -9350,7 +9350,7 @@ snapshots: regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@13.0.0(eslint@10.7.0): + eslint-plugin-simple-import-sort@14.0.0(eslint@10.7.0): dependencies: eslint: 10.7.0 From d4a2bb4871f027eecb8575ad54742a5f71e0f79e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:45:06 +0800 Subject: [PATCH 0352/1190] chore(deps-dev): bump pnpm from 10.34.4 to 10.34.5 (#22748) * chore(deps-dev): bump magic-string from 0.30.21 to 1.0.0 Bumps [magic-string](https://github.com/Rich-Harris/magic-string) from 0.30.21 to 1.0.0. - [Release notes](https://github.com/Rich-Harris/magic-string/releases) - [Changelog](https://github.com/Rich-Harris/magic-string/blob/master/CHANGELOG.md) - [Commits](https://github.com/Rich-Harris/magic-string/compare/v0.30.21...v1.0.0) --- updated-dependencies: - dependency-name: magic-string dependency-version: 1.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: remove magic-string dependency and rebuild pnpm lock * chore: update packageManager to pnpm@10.34.5 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- package.json | 3 +-- pnpm-lock.yaml | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 92cec76f167c..33cda42fed25 100644 --- a/package.json +++ b/package.json @@ -188,7 +188,6 @@ "husky": "9.1.7", "js-beautify": "2.0.3", "lint-staged": "17.0.8", - "magic-string": "0.30.21", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", @@ -229,7 +228,7 @@ "engines": { "node": "^22.20.0 || ^24" }, - "packageManager": "pnpm@10.34.4+sha512.8768be55200ae3f2226b6527fcca2687e14bc4e5f12d7721a0f25da3df47915177058648db4177baf348120fa0ba2752d8d8d93f6beaf1fe64ae18da8de961af", + "packageManager": "pnpm@10.34.5+sha512.a4ee05f2f73658255bd6a89859c065a45c28a57daefae2c893a168ee2b73168c37b91e83e57ea67654ad03f03031746430e8bce38e362e042605fb8abc80192e", "pnpm": { "onlyBuiltDependencies": [ "bufferutil", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07feedc776ae..dcf6915ccd43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -413,9 +413,6 @@ importers: lint-staged: specifier: 17.0.8 version: 17.0.8 - magic-string: - specifier: 0.30.21 - version: 0.30.21 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -3684,7 +3681,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, integrity: sha512-OG2C++3YYIzflFjA9irnexBs9eUS6KUOViAssV4oSo4W8HAl86TV+DoFYhdSXVaFd4z5htsM3zzs64WJybH4cQ==, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: From dcebbcb68fccbc05550ad5b03c2a6d824884c390 Mon Sep 17 00:00:00 2001 From: YuruShiMaShiMaRin <71917424+xiaobailoves@users.noreply.github.com> Date: Sat, 18 Jul 2026 17:12:59 +0800 Subject: [PATCH 0353/1190] fix(route/comic-walker): add radar rule for episode detail pages (#22753) --- lib/routes/comic-walker/manga.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/routes/comic-walker/manga.ts b/lib/routes/comic-walker/manga.ts index 77bdf53afcdd..bbcbc9f4bec1 100644 --- a/lib/routes/comic-walker/manga.ts +++ b/lib/routes/comic-walker/manga.ts @@ -24,6 +24,10 @@ export const route: Route = { source: ['comic-walker.com/detail/:id'], target: '/manga/:id', }, + { + source: ['comic-walker.com/detail/:id/episodes/:episodeId'], + target: '/manga/:id', + }, ], name: '漫画详情', maintainers: ['xiaobailoves'], From 631a33d490733786782d988a401b8df55729cfea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=99=8E=E6=95=85=E6=B4=9E?= <55782476+TigerCubDen@users.noreply.github.com> Date: Sun, 19 Jul 2026 03:08:41 +0800 Subject: [PATCH 0354/1190] docs(route/bilibili): update cookie retrieval instructions for followings dynamic (#22754) --- lib/routes/bilibili/followings-dynamic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts index 566e5da1aed2..d4b966e91d1c 100644 --- a/lib/routes/bilibili/followings-dynamic.ts +++ b/lib/routes/bilibili/followings-dynamic.ts @@ -37,7 +37,7 @@ export const route: Route = { 1. 打开 [https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?uid=0&type=8](https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?uid=0&type=8) 2. 打开控制台,切换到 Network 面板,刷新 3. 点击 dynamic_new 请求,找到 Cookie - 4. 视频和专栏,UP 主粉丝及关注只要求 \`SESSDATA\` 字段,动态需复制整段 Cookie`, + 4. 复制整段 Cookie,删掉其中的 \`bili_ticket\` 和 \`bili_ticket_expires\` 字段来延长有效期`, }, ], requirePuppeteer: false, From 9d486c3bda61192852544de49d8a53ad1bd95187 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 20 Jul 2026 01:09:29 +0800 Subject: [PATCH 0355/1190] fix(route/picnob): fix image and video selector (#22757) --- lib/routes/picnob/user.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/routes/picnob/user.ts b/lib/routes/picnob/user.ts index 98c621713600..80525a864709 100644 --- a/lib/routes/picnob/user.ts +++ b/lib/routes/picnob/user.ts @@ -67,13 +67,18 @@ async function handler(ctx) { const coverLink = $item.find('.cover_link').attr('href'); const image = $item.find('.cover .cover_link img'); const alt = image.attr('alt') || ''; + const downloadUrl = new URL($item.find('.downbtn').attr('href')!, baseUrl); + downloadUrl.searchParams.delete('dl'); + const fullImage = new URL(image.attr('data-src')!); + fullImage.searchParams.set('o', Buffer.from(downloadUrl.href).toString('base64')); const sum = $item.find('.sum'); const title = sum.text().split('\n', 1)[0] || alt; const content = sum.html()?.replaceAll('\n', '
    ') || alt; + const isVideo = $item.find('.corner .icon_video, .corner .icon_tv').length; return { title, - description: `
    ${content}`, + description: `
    ${content}`, link: `${baseUrl}${coverLink}`, guid: coverLink?.split('/', 3)?.[2], pubDate: parseRelativeDate($item.find('.time .txt').text()), @@ -114,7 +119,7 @@ async function handler(ctx) { return $item.html() || ''; }) .join('') - : $('.view .video').html() || ''; + : $('.view video').prop('outerHTML') || ''; item.description = `${media}
    ${item.description}`; } From 39aa7af02c6569a621b93802bd906000192037cd Mon Sep 17 00:00:00 2001 From: Jiakai Gu Date: Mon, 20 Jul 2026 01:08:00 -0400 Subject: [PATCH 0356/1190] fix(route): harden njxzc, gxmzu and jou routes (#22435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(route): harden njxzc, gxmzu and jou routes - parse all observed date formats (ISO, slash, Chinese with optional time) instead of a fixed YYYY-MM-DD pattern - fall back to list data when an article page fails or lacks content, instead of crashing the whole feed on cheerio load(null) - skip content fetching for off-site links (e.g. WeChat posts) - only override title/pubDate when found on the detail page - resolve relative links and images against the article URL - deduplicate the list/detail scaffold into per-namespace utils - remove dead response.status checks, unused ctx params and noise comments; fix namespace URLs and antiCrawler flags * refactor(route): address review feedback for gxmzu, jou and njxzc - translate code comments to English (AGENTS.md rule 53) - select the date cell via $(selector, context) instead of .find(selector) to avoid unicorn/no-array-callback-reference false positives * refactor(route): simplify gxmzu, jou and njxzc per review - drop the unused type exports, unnecessary .first() calls and the unreachable no-content early returns - restore the original selector object order in gxmzu/jou call sites - gxmzu/jou: drop try-catch in resolveArticles (no failing article observed); keep the same-host guard for off-site list entries - njxzc: drop the same-host guard (list links are same-host /_redirect URLs); keep try-catch for redirects to intranet-only subdomains - gxmzu/lib: read the link text directly (library rows have no title attribute) * refactor(route): drop jou host guard and njxzc intranet handling - jou: remove the same-host guard — no off-site rows on the current first pages, which are all the route reads - njxzc: remove the intranet-notice branch and the try-catch — every article on the current first pages loads fine from off-campus; drop the now-stale route descriptions as well --------- Co-authored-by: real-jiakai --- lib/routes/gxmzu/ai.ts | 22 +++-- lib/routes/gxmzu/lib.ts | 64 ++++++--------- lib/routes/gxmzu/namespace.ts | 2 +- lib/routes/gxmzu/utils/index.ts | 139 +++++++++++++++++++------------- lib/routes/gxmzu/yjs.ts | 23 ++++-- lib/routes/jou/home.ts | 28 ++++--- lib/routes/jou/utils/index.ts | 126 +++++++++++++++++------------ lib/routes/jou/yz.ts | 28 ++++--- lib/routes/njxzc/home.ts | 48 ++++++----- lib/routes/njxzc/lib.ts | 88 +++++--------------- lib/routes/njxzc/namespace.ts | 3 +- lib/routes/njxzc/utils/index.ts | 106 ++++++++++++------------ 12 files changed, 348 insertions(+), 329 deletions(-) diff --git a/lib/routes/gxmzu/ai.ts b/lib/routes/gxmzu/ai.ts index f62b7bf43b91..43edc652cbe0 100644 --- a/lib/routes/gxmzu/ai.ts +++ b/lib/routes/gxmzu/ai.ts @@ -1,9 +1,11 @@ +import { load } from 'cheerio'; + import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; -import { getNoticeList } from './utils'; +import { parseNoticeList, resolveArticles } from './utils'; -const url = 'https://ai.gxmzu.edu.cn/index/tzgg.htm'; -const host = 'https://ai.gxmzu.edu.cn'; +const pageUrl = 'https://ai.gxmzu.edu.cn/index/tzgg.htm'; export const route: Route = { path: '/aitzgg', @@ -13,7 +15,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: true, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -29,8 +31,12 @@ export const route: Route = { url: 'ai.gxmzu.edu.cn/index/tzgg.htm', }; -async function handler(ctx) { - const out = await getNoticeList(ctx, url, host, 'a', '.timestyle55267', { +async function handler() { + const response = await ofetch(pageUrl); + const $ = load(response); + + const list = parseNoticeList($, pageUrl, 'table.winstyle55267 tr[height="20"]', '.timestyle55267'); + const items = await resolveArticles(list, pageUrl, { title: '.titlestyle55269', content: '#vsb_newscontent', date: '.timestyle55269', @@ -38,7 +44,7 @@ async function handler(ctx) { return { title: '广西民族大学人工智能学院 -- 通知公告', - link: url, - item: out, + link: pageUrl, + item: items, }; } diff --git a/lib/routes/gxmzu/lib.ts b/lib/routes/gxmzu/lib.ts index 4b53c925caa5..c46527f8abfc 100644 --- a/lib/routes/gxmzu/lib.ts +++ b/lib/routes/gxmzu/lib.ts @@ -1,13 +1,11 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; // 使用ofetch库代替got -import { parseDate } from '@/utils/parse-date'; -import timezone from '@/utils/timezone'; +import ofetch from '@/utils/ofetch'; -const url = 'https://library.gxmzu.edu.cn/news/news_list.jsp?urltype=tree.TreeTempUrl&wbtreeid=1010'; -const host = 'https://library.gxmzu.edu.cn'; +import { parsePubDate, resolveArticles } from './utils'; + +const pageUrl = 'https://library.gxmzu.edu.cn/news/news_list.jsp?urltype=tree.TreeTempUrl&wbtreeid=1010'; export const route: Route = { path: '/libzxxx', @@ -17,7 +15,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: true, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -31,50 +29,38 @@ export const route: Route = { maintainers: ['real-jiakai'], handler, url: 'library.gxmzu.edu.cn/news/news_list.jsp', + description: '部分消息发布于微信公众号等站外页面,此类消息仅输出标题与原文链接。', }; async function handler() { - const response = await ofetch(url); - if (!response) { - return; - } + const response = await ofetch(pageUrl); const $ = load(response); const list = $('#newslist ul li') .toArray() - .map((item) => { - item = $(item); + .map((el) => { + const $item = $(el); + const $link = $item.find('a'); + const href = $link.attr('href'); + if (!href) { + return null; + } return { - title: item.find('a').text(), - link: new URL(item.find('a').attr('href'), host).href, - pubDate: timezone(parseDate(item.find('span').text(), 'YYYY-MM-DD'), 8), + title: $link.text().trim(), + link: new URL(href, pageUrl).href, + pubDate: parsePubDate($item.find('span').text()), }; - }); - - const out = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - if (item.link && !item.link.startsWith('https://library.gxmzu.edu.cn/')) { - item.description = '该通知无法直接预览,请点击原文链接↑查看'; - return item; - } + }) + .filter((item) => item !== null); - const response = await ofetch(item.link); - if (!response || (response.status >= 300 && response.status < 400)) { - item.description = '该通知无法直接预览,请点击原文链接↑查看'; - } else { - const $ = load(response); - item.title = $('h2').text(); - item.description = $('.v_news_content').html(); - } - return item; - }) - ) - ); + const items = await resolveArticles(list, pageUrl, { + title: 'h2', + content: '.v_news_content', + }); return { title: '广西民族大学图书馆 -- 最新消息', - link: url, - item: out, + link: pageUrl, + item: items, }; } diff --git a/lib/routes/gxmzu/namespace.ts b/lib/routes/gxmzu/namespace.ts index 63abd273436b..6fa510453a9c 100644 --- a/lib/routes/gxmzu/namespace.ts +++ b/lib/routes/gxmzu/namespace.ts @@ -2,6 +2,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '广西民族大学', - url: 'ai.gxmzu.edu.cn', + url: 'www.gxmzu.edu.cn', lang: 'zh-CN', }; diff --git a/lib/routes/gxmzu/utils/index.ts b/lib/routes/gxmzu/utils/index.ts index 7caa1cb5ed49..beb5b78f19f4 100644 --- a/lib/routes/gxmzu/utils/index.ts +++ b/lib/routes/gxmzu/utils/index.ts @@ -1,72 +1,99 @@ -import { load } from 'cheerio'; +import { type CheerioAPI, load } from 'cheerio'; +import type { DataItem } from '@/types'; import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; // 使用ofetch库代替got +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -async function getNoticeList(ctx, url, host, titleSelector, dateSelector, contentSelector) { - const response = await ofetch(url); - if (!response) { - return []; +const FALLBACK_DESCRIPTION = '该通知无法直接预览,请点击原文链接↑查看'; + +interface NoticeItem extends DataItem { + link: string; +} + +interface DetailSelectors { + title: string; + content: string; + date?: string; +} + +// Date formats vary across sites: 2026-05-28, 2026/06/02 or 2026年05月28日 14:53, often prefixed with a label such as "发布日期:" +export function parsePubDate(text?: string): Date | undefined { + const match = text?.match(/(\d{4})[-/.年]\s*(\d{1,2})[-/.月]\s*(\d{1,2})/); + if (!match) { + return undefined; } - const $ = load(response); + const [, year, month, day] = match; + const time = text?.match(/(\d{1,2}:\d{2}(?::\d{2})?)/)?.[1]; + return timezone(parseDate(`${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}${time ? ` ${time}` : ''}`), 8); +} - const list = $('tr[height=20]') +export function parseNoticeList($: CheerioAPI, pageUrl: string, rowSelector: string, dateSelector: string): NoticeItem[] { + return $(rowSelector) .toArray() - .map((item) => { - item = $(item); + .map((el) => { + const $row = $(el); + const $link = $row.find('a'); + const href = $link.attr('href'); + if (!href) { + return null; + } return { - title: item.find(titleSelector).attr('title'), - link: new URL(item.find(titleSelector).attr('href'), host).href, - pubDate: timezone(parseDate(item.find(dateSelector).text().trim(), 'YYYY-MM-DD'), 8), + title: $link.attr('title') || $link.text().trim(), + link: new URL(href, pageUrl).href, + pubDate: parsePubDate($(dateSelector, $row).text()), }; - }); + }) + .filter((item) => item !== null); +} - const out = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - if (item.link.includes('.jsp')) { - // 特殊处理.jsp文件,直接显示消息而不尝试爬取 - return { - ...item, - description: '该通知无法直接预览,请点击原文链接↑查看', - }; - } - const response = await ofetch(item.link); - if (!response || (response.status >= 300 && response.status < 400)) { - item.description = '该通知无法直接预览,请点击原文链接↑查看'; - } else { - const $ = load(response); +async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promise { + const response = await ofetch(item.link); + const $ = load(response); + + // Pages whose body is an embedded PDF have no extractable content + if ($('script:contains("showVsbpdfIframe")').length > 0) { + return { ...item, description: FALLBACK_DESCRIPTION }; + } - item.title = $(contentSelector.title).text(); - const hasEmbeddedPDFScript = $('script:contains("showVsbpdfIframe")').length > 0; + const $content = $(selectors.content); - if (hasEmbeddedPDFScript) { - item.description = '该通知无法直接预览,请点击原文链接↑查看'; - } else { - const $content = load($(contentSelector.content).html()); - $content('a').each((_, el) => { - const a = $(el); - const href = a.attr('href'); - if (href && !href.startsWith('http')) { - a.attr('href', new URL(href, host).href); - } - }); - item.description = $content.html(); - } - const preDate = $(contentSelector.date) - .text() - .replaceAll(/年|月/g, '-') - .replaceAll('日', ''); - item.pubDate = timezone(parseDate(preDate), 8); - } - return item; - }) - ) - ); + $content.find('a').each((_, el) => { + const $a = $(el); + const href = $a.attr('href'); + if (href) { + $a.attr('href', new URL(href, item.link).href); + } + }); + $content.find('img').each((_, el) => { + const $img = $(el); + const src = $img.attr('src'); + if (src) { + $img.attr('src', new URL(src, item.link).href); + } + }); - return out; + const title = $(selectors.title).text().trim(); + const pubDate = selectors.date ? parsePubDate($(selectors.date).text()) : undefined; + + return { + ...item, + title: title || item.title, + pubDate: pubDate ?? item.pubDate, + description: $content.html() ?? item.description, + }; } -export { getNoticeList }; +// Content of off-site links (e.g. WeChat posts or sibling-subdomain sites) is not fetched +export function resolveArticles(list: NoticeItem[], pageUrl: string, selectors: DetailSelectors): Promise { + const pageHost = new URL(pageUrl).host; + return Promise.all( + list.map((item) => { + if (new URL(item.link).host !== pageHost) { + return { ...item, description: FALLBACK_DESCRIPTION }; + } + return cache.tryGet(item.link, () => fetchArticle(item, selectors)) as Promise; + }) + ); +} diff --git a/lib/routes/gxmzu/yjs.ts b/lib/routes/gxmzu/yjs.ts index 1ca62e1dc90f..653ae8b32095 100644 --- a/lib/routes/gxmzu/yjs.ts +++ b/lib/routes/gxmzu/yjs.ts @@ -1,9 +1,11 @@ +import { load } from 'cheerio'; + import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; -import { getNoticeList } from './utils'; +import { parseNoticeList, resolveArticles } from './utils'; -const url = 'https://yjs.gxmzu.edu.cn/tzgg/zsgg.htm'; -const host = 'https://yjs.gxmzu.edu.cn'; +const pageUrl = 'https://yjs.gxmzu.edu.cn/tzgg/zsgg.htm'; export const route: Route = { path: '/yjszsgg', @@ -13,7 +15,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: true, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -29,8 +31,13 @@ export const route: Route = { url: 'yjs.gxmzu.edu.cn/tzgg/zsgg.htm', }; -async function handler(ctx) { - const out = await getNoticeList(ctx, url, host, 'a', '.timestyle55267', { +async function handler() { + const response = await ofetch(pageUrl); + const $ = load(response); + + // The graduate school shares the same Boda CMS template with the AI college, so the list and article style IDs are identical + const list = parseNoticeList($, pageUrl, 'table.winstyle55267 tr[height="20"]', '.timestyle55267'); + const items = await resolveArticles(list, pageUrl, { title: '.titlestyle55269', content: '#vsb_newscontent', date: '.timestyle55269', @@ -38,7 +45,7 @@ async function handler(ctx) { return { title: '广西民族大学研究生院 -- 招生公告', - link: url, - item: out, + link: pageUrl, + item: items, }; } diff --git a/lib/routes/jou/home.ts b/lib/routes/jou/home.ts index 7c9d147eba62..77ce1ff9a97a 100644 --- a/lib/routes/jou/home.ts +++ b/lib/routes/jou/home.ts @@ -1,9 +1,11 @@ +import { load } from 'cheerio'; + import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; -import { getItems } from './utils'; +import { parseNoticeList, resolveArticles } from './utils'; -const url = 'https://www.jou.edu.cn/index/tzgg.htm'; -const host = 'https://www.jou.edu.cn'; +const pageUrl = 'https://www.jou.edu.cn/index/tzgg.htm'; export const route: Route = { path: '/tzgg', @@ -29,16 +31,20 @@ export const route: Route = { url: 'www.jou.edu.cn/index/tzgg.htm', }; -async function handler(ctx) { - const out = await getItems(ctx, url, host, 'winstyle106390', 'timestyle106390', 'titlestyle106402', 'timestyle106402'); +async function handler() { + const response = await ofetch(pageUrl); + const $ = load(response); + + const list = parseNoticeList($, pageUrl, 'table.winstyle106390 tr[height="20"]', '.timestyle106390'); + const items = await resolveArticles(list, { + title: '.titlestyle106402', + content: '.v_news_content', + date: '.timestyle106402', + }); - // 生成RSS源 return { - // 项目标题 title: '江苏海洋大学 -- 通知公告', - // 项目链接 - link: url, - // items的内容 - item: out, + link: pageUrl, + item: items, }; } diff --git a/lib/routes/jou/utils/index.ts b/lib/routes/jou/utils/index.ts index 352071792253..edfd2a7fbcbf 100644 --- a/lib/routes/jou/utils/index.ts +++ b/lib/routes/jou/utils/index.ts @@ -1,70 +1,90 @@ -import { load } from 'cheerio'; +import { type CheerioAPI, load } from 'cheerio'; +import type { DataItem } from '@/types'; import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; // 使用ofetch库 +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -async function getItems(ctx, url, host, tableClass, timeStyleClass1, titleStyleClass, timeStyleClass2) { - const response = await ofetch(url); - if (!response) { - return []; +const FALLBACK_DESCRIPTION = '该通知无法直接预览,请点击原文链接↑查看'; + +interface NoticeItem extends DataItem { + link: string; +} + +interface DetailSelectors { + title: string; + content: string; + date?: string; +} + +// Date formats vary across sites: 2026-03-30, 2026/05/06 or 2026年03月30日 14:37, often prefixed with a label such as "发布时间:" +export function parsePubDate(text?: string): Date | undefined { + const match = text?.match(/(\d{4})[-/.年]\s*(\d{1,2})[-/.月]\s*(\d{1,2})/); + if (!match) { + return undefined; } - const $ = load(response); + const [, year, month, day] = match; + const time = text?.match(/(\d{1,2}:\d{2}(?::\d{2})?)/)?.[1]; + return timezone(parseDate(`${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}${time ? ` ${time}` : ''}`), 8); +} - const list = $(`table.${tableClass} > tbody > tr[height=20]`) +export function parseNoticeList($: CheerioAPI, pageUrl: string, rowSelector: string, dateSelector: string): NoticeItem[] { + return $(rowSelector) .toArray() - .map((item) => { - const currentItem = $(item); - const item1 = currentItem.find('td:eq(1)'); - const item2 = currentItem.find('td:eq(2)'); - const link = new URL(item1.find('a').attr('href'), host).href; - + .map((el) => { + const $row = $(el); + const $link = $row.find('a'); + const href = $link.attr('href'); + if (!href) { + return null; + } return { - title: item1.find('a').attr('title'), - link, - pubDate: timezone(parseDate(item2.find(`.${timeStyleClass1}`).text(), 'YYYY-MM-DD'), 8), + title: $link.attr('title') || $link.text().trim(), + link: new URL(href, pageUrl).href, + pubDate: parsePubDate($(dateSelector, $row).text()), }; - }); + }) + .filter((item) => item !== null); +} + +async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promise { + const response = await ofetch(item.link); + const $ = load(response); - const out = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - const response = await ofetch(item.link); - if (!response || (response.status >= 300 && response.status < 400)) { - // 响应为空或状态码表明发生了重定向 - return { - ...item, - description: '该通知无法直接预览,请点击原文链接↑查看', - }; - } - const $ = load(response); + // Pages whose body is an embedded PDF have no extractable content + if ($('script:contains("showVsbpdfIframe")').length > 0) { + return { ...item, description: FALLBACK_DESCRIPTION }; + } - item.title = $(`.${titleStyleClass}`).text(); - const hasEmbeddedPDFScript = $('script:contains("showVsbpdfIframe")').length > 0; + const $content = $(selectors.content); - if (hasEmbeddedPDFScript) { - item.description = '该通知无法直接预览,请点击原文链接↑查看'; - } else { - const contentHtml = $('.v_news_content').html(); - const $content = load(contentHtml); - $content('a').each((_, el) => { - const a = $(el); - const href = a.attr('href'); - if (href && !href.startsWith('http')) { - a.attr('href', new URL(href, host).href); - } - }); - item.description = $content.html(); - } - item.pubDate = timezone(parseDate($(`.${timeStyleClass2}`).text().replace('发布时间:', '')), 8); + $content.find('a').each((_, el) => { + const $a = $(el); + const href = $a.attr('href'); + if (href) { + $a.attr('href', new URL(href, item.link).href); + } + }); + $content.find('img').each((_, el) => { + const $img = $(el); + const src = $img.attr('src'); + if (src) { + $img.attr('src', new URL(src, item.link).href); + } + }); - return item; - }) - ) - ); + const title = $(selectors.title).text().trim(); + const pubDate = selectors.date ? parsePubDate($(selectors.date).text()) : undefined; - return out; + return { + ...item, + title: title || item.title, + pubDate: pubDate ?? item.pubDate, + description: $content.html() ?? item.description, + }; } -export { getItems }; +export function resolveArticles(list: NoticeItem[], selectors: DetailSelectors): Promise { + return Promise.all(list.map((item) => cache.tryGet(item.link, () => fetchArticle(item, selectors)) as Promise)); +} diff --git a/lib/routes/jou/yz.ts b/lib/routes/jou/yz.ts index 031c0c03112a..dfc602e4fea5 100644 --- a/lib/routes/jou/yz.ts +++ b/lib/routes/jou/yz.ts @@ -1,9 +1,11 @@ +import { load } from 'cheerio'; + import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; -import { getItems } from './utils'; +import { parseNoticeList, resolveArticles } from './utils'; -const url = 'https://yz.jou.edu.cn/index/zxgg.htm'; -const host = 'https://yz.jou.edu.cn'; +const pageUrl = 'https://yz.jou.edu.cn/index/zxgg.htm'; export const route: Route = { path: '/yztzgg', @@ -29,16 +31,20 @@ export const route: Route = { url: 'yz.jou.edu.cn/index/zxgg.htm', }; -async function handler(ctx) { - const out = await getItems(ctx, url, host, 'winstyle207638', 'timestyle207638', 'titlestyle207543', 'timestyle207543'); +async function handler() { + const response = await ofetch(pageUrl); + const $ = load(response); + + const list = parseNoticeList($, pageUrl, 'table.winstyle207638 tr[height="20"]', '.timestyle207638'); + const items = await resolveArticles(list, { + title: '.titlestyle207543', + content: '.v_news_content', + date: '.timestyle207543', + }); - // 生成RSS源 return { - // 项目标题 title: '江苏海洋大学 -- 研招通知公告', - // 项目链接 - link: url, - // items的内容 - item: out, + link: pageUrl, + item: items, }; } diff --git a/lib/routes/njxzc/home.ts b/lib/routes/njxzc/home.ts index 5975c6f22c39..ec1f0ffd3bda 100644 --- a/lib/routes/njxzc/home.ts +++ b/lib/routes/njxzc/home.ts @@ -1,9 +1,11 @@ +import { load } from 'cheerio'; + import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; -import { getNoticeList } from './utils'; +import { parsePubDate, resolveArticles } from './utils'; -const url = 'https://www.njxzc.edu.cn/89/list.htm'; -const host = 'https://www.njxzc.edu.cn'; +const pageUrl = 'https://www.njxzc.edu.cn/89/list.htm'; export const route: Route = { path: '/tzgg', @@ -13,7 +15,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: true, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -29,24 +31,30 @@ export const route: Route = { url: 'www.njxzc.edu.cn/89/list.htm', }; -async function handler(ctx) { - const out = await getNoticeList( - ctx, - url, - host, - 'a', - '.news_meta', - { - title: '.arti_title', - content: '.wp_articlecontent', - date: '.arti_update', - }, - '.news_list .news' - ); +async function handler() { + const response = await ofetch(pageUrl); + const $ = load(response); + + const list = $('.news_list .news') + .toArray() + .map((el) => { + const $item = $(el); + const $link = $item.find('a'); + const href = $link.attr('href'); + if (!href) { + return null; + } + return { + title: $link.attr('title') || $link.text().trim(), + link: new URL(href, pageUrl).href, + pubDate: parsePubDate($item.find('.news_meta').text()), + }; + }) + .filter((item) => item !== null); return { title: '南京晓庄学院 -- 通知公告', - link: url, - item: out, + link: pageUrl, + item: await resolveArticles(list), }; } diff --git a/lib/routes/njxzc/lib.ts b/lib/routes/njxzc/lib.ts index 38f0508d7710..04c2b45e79f1 100644 --- a/lib/routes/njxzc/lib.ts +++ b/lib/routes/njxzc/lib.ts @@ -1,13 +1,11 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; -import { parseDate } from '@/utils/parse-date'; -import timezone from '@/utils/timezone'; -const url = 'https://lib.njxzc.edu.cn/pxyhd/list.htm'; -const host = 'https://lib.njxzc.edu.cn'; +import { parsePubDate, resolveArticles } from './utils'; + +const pageUrl = 'https://lib.njxzc.edu.cn/pxyhd/list.htm'; export const route: Route = { path: '/libtzgg', @@ -17,7 +15,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: true, + antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, @@ -34,76 +32,30 @@ export const route: Route = { }; async function handler() { - const response = await ofetch(url); - if (!response) { - return { - title: '南京晓庄学院 -- 图书馆通知公告', - link: url, - item: [], - }; - } + const response = await ofetch(pageUrl); const $ = load(response); const list = $('a.btt-2') .toArray() - .map((item) => { - const $item = $(item); - const href = $item.attr('href') || ''; - const link = href.startsWith('http') ? href : new URL(href, host).href; - const day = $item.find('.tm-1').text().trim(); - const yearMonth = $item.find('.tm-2').text().trim(); - const dateStr = `${yearMonth}-${day}`; + .map((el) => { + const $link = $(el); + const href = $link.attr('href'); + if (!href) { + return null; + } + const day = $link.find('.tm-1').text().trim(); + const yearMonth = $link.find('.tm-2').text().trim(); return { - title: $item.find('.btt-4').text().trim(), - link, - pubDate: timezone(parseDate(dateStr, 'YYYY-MM-DD'), 8), + title: $link.find('.btt-4').text().trim(), + link: new URL(href, pageUrl).href, + pubDate: parsePubDate(`${yearMonth}-${day}`), }; - }); - - const out = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - const response = await ofetch(item.link); - const $ = load(response); - - if ($('.wp_error_msg').length > 0) { - item.description = '您当前ip并非校内地址,该信息仅允许校内地址访问'; - } else { - const $content = $('.wp_articlecontent'); - // Convert wp_pdf_player iframes to download links - $content.find('.wp_pdf_player').each((_, el) => { - const $iframe = $(el); - const pdfSrc = $iframe.attr('pdfsrc') || ''; - const pdfUrl = pdfSrc.startsWith('http') ? pdfSrc : new URL(pdfSrc, host).href; - $iframe.replaceWith(`

    附件下载

    `); - }); - // Fix relative URLs - $content.find('a').each((_, el) => { - const $a = $(el); - const href = $a.attr('href'); - if (href && !href.startsWith('http')) { - $a.attr('href', new URL(href, host).href); - } - }); - item.description = $content.html() || ''; - const title = $('.arti_title').text().trim(); - if (title) { - item.title = title; - } - const dateText = $('.arti_update').text().replace('发布时间:', '').trim(); - if (dateText) { - item.pubDate = timezone(parseDate(dateText, 'YYYY-MM-DD'), 8); - } - } - - return item; - }) - ) - ); + }) + .filter((item) => item !== null); return { title: '南京晓庄学院 -- 图书馆通知公告', - link: url, - item: out, + link: pageUrl, + item: await resolveArticles(list), }; } diff --git a/lib/routes/njxzc/namespace.ts b/lib/routes/njxzc/namespace.ts index 98e099e9e9b0..ad57813d949a 100644 --- a/lib/routes/njxzc/namespace.ts +++ b/lib/routes/njxzc/namespace.ts @@ -2,6 +2,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '南京晓庄学院', - url: 'lib.njxzc.edu.cn', + url: 'www.njxzc.edu.cn', + description: '部分文章仅限校内 IP 访问,此类文章仅输出标题与原文链接', lang: 'zh-CN', }; diff --git a/lib/routes/njxzc/utils/index.ts b/lib/routes/njxzc/utils/index.ts index 20875a3be5cc..da92a4f7a008 100644 --- a/lib/routes/njxzc/utils/index.ts +++ b/lib/routes/njxzc/utils/index.ts @@ -1,67 +1,67 @@ import { load } from 'cheerio'; +import type { DataItem } from '@/types'; import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; // 使用默认导出的方式导入ofetch +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -async function getNoticeList(ctx, url, host, titleSelector, dateSelector, contentSelector, listSelector) { - const response = await ofetch(url); - if (!response) { - return []; +interface NoticeItem extends DataItem { + link: string; +} + +// Date formats vary across the site: 2026-07-01, 2026/07/01 or 2026年07月01日 14:53, often prefixed with a label such as "发布时间:" +export function parsePubDate(text?: string): Date | undefined { + const match = text?.match(/(\d{4})[-/.年]\s*(\d{1,2})[-/.月]\s*(\d{1,2})/); + if (!match) { + return undefined; } - const $ = load(response); + const [, year, month, day] = match; + const time = text?.match(/(\d{1,2}:\d{2}(?::\d{2})?)/)?.[1]; + return timezone(parseDate(`${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}${time ? ` ${time}` : ''}`), 8); +} - const list = $(listSelector) - .toArray() - .map((item) => { - item = $(item); - const href = item.find(titleSelector).attr('href') || ''; - const link = href.startsWith('http') ? href : new URL(href, host).href; - return { - title: item.find(titleSelector).attr('title'), - link, - pubDate: timezone(parseDate(item.find(dateSelector).text(), 'YYYY-MM-DD'), 8), - }; - }); +async function fetchArticle(item: NoticeItem): Promise { + const response = await ofetch(item.link); + const $ = load(response); - const out = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - const response = await ofetch(item.link); - const $ = load(response); + const $content = $('.wp_articlecontent'); - if ($('.wp_error_msg').length > 0) { - item.description = '您当前ip并非校内地址,该信息仅允许校内地址访问'; - } else { - const $content = $(contentSelector.content); - // Convert wp_pdf_player iframes to download links - $content.find('.wp_pdf_player').each((_, el) => { - const $iframe = $(el); - const pdfSrc = $iframe.attr('pdfsrc') || ''; - const pdfUrl = pdfSrc.startsWith('http') ? pdfSrc : new URL(pdfSrc, host).href; - $iframe.replaceWith(`

    附件下载

    `); - }); - // Fix relative URLs - $content.find('a').each((_, el) => { - const $a = $(el); - const href = $a.attr('href'); - if (href && !href.startsWith('http')) { - $a.attr('href', new URL(href, host).href); - } - }); - item.description = $content.html() || ''; - item.title = $(contentSelector.title).text(); - const dateText = $(contentSelector.date).text().replace('编辑:', '').replace('发布日期:', '').replace('发布时间:', ''); - item.pubDate = timezone(parseDate(dateText, 'YYYY-MM-DD'), 8); - } + $content.find('.wp_pdf_player').each((_, el) => { + const $player = $(el); + const pdfSrc = $player.attr('pdfsrc'); + if (pdfSrc) { + $player.replaceWith(`

    附件下载

    `); + } else { + $player.remove(); + } + }); + $content.find('a').each((_, el) => { + const $a = $(el); + const href = $a.attr('href'); + if (href) { + $a.attr('href', new URL(href, item.link).href); + } + }); + $content.find('img').each((_, el) => { + const $img = $(el); + const src = $img.attr('src'); + if (src) { + $img.attr('src', new URL(src, item.link).href); + } + }); - return item; - }) - ) - ); + const title = $('.arti_title').text().trim(); + const pubDate = parsePubDate($('.arti_update').text()); - return out; + return { + ...item, + title: title || item.title, + pubDate: pubDate ?? item.pubDate, + description: $content.html() ?? item.description, + }; } -export { getNoticeList }; +export function resolveArticles(list: NoticeItem[]): Promise { + return Promise.all(list.map((item) => cache.tryGet(item.link, () => fetchArticle(item)) as Promise)); +} From 2683b4890757d46ae82755a7abd4b9e73c418df1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 08:14:12 +0000 Subject: [PATCH 0357/1190] chore(deps-dev): bump tsdown from 0.22.9 to 0.22.12 (#22762) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.9 to 0.22.12. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.9...v0.22.12) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.12 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 257 +++++++++++++++++++++++++------------------------ 2 files changed, 133 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 33cda42fed25..b7f43488d810 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.9", + "tsdown": "0.22.12", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcf6915ccd43..09cff1076421 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -450,8 +450,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.9 - version: 0.22.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) + specifier: 0.22.12 + version: 0.22.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -3027,130 +3027,130 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.6.5': - resolution: {integrity: sha512-uR1OCnftxC89GP6ZLPbaAXU9wdycmXt5BUQAOaDUmU/b38WJefse4RcL793rsOw1rkb8UC3UqP9F8hQ0lDB5xg==} + '@yuku-codegen/binding-darwin-arm64@0.7.0': + resolution: {integrity: sha512-RLfjSuJUolQJ04zYq3kM2vSUk9BkFFSOhmOWARb7Pc7Gnf0vMLfj/fepnn9IdsyE2FsJjoCJPKM5bBze4ld5pQ==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.6.5': - resolution: {integrity: sha512-b5m/NymPwAV8OYmWPK0GwVeXw/D7EMIr1GkgL9fW/JCFDvkSkBTz8cbGl5Jkoxr+bamOZy4C1ySngpU///4CFQ==} + '@yuku-codegen/binding-darwin-x64@0.7.0': + resolution: {integrity: sha512-OI9z6U69j2TvaWgDzheUNlMPSrlNQs8PD3isfyuGxQVbO8Dqi3F7PRT1JnG7pkId+IKvmEu+40sjXWDHRbtlMA==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.6.5': - resolution: {integrity: sha512-wLTe/QeBF37qb4bR++t/jLP3LQ7oS76lsZigk7J10IR2GwD4w8GJqhIm1NsAwho6OjWDNnfxkkw/Oadf/jex4Q==} + '@yuku-codegen/binding-freebsd-x64@0.7.0': + resolution: {integrity: sha512-nhoH3yXu2e6itB1Hbpj1+kZJ6yTJtsXBN5dWYm20TKvs1Iq79di6hdCKBlHE12RHFW39aQKcnn+vlFF1J1RGsw==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.6.5': - resolution: {integrity: sha512-Clah7LByDMBFkHKeRsUrqoftiyocoYaAmDc7aM14S9qghvKPnbTAtvrG8QOLMkWviWS+rQ94YWFGqzk1cRxAnA==} + '@yuku-codegen/binding-linux-arm-gnu@0.7.0': + resolution: {integrity: sha512-bgmapvrk/f/1bOSl+XA9KTLKb7vmxuXhqgCchlhTvgNOWnHB4rPLfzu0TkQU5CjVFzZQmHoEmhObf9DKUzcDMA==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.6.5': - resolution: {integrity: sha512-3ghZT7Dtp5tzIyfSFWcLFxtwnXjrrqLhO+MTuw3Am03K9Coo2NQICwwA9DN2HBrb8KXES1U43an1rNgzRw9xog==} + '@yuku-codegen/binding-linux-arm-musl@0.7.0': + resolution: {integrity: sha512-HpVSId+bxtqSkIsbBUP78J0j4ZUnCvqj14WGmAjKDP25oaQ0SZuFtVIZJmTisuKVSM4AIx1CVBYlJ2CCIm/vsQ==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.6.5': - resolution: {integrity: sha512-9zT+uVEtVJkvm0Ba5BkUgBXNqw3kcolV9RAf0kSgIe44bi5Lp6MPqCRm9JrWnJTZOySzPII3oRNRl9dtBlOdLg==} + '@yuku-codegen/binding-linux-arm64-gnu@0.7.0': + resolution: {integrity: sha512-oL8OGWo99U0arObIl5UKov3ZvsoNGWUMWRJrlXP99P4LpVz+gZC9KwfYCzxlhGnNk9rpp/fcTUUHYHbTskuZwQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.6.5': - resolution: {integrity: sha512-Uyi/vT++0gyVsyan3XvEii8AWbc+yFJ3M0GEloup7eBx1j4FtK7oq23F6mbQD1gNaozGKkN74fCJpIxF176hZA==} + '@yuku-codegen/binding-linux-arm64-musl@0.7.0': + resolution: {integrity: sha512-ULsFt9PnI5vBMCG5pGir1YQtJ03o0Sb5SsRTDYRSeKaVaxdog+kA9Guwnk8q8OMPFsI3s40Fhu/+45cQXHSZ8Q==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.6.5': - resolution: {integrity: sha512-JZzAbPDh5eQ2GRAAbo7cKcFabPQ9UEAQglk1BE0psoWUDt1wktp4ZX9ZJgDgF9EbSH3UUACo0lIkWUtSjRyRoQ==} + '@yuku-codegen/binding-linux-x64-gnu@0.7.0': + resolution: {integrity: sha512-l1TP6G39gnF4eiHEApjViyA12n+YHT18A0y3FDUy1jF5hkZt2RWlBNrU4HPhzTd6kQlozJEWyjhVNja2J3/eBw==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.6.5': - resolution: {integrity: sha512-Dmh6Qhoo1UNM10eDLs/DND8E3cLfQQboFfZgnnNNIJ2RQ/G0GXGh7Kzff1QmglUNuRami42NkTrsF9VrkCttZg==} + '@yuku-codegen/binding-linux-x64-musl@0.7.0': + resolution: {integrity: sha512-h8GZdSNSaBWySA4p8eYFWkH8OWdqA4peOAFeWs+DltJo8r9ZXSvVB0XBypMxkLwA0qLDqwtWoVssg2LK1zJKdw==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.6.5': - resolution: {integrity: sha512-UAq2wLFT0mRN2rzJfZjmaqVMJD9kYywARc6Wk8Ggnm4p8yJYigNxMkJ0gt3bpd0MOnvre4LFEDyAMr4esRubuQ==} + '@yuku-codegen/binding-win32-arm64@0.7.0': + resolution: {integrity: sha512-hVkxv4UTPXex7q5ldvqelSMvvYc6bCAYlDSMMg3wmttHZF9yQzPb4yYw69eWvR4coEMFaUv63ADJNjiGjNxkYg==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.6.5': - resolution: {integrity: sha512-hlEo+UIMHaEoLHe8woLr9eI6fz+8LRwdRrid/iegVU1N+53zkh4WMkI8N7e4vUNKKMVN2kJo6Z73J+JfskHO1g==} + '@yuku-codegen/binding-win32-x64@0.7.0': + resolution: {integrity: sha512-g3YQpqvsTbDHjjBDnNGhaO4ejN+m2GpsWc50dcGPR/RUx+yt++uDEXKSEyiAaCpEM2p5PuDIW/YEjXx0oVdsoQ==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.6.5': - resolution: {integrity: sha512-QVdaZzj9T3KdaprM8VYpiYIFJrcJB347P2U3bg683nPQaDNmX733CFtCqpkc1KHccFf199EXx4OazkJlnHvImA==} + '@yuku-parser/binding-darwin-arm64@0.7.0': + resolution: {integrity: sha512-LoZ945MxFzc6+ltMenaFtXhJ4rdj11j1IwkRWLR7WPim2jdXUURTfWhAm7VzQDtSCHtnDz8PAH55eIHmNilTlA==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.6.5': - resolution: {integrity: sha512-9omiEXwzJo3hsJiaohFek44wKRmnNy8o8acf8PXJdVo19I9O5eY7c3Nhca1DRTozoLqIJEI68CH8OoR2/Dv7ww==} + '@yuku-parser/binding-darwin-x64@0.7.0': + resolution: {integrity: sha512-HVS2bgNGqCl5oU5wP16tZUMmXAO1avgxp/uzDOJcNqA7WOlV1PqTWD3P/1GXaMAijIZNu3VLSXkU1dovwwPpVA==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.6.5': - resolution: {integrity: sha512-0BZ14CHc8H3EHmlAMhxTDVMYijsT1nNZUhl6JTm/xzl+Gaun/vahgdaJaFESuweLeZ1WnwVfPVPvjsdfnbTN8g==} + '@yuku-parser/binding-freebsd-x64@0.7.0': + resolution: {integrity: sha512-nVZgLmiuEdkRb8oJsXBoQphfZwsMTcatSEB5t1QL9aH92/hx2TxAGayZy/g326l5rGVMmieCHc9FYxu/MnVFHQ==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.6.5': - resolution: {integrity: sha512-VidTdzelGosQDkVkU4X/9qrILPT5HunzL5shW9jrq860naAEjGS/41a4s3J5KBRsKRHscwOsbtSzUev1TMdgfg==} + '@yuku-parser/binding-linux-arm-gnu@0.7.0': + resolution: {integrity: sha512-rIq85RCqwMQFtQSBIvUbmEAkNw0pu89PsrikSt+uVMCxjN1ZjbekTWw4hi0NVax8kAb5t2Xgs6kQrFoapyQ3Xg==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.6.5': - resolution: {integrity: sha512-Xes/SwXQiPFPqLMhnwjRv0s6mlm6V7+jpw/kmCMkal9Q0UfS4hVpFo2T5NNIKNLCj5qAZ+2EPtgDBz0S1lH4xg==} + '@yuku-parser/binding-linux-arm-musl@0.7.0': + resolution: {integrity: sha512-BNoI0mP8o3iV6dJEgfJq1U6cAMn7iSYe3gSYkN05DL1FyOU6ni2NwcDScsV6RBBQHj4V1s5123F85JLPhhXfqA==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.6.5': - resolution: {integrity: sha512-MaFn3Vh+kBETYXtPYBaCQUMsUk5SqSatM+mp9Vz7COuCKXim7cG8kGcUpqq93wegLEkkEnv8pyiDFFx/gmNOiQ==} + '@yuku-parser/binding-linux-arm64-gnu@0.7.0': + resolution: {integrity: sha512-YrFNrrc0bq5B+cV2EuxDU4VPhuH0RHkV0M1rne8UTxPbqjPmarWxLcl1JEeyaXcL0856kwnaV728GMeB1o/Gdg==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.6.5': - resolution: {integrity: sha512-s7GAjJ7kzmnEBVAq5c5B+h/DCOSZG7WqTNpFRYUVGQU+D8wf1gMnfDQ5v8zFdByhpHiW+HNerxI8sml5dl47Rw==} + '@yuku-parser/binding-linux-arm64-musl@0.7.0': + resolution: {integrity: sha512-WaDi6BsjZ/vrO7EgX36C4sLN9fUPZd/vM0Nh+82uOjygCxgFtiRf5NsFxdaLSzqMi+sbGsg+hWM/De4vkana3A==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.6.5': - resolution: {integrity: sha512-5/+mLrZdCtGoKOw/zNJC0+fSy4aKI97JvPm5rr8DXui/l3+BrvU5g7czp1DVHN67I7VzTLizu2n2y5cz2CjzEg==} + '@yuku-parser/binding-linux-x64-gnu@0.7.0': + resolution: {integrity: sha512-gxlbaqglGr7ir9UZLF0945JKTImI9MFDZiny57mRiqktCTDVQPnb5Lmj0okKJ6w0nBX1NzwviMX6v4i+rwLSWw==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.6.5': - resolution: {integrity: sha512-Svj4h/WZQ1VdAxaFJrakVi85IzUJPYWcNw15gNPoeNg+/SGGTTkTeUzODiMRY/8ioJ/k3+/K7zQwASIEBF7L3Q==} + '@yuku-parser/binding-linux-x64-musl@0.7.0': + resolution: {integrity: sha512-n7pCgW5iNoaKEpt8K3UTkg7ACX87MueWkJQD7cQSOgxyu/Qx0BCdwi0iOs60Gjj88wyKwqkYIBPMN3wKE5P7Yw==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.6.5': - resolution: {integrity: sha512-N96G8zoKihVwhMaH+kLp8MFIA1i3+dRaBO4KYK0otTPVdX+3uS2wpUY5vDLj4JbkKARuQA/DUmfgjleoXL6ugA==} + '@yuku-parser/binding-win32-arm64@0.7.0': + resolution: {integrity: sha512-kyuC7W1mGIMbJp6t86hIhDjnptFxZiTaOrbHhYkCpfsXPF6pszWNK03DUMc//Udnw3Af3d5w93CRJlRNpCb5eg==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.6.5': - resolution: {integrity: sha512-RP7rz120SodZI/vUc1H4uzIMEZrxeG//d11qWUI5inMbU4YSnufJAScwjV+qQHxD3FAwoueNxsPjrVxagwAymg==} + '@yuku-parser/binding-win32-x64@0.7.0': + resolution: {integrity: sha512-xEm5rwtETud7iNbxSocACgXzPrv2x4vkk339BtAqZAKoCS+edaO767EKAGcUO3xo8STBgCud1cAA5oIyzN8APA==} cpu: [x64] os: [win32] - '@yuku-toolchain/types@0.5.43': - resolution: {integrity: sha512-kSpvPntnXw5+lYjO71ffBEnQ5ycQ74KGIYknh0TS4xeyCuBkOqxyJumxZkMhLBBUCLjDAbx2+Icnr3Zh4ftjpQ==} + '@yuku-toolchain/types@0.7.0': + resolution: {integrity: sha512-kKleXJmXcZnJ5LUDTeYvK350SB+BXdpoHUwT78GGyOXOhCZ0tWf+seDPAvVnCrjoJhf+FhqtR5HRUfWW+yILQw==} '@zone-eu/mailsplit@5.4.14': resolution: {integrity: sha512-rz0FQOhN3Vq1XrSeSSa9+dPcaFbBxmQPjiZm6zS9oxdVHV7rOWIAYX3yP2YAUf0qBncY8CI+NogzPCmMVrMXcw==} @@ -3681,7 +3681,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, integrity: sha512-OG2C++3YYIzflFjA9irnexBs9eUS6KUOViAssV4oSo4W8HAl86TV+DoFYhdSXVaFd4z5htsM3zzs64WJybH4cQ==, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -5373,8 +5373,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.19: - resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + postcss@8.5.20: + resolution: {integrity: sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -5594,8 +5594,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.11: - resolution: {integrity: sha512-DnBl4USSTV/h/sgy//QjCLHVo2JypE/52P5QugGeYaEqZmjBz/FYESOld0MhyIhul2U3Vsh41K63UWGHhJU/qQ==} + rolldown-plugin-dts@0.27.12: + resolution: {integrity: sha512-DJg5ELVEdLAVhirvtak7GS4nbNvBzLNAtYL1M8hLghDURBFKU2MwEH6ncHibYs6khq1NaRQ97iFMiHvzw+WoSw==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@typescript/native-preview': '*' @@ -5990,14 +5990,14 @@ packages: typescript: optional: true - tsdown@0.22.9: - resolution: {integrity: sha512-/0QEjQEhOU1t1YxOIAGzFN7bIssd8P0pBpkOmNLCQi3c5UtrcMF5bvq3f30xHJNW9QCA9aUNcNAorMr2CTd6Lg==} + tsdown@0.22.12: + resolution: {integrity: sha512-IzGRfGwSsufXJWOcQ0tmECKMG/dbxhUwDOySTS7JE1AM8pkB9+bpcTPs8bTxxSNrSvGocSczrBlOh97tZObq9Q==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.9 - '@tsdown/exe': 0.22.9 + '@tsdown/css': 0.22.12 + '@tsdown/exe': 0.22.12 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6225,6 +6225,10 @@ packages: resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} engines: {node: '>=0.10.0'} + verkit@0.1.2: + resolution: {integrity: sha512-WqkT8n3hqizuCu71W3bUzf5fjBmkbXcudsehe/NbxA8PgqoKnSOY5K0Ba2ckg1qaRaSpSz7as/n9K1R9JXjQKg==} + engines: {node: '>=18.12.0'} + verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -6519,14 +6523,14 @@ packages: youtubei.js@17.2.0: resolution: {integrity: sha512-XLNsgRKO1h7t4i9tIMWSQSeWdD7Ujkk5v1m5YCaumaHMhu/xuLqtO3M0Hq7CXNup9HlJ1NGrT1Y+HLIHnL6Ujg==} - yuku-ast@0.1.7: - resolution: {integrity: sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA==} + yuku-ast@0.7.0: + resolution: {integrity: sha512-W5UzqYg/Xuyz41cAyxwo/TIPpDX221OuC9U5f44+NulDAHDwtzvGE3M3Xz3mdcLEutWOmTc/WP/y7NO6ANA2gw==} - yuku-codegen@0.6.5: - resolution: {integrity: sha512-g8gbp05j2NNSKe0Uu2ViBRvawzXaWggxS9YwcVJ2d3I99VxbeOeENSUseA8AXPCZj/gcLkQxl7uhTmtK36qh8Q==} + yuku-codegen@0.7.0: + resolution: {integrity: sha512-RJgoLaIU2AGKRkUHqMtw6gQhYm+NXZm/AFnfn+5YAHgRfApIc/PNJABJHihHoxWltayjbwEOCa68zqxdJafgfA==} - yuku-parser@0.6.5: - resolution: {integrity: sha512-9A5zaOqE3X3wcPOTK97CYInpKwaUGYnwfHWasJaI2Je1OhI4pQmRA6YCSh7oKewEb2iJM4xlJdbwwj4Aydty7w==} + yuku-parser@0.7.0: + resolution: {integrity: sha512-72HXJhlPOolJN4ER0xZy1wtAeWZEG4uXfZnzEm2uD7yAWt32VE/52FwIfVGi2Hs2P0JRZK2+sPwULQMTuEzYtw==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -8547,73 +8551,73 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.6.5': + '@yuku-codegen/binding-darwin-arm64@0.7.0': optional: true - '@yuku-codegen/binding-darwin-x64@0.6.5': + '@yuku-codegen/binding-darwin-x64@0.7.0': optional: true - '@yuku-codegen/binding-freebsd-x64@0.6.5': + '@yuku-codegen/binding-freebsd-x64@0.7.0': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.6.5': + '@yuku-codegen/binding-linux-arm-gnu@0.7.0': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.6.5': + '@yuku-codegen/binding-linux-arm-musl@0.7.0': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.6.5': + '@yuku-codegen/binding-linux-arm64-gnu@0.7.0': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.6.5': + '@yuku-codegen/binding-linux-arm64-musl@0.7.0': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.6.5': + '@yuku-codegen/binding-linux-x64-gnu@0.7.0': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.6.5': + '@yuku-codegen/binding-linux-x64-musl@0.7.0': optional: true - '@yuku-codegen/binding-win32-arm64@0.6.5': + '@yuku-codegen/binding-win32-arm64@0.7.0': optional: true - '@yuku-codegen/binding-win32-x64@0.6.5': + '@yuku-codegen/binding-win32-x64@0.7.0': optional: true - '@yuku-parser/binding-darwin-arm64@0.6.5': + '@yuku-parser/binding-darwin-arm64@0.7.0': optional: true - '@yuku-parser/binding-darwin-x64@0.6.5': + '@yuku-parser/binding-darwin-x64@0.7.0': optional: true - '@yuku-parser/binding-freebsd-x64@0.6.5': + '@yuku-parser/binding-freebsd-x64@0.7.0': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.6.5': + '@yuku-parser/binding-linux-arm-gnu@0.7.0': optional: true - '@yuku-parser/binding-linux-arm-musl@0.6.5': + '@yuku-parser/binding-linux-arm-musl@0.7.0': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.6.5': + '@yuku-parser/binding-linux-arm64-gnu@0.7.0': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.6.5': + '@yuku-parser/binding-linux-arm64-musl@0.7.0': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.6.5': + '@yuku-parser/binding-linux-x64-gnu@0.7.0': optional: true - '@yuku-parser/binding-linux-x64-musl@0.6.5': + '@yuku-parser/binding-linux-x64-musl@0.7.0': optional: true - '@yuku-parser/binding-win32-arm64@0.6.5': + '@yuku-parser/binding-win32-arm64@0.7.0': optional: true - '@yuku-parser/binding-win32-x64@0.6.5': + '@yuku-parser/binding-win32-x64@0.7.0': optional: true - '@yuku-toolchain/types@0.5.43': {} + '@yuku-toolchain/types@0.7.0': {} '@zone-eu/mailsplit@5.4.14': dependencies: @@ -11167,7 +11171,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.19: + postcss@8.5.20: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -11419,15 +11423,15 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.11(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): + rolldown-plugin-dts@0.27.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.5 obug: 2.1.4 rolldown: 1.2.0 - yuku-ast: 0.1.7 - yuku-codegen: 0.6.5 - yuku-parser: 0.6.5 + yuku-ast: 0.7.0 + yuku-codegen: 0.7.0 + yuku-parser: 0.7.0 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11872,7 +11876,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.9(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): + tsdown@0.22.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11883,12 +11887,12 @@ snapshots: obug: 2.1.4 picomatch: 4.0.5 rolldown: 1.2.0 - rolldown-plugin-dts: 0.27.11(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) - semver: 7.8.5 + rolldown-plugin-dts: 0.27.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) tinyexec: 1.2.4 tinyglobby: 0.2.17 tree-kill: 1.2.2 unconfig-core: 7.5.0 + verkit: 0.1.2 optionalDependencies: tsx: 4.23.1 typescript: '@typescript/typescript6@6.0.2' @@ -12091,6 +12095,8 @@ snapshots: vali-date@1.0.0: {} + verkit@0.1.2: {} + verror@1.10.0: dependencies: assert-plus: 1.0.0 @@ -12122,7 +12128,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.19 + postcss: 8.5.20 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: @@ -12385,41 +12391,42 @@ snapshots: fflate: 0.8.3 meriyah: 6.1.4 - yuku-ast@0.1.7: + yuku-ast@0.7.0: dependencies: - '@yuku-toolchain/types': 0.5.43 + '@yuku-toolchain/types': 0.7.0 - yuku-codegen@0.6.5: + yuku-codegen@0.7.0: dependencies: - '@yuku-toolchain/types': 0.5.43 + '@yuku-toolchain/types': 0.7.0 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.6.5 - '@yuku-codegen/binding-darwin-x64': 0.6.5 - '@yuku-codegen/binding-freebsd-x64': 0.6.5 - '@yuku-codegen/binding-linux-arm-gnu': 0.6.5 - '@yuku-codegen/binding-linux-arm-musl': 0.6.5 - '@yuku-codegen/binding-linux-arm64-gnu': 0.6.5 - '@yuku-codegen/binding-linux-arm64-musl': 0.6.5 - '@yuku-codegen/binding-linux-x64-gnu': 0.6.5 - '@yuku-codegen/binding-linux-x64-musl': 0.6.5 - '@yuku-codegen/binding-win32-arm64': 0.6.5 - '@yuku-codegen/binding-win32-x64': 0.6.5 - - yuku-parser@0.6.5: - dependencies: - '@yuku-toolchain/types': 0.5.43 + '@yuku-codegen/binding-darwin-arm64': 0.7.0 + '@yuku-codegen/binding-darwin-x64': 0.7.0 + '@yuku-codegen/binding-freebsd-x64': 0.7.0 + '@yuku-codegen/binding-linux-arm-gnu': 0.7.0 + '@yuku-codegen/binding-linux-arm-musl': 0.7.0 + '@yuku-codegen/binding-linux-arm64-gnu': 0.7.0 + '@yuku-codegen/binding-linux-arm64-musl': 0.7.0 + '@yuku-codegen/binding-linux-x64-gnu': 0.7.0 + '@yuku-codegen/binding-linux-x64-musl': 0.7.0 + '@yuku-codegen/binding-win32-arm64': 0.7.0 + '@yuku-codegen/binding-win32-x64': 0.7.0 + + yuku-parser@0.7.0: + dependencies: + '@yuku-toolchain/types': 0.7.0 + yuku-ast: 0.7.0 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.6.5 - '@yuku-parser/binding-darwin-x64': 0.6.5 - '@yuku-parser/binding-freebsd-x64': 0.6.5 - '@yuku-parser/binding-linux-arm-gnu': 0.6.5 - '@yuku-parser/binding-linux-arm-musl': 0.6.5 - '@yuku-parser/binding-linux-arm64-gnu': 0.6.5 - '@yuku-parser/binding-linux-arm64-musl': 0.6.5 - '@yuku-parser/binding-linux-x64-gnu': 0.6.5 - '@yuku-parser/binding-linux-x64-musl': 0.6.5 - '@yuku-parser/binding-win32-arm64': 0.6.5 - '@yuku-parser/binding-win32-x64': 0.6.5 + '@yuku-parser/binding-darwin-arm64': 0.7.0 + '@yuku-parser/binding-darwin-x64': 0.7.0 + '@yuku-parser/binding-freebsd-x64': 0.7.0 + '@yuku-parser/binding-linux-arm-gnu': 0.7.0 + '@yuku-parser/binding-linux-arm-musl': 0.7.0 + '@yuku-parser/binding-linux-arm64-gnu': 0.7.0 + '@yuku-parser/binding-linux-arm64-musl': 0.7.0 + '@yuku-parser/binding-linux-x64-gnu': 0.7.0 + '@yuku-parser/binding-linux-x64-musl': 0.7.0 + '@yuku-parser/binding-win32-arm64': 0.7.0 + '@yuku-parser/binding-win32-x64': 0.7.0 zod@3.25.76: {} From f74920785b5b69b0eb26659c774def4d8400d034 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 08:16:48 +0000 Subject: [PATCH 0358/1190] chore(deps): bump hono from 4.12.30 to 4.12.31 (#22765) Bumps [hono](https://github.com/honojs/hono) from 4.12.30 to 4.12.31. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.30...v4.12.31) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.31 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index b7f43488d810..f48a9fc7482b 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", "header-generator": "2.1.82", - "hono": "4.12.30", + "hono": "4.12.31", "html-to-text": "10.0.0", "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09cff1076421..845c90422305 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,10 +48,10 @@ importers: version: 6.14.0 '@hono/node-server': specifier: 2.0.10 - version: 2.0.10(hono@4.12.30) + version: 2.0.10(hono@4.12.31) '@hono/zod-openapi': specifier: 1.5.1 - version: 1.5.1(hono@4.12.30)(zod@4.4.3) + version: 1.5.1(hono@4.12.31)(zod@4.4.3) '@jocmp/mercury-parser': specifier: 3.0.9 version: 3.0.9 @@ -84,7 +84,7 @@ importers: version: 0.0.25 '@scalar/hono-api-reference': specifier: 0.11.11 - version: 0.11.11(hono@4.12.30) + version: 0.11.11(hono@4.12.31) '@sentry/node': specifier: 10.66.0 version: 10.66.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) @@ -128,8 +128,8 @@ importers: specifier: 2.1.82 version: 2.1.82 hono: - specifier: 4.12.30 - version: 4.12.30 + specifier: 4.12.31 + version: 4.12.31 html-to-text: specifier: 10.0.0 version: 10.0.0 @@ -4258,8 +4258,8 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.12.30: - resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==} + hono@4.12.31: + resolution: {integrity: sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==} engines: {node: '>=16.9.0'} hookable@6.1.1: @@ -7029,21 +7029,21 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.10(hono@4.12.30)': + '@hono/node-server@2.0.10(hono@4.12.31)': dependencies: - hono: 4.12.30 + hono: 4.12.31 - '@hono/zod-openapi@1.5.1(hono@4.12.30)(zod@4.4.3)': + '@hono/zod-openapi@1.5.1(hono@4.12.31)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) - '@hono/zod-validator': 0.9.0(hono@4.12.30)(zod@4.4.3) - hono: 4.12.30 + '@hono/zod-validator': 0.9.0(hono@4.12.31)(zod@4.4.3) + hono: 4.12.31 openapi3-ts: 4.6.0 zod: 4.4.3 - '@hono/zod-validator@0.9.0(hono@4.12.30)(zod@4.4.3)': + '@hono/zod-validator@0.9.0(hono@4.12.31)(zod@4.4.3)': dependencies: - hono: 4.12.30 + hono: 4.12.31 zod: 4.4.3 '@humanfs/core@0.19.2': @@ -8052,10 +8052,10 @@ snapshots: '@scalar/helpers@0.9.2': {} - '@scalar/hono-api-reference@0.11.11(hono@4.12.30)': + '@scalar/hono-api-reference@0.11.11(hono@4.12.31)': dependencies: '@scalar/client-side-rendering': 0.3.4 - hono: 4.12.30 + hono: 4.12.31 '@scalar/schemas@0.7.4': dependencies: @@ -9785,7 +9785,7 @@ snapshots: hmacsha1@1.0.0: {} - hono@4.12.30: {} + hono@4.12.31: {} hookable@6.1.1: {} From 63771e18b407f10aa1ed36969f9af29fa05315f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:52:27 +0800 Subject: [PATCH 0359/1190] chore(deps): bump devenv from `d773046` to `5f1cf17` (#22768) Bumps [devenv](https://github.com/cachix/devenv) from `d773046` to `5f1cf17`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/d77304696697c43672e10f29d1da401b33ab2778...5f1cf17be0fc48689bd0ecb810de6d2e06d259a1) --- updated-dependencies: - dependency-name: devenv dependency-version: 5f1cf17be0fc48689bd0ecb810de6d2e06d259a1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 61885b0dacea..cd29018fcc2a 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784248563, - "narHash": "sha256-rRjR2qADMZbvrDkZzeYrdssLKpbTGymNXLnVdbT6XpQ=", + "lastModified": 1784325378, + "narHash": "sha256-Gof6j4d43yX2qSLLp78JILke346IggDFIxTgl3ecVQE=", "owner": "cachix", "repo": "devenv", - "rev": "d77304696697c43672e10f29d1da401b33ab2778", + "rev": "5f1cf17be0fc48689bd0ecb810de6d2e06d259a1", "type": "github" }, "original": { From 95758d31a6c861548331f577a263471ce79f01b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:54:23 +0800 Subject: [PATCH 0360/1190] chore(deps): bump nixpkgs from `753cc8a` to `61b7c44` (#22767) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `753cc8a` to `61b7c44`. - [Commits](https://github.com/NixOS/nixpkgs/compare/753cc8a3a87467296ddd1fa93f0cc3e81120ee46...61b7c44c4073f0b827768aff0049561b5110ea5a) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: 61b7c44c4073f0b827768aff0049561b5110ea5a dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cd29018fcc2a..9c645e227eb6 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784120854, - "narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=", + "lastModified": 1784356753, + "narHash": "sha256-12KrbMiWLcf8m7pCvAtZh1ZrgF85ZXDXvfR/fWTKy84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46", + "rev": "61b7c44c4073f0b827768aff0049561b5110ea5a", "type": "github" }, "original": { From ef5506b74c2d0236481f5d0329212af21eb05edd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:58:16 +0800 Subject: [PATCH 0361/1190] chore(deps): bump source-map from 0.7.6 to 0.8.0 (#22766) Bumps [source-map](https://github.com/mozilla/source-map) from 0.7.6 to 0.8.0. - [Release notes](https://github.com/mozilla/source-map/releases) - [Changelog](https://github.com/mozilla/source-map/blob/master/CHANGELOG.md) - [Commits](https://github.com/mozilla/source-map/compare/0.7.6...v0.8.0) --- updated-dependencies: - dependency-name: source-map dependency-version: 0.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f48a9fc7482b..ddab1ceb884d 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "sanitize-html": "2.17.6", "simplecc-wasm": "1.1.1", "socks-proxy-agent": "10.1.0", - "source-map": "0.7.6", + "source-map": "0.8.0", "telegram": "2.26.22", "title": "4.0.1", "tldts": "7.4.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 845c90422305..9803a58c5933 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,8 +230,8 @@ importers: specifier: 10.1.0 version: 10.1.0 source-map: - specifier: 0.7.6 - version: 0.7.6 + specifier: 0.8.0 + version: 0.8.0 telegram: specifier: 2.26.22 version: 2.26.22 @@ -5733,8 +5733,8 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} engines: {node: '>= 12'} split-on-first@3.0.0: @@ -11644,7 +11644,7 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.6: {} + source-map@0.8.0: {} split-on-first@3.0.0: {} From 42a788299aa335b38969af13e8468822efc58643 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:01:53 +0800 Subject: [PATCH 0362/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 3 updates (#22761) Bumps the cloudflare group with 3 updates in the / directory: [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-pool-workers` from 0.18.5 to 0.18.6 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.18.6/packages/vitest-pool-workers) Updates `@cloudflare/workers-types` from 5.20260717.1 to 5.20260719.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.111.0 to 4.112.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.112.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-pool-workers" dependency-version: 0.18.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260719.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.112.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 104 ++++++++++++++++++++++++------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index ddab1ceb884d..958b0b41c0e8 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", - "@cloudflare/vitest-pool-workers": "0.18.5", - "@cloudflare/workers-types": "5.20260717.1", + "@cloudflare/vitest-pool-workers": "0.18.6", + "@cloudflare/workers-types": "5.20260719.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", - "wrangler": "4.111.0", + "wrangler": "4.112.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9803a58c5933..c3816a6938b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,11 +294,11 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/vitest-pool-workers': - specifier: 0.18.5 - version: 0.18.5(@cloudflare/workers-types@5.20260717.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + specifier: 0.18.6 + version: 0.18.6(@cloudflare/workers-types@5.20260719.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260717.1 - version: 5.20260717.1 + specifier: 5.20260719.1 + version: 5.20260719.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -471,8 +471,8 @@ importers: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: - specifier: 4.111.0 - version: 4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.112.0 + version: 4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -604,45 +604,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.18.5': - resolution: {integrity: sha512-Qe00zuHDRyAsOO7DmHnPcOhCiShdkt/NlRf/GrnKHzuXuysmt5YVyU/WaMVPqEkPYrgO+H9bSEJ1c9/7Nbw1Hg==} + '@cloudflare/vitest-pool-workers@0.18.6': + resolution: {integrity: sha512-6JGqaQsQRZIVq/6jEC4ouJnShZriPIJ2X0yGndwMm+SiPP93pJi5Dp30dYAoztNrNJC7wWK7ec5slLfMBMZ8jA==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260710.1': - resolution: {integrity: sha512-OqJl2eWF5+y9jarMm3YqqCTUe7Hd4ihogX5jyRU8iaAgOVyDr/Bk6aXpPCVUi1/MHzO93a18R/TmSTtzmB0sQw==} + '@cloudflare/workerd-darwin-64@1.20260714.1': + resolution: {integrity: sha512-ZWXqAN8G7Cx9hMRQuk+59ziJhR3j1F4iO+Qs8aHdfKZ3Dq5Yi/57xvkJTgCGBnW1YU/L78r8f6HEy51bwbTpNw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260710.1': - resolution: {integrity: sha512-MYBqWgUblO+VlGvO73zYsH3hB9tdRj+yLyt5IHDFWryipb2l1efmNiWtAOkIhSRfypqLYGFrfpaDm2Hg00XVKw==} + '@cloudflare/workerd-darwin-arm64@1.20260714.1': + resolution: {integrity: sha512-tueWxWC3wyCbMG6zRAxsMXX0YLgrRWbiAPYFQ2uJ7dUH8G+5E7UTWaQS9B1HdJ0bpKFW1NWxhs1o2noKVFSUYg==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260710.1': - resolution: {integrity: sha512-lVWUgqI8qrkqvaCBGElu1kdaUFdAvaS2RD8K4qkCFP9hI3f5TCXumEs5qWSeZkvKum0+X/uJZ5hBFWsYI5SmoQ==} + '@cloudflare/workerd-linux-64@1.20260714.1': + resolution: {integrity: sha512-1VChTZRb0l0F7R4e1G5RtLKV4oFi6x+rQgxh2+yu887j3l/3TLgatuv1L8/5zhc9gKEhATTxOh0e52Rtd9dDWQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260710.1': - resolution: {integrity: sha512-kDwDPItBjAI4JL0df9Fma2N+Qggbm77IB/DnroAkEGQ79fpR80sYMyuB/ZQKyjEk9f48Ocq7HCCLq59qVSyNqA==} + '@cloudflare/workerd-linux-arm64@1.20260714.1': + resolution: {integrity: sha512-rMm3G+NirG2UdgHIRDdF1asNC6FqgIzZzkRG+VDhhDGcVxAQwvrMT1E38BivEvHr3G04MB4AfhcOczX0+GtRkQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260710.1': - resolution: {integrity: sha512-GcLHy1oN1dfK6g1Z7UDV9f5xMGyTfPwcjWQ0sfWKH31IsoEVCRapnj3IC0PoIrDbnoo6irGPP0CwVs3WzdTajw==} + '@cloudflare/workerd-windows-64@1.20260714.1': + resolution: {integrity: sha512-cGqnU3Hg2YZS/k3SAqrMp1DjpdsyFde72tWltdl6ZT9+SFz/Zrk/8gyTU1TcxC4YApXeNVH5TyU5cOGPgUJ0pg==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260717.1': - resolution: {integrity: sha512-En+uoU8kbQoUcJMAPOQU+3hyQ4INEpiPO04GIyuh5b0Q5+uPB6az+icvkl+SoSzN3gbQFHZACpShRY+AK3zD1Q==} + '@cloudflare/workers-types@5.20260719.1': + resolution: {integrity: sha512-DcGasbfUuczQilc80vhL2MPPdWcaxWkx0hN5IW9UdNDBdrRvWcal3akSJ6Ccm7e8+/OGeR+8tYrqkykA3YGSZw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3681,7 +3681,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -4982,8 +4982,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260710.0: - resolution: {integrity: sha512-x1LLRkU6o1p7hiKrB0TRnL0MJn6xFOT+/vrlEQINz5cRDKLP8ru4hBqWTIvXAetzr1acKAnmAaG84pQ4W/K14g==} + miniflare@4.20260714.0: + resolution: {integrity: sha512-MYlTCLdWCPqvrYY2uLwOjXwmglXuiHE3TGGkbOW4BwjUPa1r07E0iuHwrNDIs/sxK21r+o90Jx58AV2KeNdJZw==} engines: {node: '>=22.0.0'} hasBin: true @@ -6390,17 +6390,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260710.1: - resolution: {integrity: sha512-U2sBPPrb9U97sBKnnMN6Kv8p65903P35nwMkPE9vSH/bRuRqkZ3a1EjUw3jV28RhiyXpkLF77Evzw8XimFxyTw==} + workerd@1.20260714.1: + resolution: {integrity: sha512-oIbQzfdyl9UQUnG6XLegcSq0Mgt/7WKDbFOoqGgOWCS+/fhyGB460uKEgdAQQ9RHCO/ttcNCX/KiMIQzdoeu3Q==} engines: {node: '>=16'} hasBin: true - wrangler@4.111.0: - resolution: {integrity: sha512-bffpI9EyrnpKkF/1S+RaIv8oRD93GtbsA7TlfWwOsGJGB7VO3jVbdGzpC9TU7Bqom3z7jUxcte4Z9MPhaQ4HoQ==} + wrangler@4.112.0: + resolution: {integrity: sha512-5H+XUD0TySCv1LuktFHDIEOkboH2nTfQs+35L+USt3MtntjDTMVIJprLgQcL2WBjulOyjxpd1vyTiSTJVW5MjQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260710.1 + '@cloudflare/workers-types': ^5.20260714.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6694,43 +6694,43 @@ snapshots: '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260714.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260710.1 + workerd: 1.20260714.1 - '@cloudflare/vitest-pool-workers@0.18.5(@cloudflare/workers-types@5.20260717.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.6(@cloudflare/workers-types@5.20260719.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260710.1': + '@cloudflare/workerd-darwin-64@1.20260714.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260710.1': + '@cloudflare/workerd-darwin-arm64@1.20260714.1': optional: true - '@cloudflare/workerd-linux-64@1.20260710.1': + '@cloudflare/workerd-linux-64@1.20260714.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260710.1': + '@cloudflare/workerd-linux-arm64@1.20260714.1': optional: true - '@cloudflare/workerd-windows-64@1.20260710.1': + '@cloudflare/workerd-windows-64@1.20260714.1': optional: true - '@cloudflare/workers-types@5.20260717.1': {} + '@cloudflare/workers-types@5.20260719.1': {} '@colors/colors@1.6.0': {} @@ -10708,12 +10708,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.28.0 - workerd: 1.20260710.1 + workerd: 1.20260714.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -12243,26 +12243,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260710.1: + workerd@1.20260714.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260710.1 - '@cloudflare/workerd-darwin-arm64': 1.20260710.1 - '@cloudflare/workerd-linux-64': 1.20260710.1 - '@cloudflare/workerd-linux-arm64': 1.20260710.1 - '@cloudflare/workerd-windows-64': 1.20260710.1 + '@cloudflare/workerd-darwin-64': 1.20260714.1 + '@cloudflare/workerd-darwin-arm64': 1.20260714.1 + '@cloudflare/workerd-linux-64': 1.20260714.1 + '@cloudflare/workerd-linux-arm64': 1.20260714.1 + '@cloudflare/workerd-windows-64': 1.20260714.1 - wrangler@4.111.0(@cloudflare/workers-types@5.20260717.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260710.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260714.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260710.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260710.1 + workerd: 1.20260714.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260717.1 + '@cloudflare/workers-types': 5.20260719.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From e63c66fc2e48d8cac7cc8077516f783494c690a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:04:15 +0800 Subject: [PATCH 0363/1190] chore(deps): bump MatteoGabriele/agentscan-action from 2.0.1 to 2.2.0 (#22760) Bumps [MatteoGabriele/agentscan-action](https://github.com/matteogabriele/agentscan-action) from 2.0.1 to 2.2.0. - [Release notes](https://github.com/matteogabriele/agentscan-action/releases) - [Commits](https://github.com/matteogabriele/agentscan-action/compare/c7d61446e7aece6bdd3edcee4558bbfc0392615e...e02ef270c024bfad3541b2a71619c3c9f20b3312) --- updated-dependencies: - dependency-name: MatteoGabriele/agentscan-action dependency-version: 2.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9f315d2c15cc..f07e0e6060d0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -107,7 +107,7 @@ jobs: restore-keys: agentscan-cache- - name: AgentScan - uses: MatteoGabriele/agentscan-action@c7d61446e7aece6bdd3edcee4558bbfc0392615e # v2.0.1 + uses: MatteoGabriele/agentscan-action@e02ef270c024bfad3541b2a71619c3c9f20b3312 # v2.2.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} trusted-author-associations: 'collaborator,member,owner' From 81af981d677eecc0dc50120f0d29a82a63bc7586 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:17:37 +0800 Subject: [PATCH 0364/1190] chore(deps): bump @honeybadger-io/js from 6.14.0 to 6.15.0 (#22764) Bumps [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js) from 6.14.0 to 6.15.0. - [Release notes](https://github.com/honeybadger-io/honeybadger-js/releases) - [Commits](https://github.com/honeybadger-io/honeybadger-js/compare/@honeybadger-io/js@6.14.0...@honeybadger-io/js@6.15.0) --- updated-dependencies: - dependency-name: "@honeybadger-io/js" dependency-version: 6.15.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 958b0b41c0e8..d7859c270008 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@bbob/plugin-helper": "4.3.1", "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", - "@honeybadger-io/js": "6.14.0", + "@honeybadger-io/js": "6.15.0", "@hono/node-server": "2.0.10", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3816a6938b3..de425c260e7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 33.0.0 version: 33.0.0 '@honeybadger-io/js': - specifier: 6.14.0 - version: 6.14.0 + specifier: 6.15.0 + version: 6.15.0 '@hono/node-server': specifier: 2.0.10 version: 2.0.10(hono@4.12.31) @@ -1084,12 +1084,12 @@ packages: resolution: {integrity: sha512-PW3SdoZ8ULzW0We0lgplyZHjuXJtOdRvU5RL6ErNUlcTeSqjoHtk1pWdHKTwk3brPuIiqUB26K7fX84VroDqRg==} engines: {node: '>=12.0.0'} - '@honeybadger-io/core@6.9.0': - resolution: {integrity: sha512-nHkTigMgqABQ6XuhZ4/2cVIArwImctdWfmu/ckWLoF5E4JlATe9WUL3hMRL8UZPmu/ThmbojxkLwDgHts1LyUA==} + '@honeybadger-io/core@6.10.0': + resolution: {integrity: sha512-U3hVsd/l55SCV5s7pkYZeOZcMx5dRevnrPcF99AZyjGxsnIbT4+GcCdmd5vfrvzw23SFM/0U40zwd5shR3ohsw==} engines: {node: '>=14'} - '@honeybadger-io/js@6.14.0': - resolution: {integrity: sha512-ZQGop/Ik7vyRJzW/wzE1CL4611fz9+5arrMdS0ivrzDmm/4BQeK4OKZlwfMZqEZtODwfnl32ngh0ayW/jahoSg==} + '@honeybadger-io/js@6.15.0': + resolution: {integrity: sha512-MYBdQuaq/8tJ+w75+H9CnVRZrZKl0DiBSvu3hkS7w6tGVqJaIW6uDptg9MC/ltOgIvEECQWCy5Db9j+sXxcScg==} engines: {node: '>=14'} hasBin: true @@ -3681,7 +3681,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -7018,14 +7018,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@honeybadger-io/core@6.9.0': + '@honeybadger-io/core@6.10.0': dependencies: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.14.0': + '@honeybadger-io/js@6.15.0': dependencies: - '@honeybadger-io/core': 6.9.0 + '@honeybadger-io/core': 6.10.0 '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 From 86aa09e61be238dd6923e40304d21f7dcd0e3438 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:29:11 +0800 Subject: [PATCH 0365/1190] chore(deps-dev): bump lint-staged from 17.0.8 to 17.1.0 (#22763) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 17.0.8 to 17.1.0. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.0.8...v17.1.0) --- updated-dependencies: - dependency-name: lint-staged dependency-version: 17.1.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 175 ++----------------------------------------------- 2 files changed, 6 insertions(+), 171 deletions(-) diff --git a/package.json b/package.json index d7859c270008..3729837291fa 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "globals": "17.7.0", "husky": "9.1.7", "js-beautify": "2.0.3", - "lint-staged": "17.0.8", + "lint-staged": "17.1.0", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de425c260e7f..6fbec76d9493 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -411,8 +411,8 @@ importers: specifier: 2.0.3 version: 2.0.3 lint-staged: - specifier: 17.0.8 - version: 17.0.8 + specifier: 17.1.0 + version: 17.1.0 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -3193,10 +3193,6 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3436,14 +3432,6 @@ packages: class-transformer@0.3.1: resolution: {integrity: sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA==} - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - - cli-truncate@5.2.0: - resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} - engines: {node: '>=20'} - cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -3760,9 +3748,6 @@ packages: electron-to-chromium@1.5.392: resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==} - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3809,10 +3794,6 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} @@ -3997,9 +3978,6 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -4144,10 +4122,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -4443,10 +4417,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} - engines: {node: '>=18'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -4730,15 +4700,11 @@ packages: linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} - lint-staged@17.0.8: - resolution: {integrity: sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==} + lint-staged@17.1.0: + resolution: {integrity: sha512-d7UQRu/9ZPgfu4+hu/k0wny5GEaIxo+2jb2LJqQDkE7cHRTm1HGqNUDq5UOwsGPpjpaNAFmgAsYo3TR+i9cSJw==} engines: {node: '>=22.22.1'} hasBin: true - listr2@10.2.2: - resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} - engines: {node: '>=22.13.0'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -4750,10 +4716,6 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} - logform@2.7.0: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} @@ -4974,10 +4936,6 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - mimic-response@4.0.0: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5161,10 +5119,6 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -5577,19 +5531,12 @@ packages: resolution: {integrity: sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==} engines: {node: '>=20'} - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - rettime@0.11.11: resolution: {integrity: sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==} rfc4648@1.5.4: resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@5.0.10: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true @@ -5691,14 +5638,6 @@ packages: simplecc-wasm@1.1.1: resolution: {integrity: sha512-NHBvSBlnOk7nqiNKvNpG/3DqD4XBkls3CqmTl4t3At4v7N9z1HO2t9g7vK//0o33zcgVOKE59vFcyCcgCuw6zQ==} - slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} - - slice-ansi@8.0.0: - resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} - engines: {node: '>=20'} - slide@1.1.6: resolution: {integrity: sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==} @@ -5798,14 +5737,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - string-width@8.2.2: - resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} - engines: {node: '>=20'} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -6405,10 +6336,6 @@ packages: '@cloudflare/workers-types': optional: true - wrap-ansi@10.0.0: - resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} - engines: {node: '>=20'} - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -6421,10 +6348,6 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrap-ansi@9.0.2: - resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} - engines: {node: '>=18'} - write-file-atomic@1.3.4: resolution: {integrity: sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==} @@ -8652,10 +8575,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-escapes@7.3.0: - dependencies: - environment: 1.1.0 - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -8867,15 +8786,6 @@ snapshots: class-transformer@0.3.1: {} - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-truncate@5.2.0: - dependencies: - slice-ansi: 8.0.0 - string-width: 8.2.2 - cli-width@4.1.0: {} clipboardy@4.0.0: @@ -9154,8 +9064,6 @@ snapshots: electron-to-chromium@1.5.392: {} - emoji-regex@10.6.0: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -9190,8 +9098,6 @@ snapshots: entities@8.0.0: {} - environment@1.1.0: {} - error-stack-parser-es@1.0.5: {} es-define-property@1.0.1: {} @@ -9484,8 +9390,6 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 - eventemitter3@5.0.4: {} - execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -9640,8 +9544,6 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.6.0: {} - get-stream@8.0.1: {} get-stream@9.0.1: @@ -9999,10 +9901,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-fullwidth-code-point@5.1.0: - dependencies: - get-east-asian-width: 1.6.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -10268,23 +10166,14 @@ snapshots: dependencies: uc.micro: 2.1.0 - lint-staged@17.0.8: + lint-staged@17.1.0: dependencies: - listr2: 10.2.2 picomatch: 4.0.5 string-argv: 0.3.2 tinyexec: 1.2.4 optionalDependencies: yaml: 2.9.0 - listr2@10.2.2: - dependencies: - cli-truncate: 5.2.0 - eventemitter3: 5.0.4 - log-update: 6.1.0 - rfdc: 1.4.1 - wrap-ansi: 10.0.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -10293,14 +10182,6 @@ snapshots: lodash@4.18.1: {} - log-update@6.1.0: - dependencies: - ansi-escapes: 7.3.0 - cli-cursor: 5.0.0 - slice-ansi: 7.1.2 - strip-ansi: 7.2.0 - wrap-ansi: 9.0.2 - logform@2.7.0: dependencies: '@colors/colors': 1.6.0 @@ -10704,8 +10585,6 @@ snapshots: mimic-fn@4.0.0: {} - mimic-function@5.0.1: {} - mimic-response@4.0.0: {} miniflare@4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): @@ -10878,10 +10757,6 @@ snapshots: dependencies: mimic-fn: 4.0.0 - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -11408,17 +11283,10 @@ snapshots: dependencies: lowercase-keys: 3.0.0 - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - rettime@0.11.11: {} rfc4648@1.5.4: {} - rfdc@1.4.1: {} - rimraf@5.0.10: dependencies: glob: 10.5.0 @@ -11596,16 +11464,6 @@ snapshots: simplecc-wasm@1.1.1: {} - slice-ansi@7.1.2: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - - slice-ansi@8.0.0: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - slide@1.1.6: {} smart-buffer@4.2.0: {} @@ -11702,17 +11560,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.2.0 - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - - string-width@8.2.2: - dependencies: - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - string_decoder@1.3.0: dependencies: safe-buffer: '@nolyfill/safe-buffer@1.0.44' @@ -12268,12 +12115,6 @@ snapshots: - bufferutil - utf-8-validate - wrap-ansi@10.0.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 8.2.2 - strip-ansi: 7.2.0 - wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -12292,12 +12133,6 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.2.0 - wrap-ansi@9.0.2: - dependencies: - ansi-styles: 6.2.3 - string-width: 7.2.0 - strip-ansi: 7.2.0 - write-file-atomic@1.3.4: dependencies: graceful-fs: 4.2.11 From abb7a21e28f6891c95b36cc4b018a2b725ce67bf Mon Sep 17 00:00:00 2001 From: Serhii Hospodarchuk <69005134+gosxrgxx@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:31:04 +0300 Subject: [PATCH 0366/1190] feat(core): add summary field support for atom and json feed (#22758) * fix(core): summary field in json feed * feat(core): add summary field to atom * test: update json view test to match the new summary field --- lib/types.ts | 1 + lib/views/atom.test.tsx | 1 + lib/views/atom.tsx | 1 + lib/views/json.test.ts | 2 +- lib/views/json.ts | 2 +- 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/types.ts b/lib/types.ts index 54c128ae3958..a565a21be7dd 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -50,6 +50,7 @@ export type DataItem = { html: string; text: string; }; + summary?: string; image?: string; banner?: string; updated?: number | string | Date; diff --git a/lib/views/atom.test.tsx b/lib/views/atom.test.tsx index 8010a8cec630..59bac8f50593 100644 --- a/lib/views/atom.test.tsx +++ b/lib/views/atom.test.tsx @@ -22,6 +22,7 @@ describe('Atom view', () => { description: 'Entry One', pubDate: '2024-01-01T00:00:00Z', updated: '2024-01-02T00:00:00Z', + summary: 'Entry One', author: 'Author One', category: 'News', media: { diff --git a/lib/views/atom.tsx b/lib/views/atom.tsx index 71ccabd688d5..f2c997663f59 100644 --- a/lib/views/atom.tsx +++ b/lib/views/atom.tsx @@ -25,6 +25,7 @@ const RSS: FC<{ data: Data }> = ({ data }) => ( {item.guid || item.link || item.title} {item.pubDate && {new Date(item.pubDate).toISOString()}} {new Date(item.updated || item.pubDate || new Date()).toISOString()} + {item.summary && {item.summary}} {item.author && ( {item.author} diff --git a/lib/views/json.test.ts b/lib/views/json.test.ts index f18046ca44bc..ddfd7671da2a 100644 --- a/lib/views/json.test.ts +++ b/lib/views/json.test.ts @@ -16,7 +16,7 @@ describe('JSON view', () => { { title: 'Item One', link: 'https://example.com/one', - description: 'Entry One', + summary: 'Entry One', guid: 'guid-1', content: { html: '

    hello

    ', diff --git a/lib/views/json.ts b/lib/views/json.ts index f79536f50de4..5dfff9a83479 100644 --- a/lib/views/json.ts +++ b/lib/views/json.ts @@ -22,7 +22,7 @@ const json = (data: Data) => { // content_html and content_text are each optional strings — but one or both must be present content_html: (item.content && item.content.html) || item.description || item.title, content_text: item.content && item.content.text, - summary: item.description, + summary: item.summary, image: item.image || item.itunes_item_image, banner_image: item.banner, date_published: item.pubDate, From de93618f1838df9c8bcf09cf21d237499f0444d2 Mon Sep 17 00:00:00 2001 From: Sryvkver <20717854+Sryvkver@users.noreply.github.com> Date: Tue, 21 Jul 2026 04:03:19 +0200 Subject: [PATCH 0367/1190] fix(route/fanbox): refactor to use playwright to load posts (#22681) * fix(route/fanbox): refactor to use playwright to load posts * fix(route/fanbox) added missing await * fix(route/fanbox) closing context after page close * fix(route/fanbox) Update model to conform to new API response --------- Co-authored-by: Sryvkver --- lib/routes/fanbox/index.ts | 38 ++++++++++++++++++++++++++++++++++--- lib/routes/fanbox/types.ts | 4 +++- lib/routes/fanbox/utils.tsx | 38 ++++++++++++++++++++++++++++++------- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/lib/routes/fanbox/index.ts b/lib/routes/fanbox/index.ts index 487b5bc98e41..4e8a0d4855d2 100644 --- a/lib/routes/fanbox/index.ts +++ b/lib/routes/fanbox/index.ts @@ -1,12 +1,14 @@ import type { Context } from 'hono'; import InvalidParameterError from '@/errors/types/invalid-parameter'; -import type { Data, Route } from '@/types'; +import type { Data, DataItem, Route } from '@/types'; import ofetch from '@/utils/ofetch'; +import playwright from '@/utils/playwright'; +import { setCookies } from '@/utils/playwright-utils'; import { isValidHost } from '@/utils/valid-host'; import type { PostListResponse, UserInfoResponse } from './types'; -import { getHeaders, parseItem } from './utils'; +import { getCookieString, getHeaders, parseItem } from './utils'; export const route: Route = { path: '/:creator', @@ -24,6 +26,7 @@ export const route: Route = { optional: true, }, ], + requirePuppeteer: true, nsfw: true, }, }; @@ -53,7 +56,36 @@ async function handler(ctx: Context): Promise { } const postListResponse = (await ofetch(`https://api.fanbox.cc/post.listCreator?creatorId=${creator}&limit=20&withPinned=true`, { headers: getHeaders() })) as PostListResponse; - const items = await Promise.all(postListResponse.body.map((i) => parseItem(i))); + + const context = await playwright(); + const page = await context.newPage(); + + const cookieString = getCookieString(); + if (cookieString) { + await setCookies(page, cookieString, '.fanbox.cc'); + } + + await page.route('**/*', (route) => { + const request = route.request(); + + if (request.url().startsWith('https://api.fanbox.cc/post.info')) { + route.continue(); + return; + } + + request.resourceType() === 'document' ? route.continue() : route.abort(); + }); + await page.goto('https://www.fanbox.cc/', { + waitUntil: 'domcontentloaded', + }); + + let items: DataItem[]; + try { + items = await Promise.all(postListResponse.body.map((i) => parseItem(page, i))); + } finally { + await page.close(); + await context.close(); + } return { title, diff --git a/lib/routes/fanbox/types.ts b/lib/routes/fanbox/types.ts index cb447ef06118..774843c3963d 100644 --- a/lib/routes/fanbox/types.ts +++ b/lib/routes/fanbox/types.ts @@ -29,7 +29,9 @@ export interface PostListResponse { } export interface PostDetailResponse { - body: PostDetail; + body: { + post: PostDetail; + }; } export interface PostItem { diff --git a/lib/routes/fanbox/utils.tsx b/lib/routes/fanbox/utils.tsx index f0d6d6713031..2abc076a4a64 100644 --- a/lib/routes/fanbox/utils.tsx +++ b/lib/routes/fanbox/utils.tsx @@ -5,18 +5,22 @@ import type { DataItem } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import type { Page } from '@/utils/playwright'; import type { ArticlePost, FilePost, ImagePost, PostDetailResponse, PostItem, TextPost, VideoPost } from './types'; export function getHeaders() { - const sessionid = config.fanbox.session; - const cookie = sessionid ? `FANBOXSESSID=${sessionid}` : ''; return { origin: 'https://fanbox.cc', - cookie, + cookie: getCookieString(), }; } +export function getCookieString() { + const sessionid = config.fanbox.session; + return sessionid ? `FANBOXSESSID=${sessionid}` : ''; +} + function embedUrlMap(urlEmbed: ArticlePost['body']['urlEmbedMap'][string]) { switch (urlEmbed.type) { case 'html': @@ -131,7 +135,7 @@ async function parseArtile(body: ArticlePost['body']) { return ret.join(''); } -async function parseDetail(i: PostDetailResponse['body']) { +async function parseDetail(i: PostDetailResponse['body']['post']) { let ret = ''; if (i.feeRequired !== 0) { ret += `Fee Required: ${i.feeRequired} JPY/month
    `; @@ -167,12 +171,32 @@ async function parseDetail(i: PostDetailResponse['body']) { return ret; } -export function parseItem(item: PostItem) { +export function parseItem(page: Page, item: PostItem) { return cache.tryGet(`fanbox-${item.id}-${item.updatedDatetime}`, async () => { - const postDetail = (await ofetch(`https://api.fanbox.cc/post.info?postId=${item.id}`, { headers: { ...getHeaders(), 'User-Agent': config.trueUA } })) as PostDetailResponse; + const postDetail: PostDetailResponse = await page.evaluate( + async ({ url }) => { + const res = await fetch(url, { + method: 'GET', + credentials: 'include', + headers: { + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-site', + }, + }); + + if (!res.ok) { + throw new Error(`HTTP error! status: ${res.status}`); + } + + return res.json(); + }, + { url: `https://api.fanbox.cc/post.info?postId=${item.id}` } + ); + return { title: item.title || 'No title', - description: await parseDetail(postDetail.body), + description: await parseDetail(postDetail.body.post), pubDate: parseDate(item.updatedDatetime), link: `https://${item.creatorId}.fanbox.cc/posts/${item.id}`, category: item.tags, From e6b750f22b7e98cee3c8ed65107213018c1467e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:06:52 +0000 Subject: [PATCH 0368/1190] chore(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#22773) Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-assets.yml | 4 ++-- .github/workflows/codeql.yml | 2 +- .github/workflows/comment-on-issue.yml | 4 ++-- .github/workflows/dependabot-fork.yml | 2 +- .github/workflows/docker-release.yml | 4 ++-- .github/workflows/docker-test-cont.yml | 2 +- .github/workflows/docker-test.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/issue-command.yml | 8 ++++---- .github/workflows/lint.yml | 2 +- .github/workflows/npm-publish.yml | 2 +- .github/workflows/pr-review.yml | 2 +- .github/workflows/semgrep.yml | 2 +- .github/workflows/similar-issues.yml | 2 +- .github/workflows/test-full-routes.yml | 2 +- .github/workflows/test.yml | 6 +++--- .github/workflows/update-nix-hash.yml | 2 +- 17 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 3158d9791682..4c0798c9c390 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -18,7 +18,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js Active LTS @@ -51,7 +51,7 @@ jobs: if: ${{ env.DOCS_API_TOKEN != '' }} run: echo "defined=true" >> $GITHUB_OUTPUT - name: Checkout docs - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: steps.check-docs-env.outputs.defined == 'true' with: repository: 'RSSNext/rsshub-docs' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a0093b2d53a7..3edda4f7d8b7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Initializes the CodeQL tools for scanning. # TODO: use hash pinning when https://github.com/dependabot/dependabot-core/pull/13007 pass diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index d2952e479323..2381ed7aef23 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -26,7 +26,7 @@ jobs: outputs: closed: ${{ steps.check.outputs.closed }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -60,7 +60,7 @@ jobs: (needs.checkIssue.result == 'success' || needs.checkIssue.result == 'skipped') && needs.checkIssue.outputs.closed != 'true' steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/dependabot-fork.yml b/.github/workflows/dependabot-fork.yml index 3de657ec30d2..ab0c6354d717 100644 --- a/.github/workflows/dependabot-fork.yml +++ b/.github/workflows/dependabot-fork.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Comment Dependabot PR uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 10c1227fb8ce..f6c774e17585 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -61,7 +61,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Extract repository name id: repo-name @@ -277,7 +277,7 @@ jobs: if: needs.check-env.outputs.check-docker == 'true' timeout-minutes: 5 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Docker Hub Description uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0 diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index 7f20ab28316c..d193a284f751 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -14,7 +14,7 @@ jobs: actions: read if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # https://github.com/orgs/community/discussions/25220#discussioncomment-11316244 - name: Search the PR that triggered this workflow diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 9624061fb80e..d3293165f31a 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -27,7 +27,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Docker Buildx # needed by `cache-from` uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index e60ab6ad85be..4c5444ab7395 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 15 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 732ff9563d89..96ecbfc85a8a 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - name: Checkout the latest code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Automatic Rebase @@ -49,7 +49,7 @@ jobs: group: vouch-manage cancel-in-progress: false steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: vouch uses: mitchellh/vouch/action/manage-by-issue@d66fa29a64600490892131ad87597c30c91fcac4 # v1.5.0 @@ -102,11 +102,11 @@ jobs: - name: Checkout if: ${{ !github.event.issue.pull_request }} - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Checkout PR if: github.event.issue.pull_request - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ fromJson(steps.pr-data.outputs.data).head.ref }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f07e0e6060d0..8a06cac56831 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,7 +20,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 34117cde510f..e24dbf57f876 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -22,7 +22,7 @@ jobs: env: HUSKY: 0 steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index ba8cee0b2720..93e7f6df66a8 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -22,7 +22,7 @@ jobs: pull-requests: write steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # https://github.com/orgs/community/discussions/25220#discussioncomment-11316244 - name: Search the PR that triggered this workflow diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 083e4e78991e..f20c0407ebe7 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -22,7 +22,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: semgrep ci --sarif > semgrep.sarif env: SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} diff --git a/.github/workflows/similar-issues.yml b/.github/workflows/similar-issues.yml index 3269b6822e66..14e95701d0c8 100644 --- a/.github/workflows/similar-issues.yml +++ b/.github/workflows/similar-issues.yml @@ -18,7 +18,7 @@ jobs: issues: write steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Check for duplicate issues uses: anthropics/claude-code-action@v1 diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index fd36f0f40a60..0f7fb60ac3d7 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -14,7 +14,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js Active LTS diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02bda0de9aff..4b3db5a5dc11 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,7 @@ jobs: node-version: [latest, lts/*] name: Vitest on Node ${{ matrix.node-version }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -77,7 +77,7 @@ jobs: environment: '{ "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": "1" }' name: Vitest Playwright on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -126,7 +126,7 @@ jobs: node-version: [26, 24] name: Build radar and maintainer on Node ${{ matrix.node-version }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/update-nix-hash.yml b/.github/workflows/update-nix-hash.yml index efbb86956d08..6d99b4eeea74 100644 --- a/.github/workflows/update-nix-hash.yml +++ b/.github/workflows/update-nix-hash.yml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Nix uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 with: From a1f712bb4e1cd5a424d4dec82da851faf69fc59d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:20:11 +0000 Subject: [PATCH 0369/1190] chore(deps): bump p-map from 7.0.5 to 7.0.6 (#22779) Bumps [p-map](https://github.com/sindresorhus/p-map) from 7.0.5 to 7.0.6. - [Release notes](https://github.com/sindresorhus/p-map/releases) - [Commits](https://github.com/sindresorhus/p-map/compare/v7.0.5...v7.0.6) --- updated-dependencies: - dependency-name: p-map dependency-version: 7.0.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3729837291fa..f49d7053d6a9 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "oauth-1.0a": "2.2.6", "ofetch": "1.5.1", "otplib": "13.4.1", - "p-map": "7.0.5", + "p-map": "7.0.6", "pac-proxy-agent": "9.1.0", "patchright": "1.61.1", "query-string": "9.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fbec76d9493..768ae4ba7c78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,8 +197,8 @@ importers: specifier: 13.4.1 version: 13.4.1 p-map: - specifier: 7.0.5 - version: 7.0.5 + specifier: 7.0.6 + version: 7.0.6 pac-proxy-agent: specifier: 9.1.0 version: 9.1.0 @@ -5207,8 +5207,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@7.0.5: - resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==} + p-map@7.0.6: + resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==} engines: {node: '>=18'} p-timeout@6.1.4: @@ -10922,7 +10922,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.5: {} + p-map@7.0.6: {} p-timeout@6.1.4: {} From a676fcb8cfbbf9d2aee8998eee43b94f12afd2f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:20:21 +0000 Subject: [PATCH 0370/1190] chore(deps): bump @honeybadger-io/js from 6.15.0 to 6.15.3 (#22778) Bumps [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js) from 6.15.0 to 6.15.3. - [Release notes](https://github.com/honeybadger-io/honeybadger-js/releases) - [Commits](https://github.com/honeybadger-io/honeybadger-js/compare/@honeybadger-io/js@6.15.0...@honeybadger-io/js@6.15.3) --- updated-dependencies: - dependency-name: "@honeybadger-io/js" dependency-version: 6.15.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index f49d7053d6a9..4696ec466054 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@bbob/plugin-helper": "4.3.1", "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", - "@honeybadger-io/js": "6.15.0", + "@honeybadger-io/js": "6.15.3", "@hono/node-server": "2.0.10", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 768ae4ba7c78..1b3f5798544e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 33.0.0 version: 33.0.0 '@honeybadger-io/js': - specifier: 6.15.0 - version: 6.15.0 + specifier: 6.15.3 + version: 6.15.3 '@hono/node-server': specifier: 2.0.10 version: 2.0.10(hono@4.12.31) @@ -1084,12 +1084,12 @@ packages: resolution: {integrity: sha512-PW3SdoZ8ULzW0We0lgplyZHjuXJtOdRvU5RL6ErNUlcTeSqjoHtk1pWdHKTwk3brPuIiqUB26K7fX84VroDqRg==} engines: {node: '>=12.0.0'} - '@honeybadger-io/core@6.10.0': - resolution: {integrity: sha512-U3hVsd/l55SCV5s7pkYZeOZcMx5dRevnrPcF99AZyjGxsnIbT4+GcCdmd5vfrvzw23SFM/0U40zwd5shR3ohsw==} + '@honeybadger-io/core@6.10.2': + resolution: {integrity: sha512-iLj5fmz6v5+ry3+Guv2WFTxZ9Bz1KJUhBYme/uwQfSKguNH4ylgzOnvvoBX8uGDcGaOGSBu3L9OedP6fi1yGYg==} engines: {node: '>=14'} - '@honeybadger-io/js@6.15.0': - resolution: {integrity: sha512-MYBdQuaq/8tJ+w75+H9CnVRZrZKl0DiBSvu3hkS7w6tGVqJaIW6uDptg9MC/ltOgIvEECQWCy5Db9j+sXxcScg==} + '@honeybadger-io/js@6.15.3': + resolution: {integrity: sha512-SQxuR9JAAqxQx6uglT6NICIxMGiCw7KmP1ULmUsRKEWQ1yykY+LlMW14DarH591+AEjAehp8EPiBwBH6l4xu8g==} engines: {node: '>=14'} hasBin: true @@ -6941,14 +6941,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@honeybadger-io/core@6.10.0': + '@honeybadger-io/core@6.10.2': dependencies: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.15.0': + '@honeybadger-io/js@6.15.3': dependencies: - '@honeybadger-io/core': 6.10.0 + '@honeybadger-io/core': 6.10.2 '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 From 36ed36ab8e13a83b9a86e80ed3c54930360cfd9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 08:26:51 +0000 Subject: [PATCH 0371/1190] chore(deps): bump @hono/node-server from 2.0.10 to 2.0.11 (#22780) Bumps [@hono/node-server](https://github.com/honojs/node-server) from 2.0.10 to 2.0.11. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v2.0.10...v2.0.11) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 2.0.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4696ec466054..1a4942673416 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.15.3", - "@hono/node-server": "2.0.10", + "@hono/node-server": "2.0.11", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b3f5798544e..dede323f2443 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 6.15.3 version: 6.15.3 '@hono/node-server': - specifier: 2.0.10 - version: 2.0.10(hono@4.12.31) + specifier: 2.0.11 + version: 2.0.11(hono@4.12.31) '@hono/zod-openapi': specifier: 1.5.1 version: 1.5.1(hono@4.12.31)(zod@4.4.3) @@ -1093,8 +1093,8 @@ packages: engines: {node: '>=14'} hasBin: true - '@hono/node-server@2.0.10': - resolution: {integrity: sha512-ZcnNVhKTmyDJeg0UlnZjvM73JBsTAuhrH/J4fjwGOw59PwOW51r4J+p6CsKZWXdKSme4MFqU62CZMOsdDrU4CA==} + '@hono/node-server@2.0.11': + resolution: {integrity: sha512-bjD221KPLoJTWUwso1J6fGKiTXEUFedG/s0visavY4zakFPkeGURMRNly+FhBHs7T8Dz4qHaZIMX9ZoJHSJtKA==} engines: {node: '>=20'} peerDependencies: hono: ^4 @@ -6952,7 +6952,7 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.10(hono@4.12.31)': + '@hono/node-server@2.0.11(hono@4.12.31)': dependencies: hono: 4.12.31 From b9c797de3346d7ffd44cddca153b4eba6e43a72b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:55:22 +0800 Subject: [PATCH 0372/1190] chore(deps): bump actions/labeler from 6.2.0 to 7.0.0 (#22774) Bumps [actions/labeler](https://github.com/actions/labeler) from 6.2.0 to 7.0.0. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/b8dd2d9be0f68b860e7dae5dae7d772984eacd6d...bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8a06cac56831..a22f82fa636e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-slim timeout-minutes: 5 steps: - - uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0 + - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} sync-labels: true From b4848ef7e4ab63608e1a65ef5853e7c8415674f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:00:12 +0800 Subject: [PATCH 0373/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22775) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260719.1 to 5.20260721.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260721.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 1a4942673416..5d87924b51e6 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.6", - "@cloudflare/workers-types": "5.20260719.1", + "@cloudflare/workers-types": "5.20260721.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dede323f2443..4e551dab8fc2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.6 - version: 0.18.6(@cloudflare/workers-types@5.20260719.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.6(@cloudflare/workers-types@5.20260721.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260719.1 - version: 5.20260719.1 + specifier: 5.20260721.1 + version: 5.20260721.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.112.0 - version: 4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -641,8 +641,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260719.1': - resolution: {integrity: sha512-DcGasbfUuczQilc80vhL2MPPdWcaxWkx0hN5IW9UdNDBdrRvWcal3akSJ6Ccm7e8+/OGeR+8tYrqkykA3YGSZw==} + '@cloudflare/workers-types@5.20260721.1': + resolution: {integrity: sha512-J6HZRuQOP3gVe9G5rxHQVS8kQRjo7NIaF+Lz4kO2lVmaCkQ1E78APOOthaI7KyCQzp+A2NbXBQI6QLRIswdWbA==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3669,7 +3669,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -6623,7 +6623,7 @@ snapshots: optionalDependencies: workerd: 1.20260714.1 - '@cloudflare/vitest-pool-workers@0.18.6(@cloudflare/workers-types@5.20260719.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.6(@cloudflare/workers-types@5.20260721.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6631,7 +6631,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6653,7 +6653,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260714.1': optional: true - '@cloudflare/workers-types@5.20260719.1': {} + '@cloudflare/workers-types@5.20260721.1': {} '@colors/colors@1.6.0': {} @@ -12098,7 +12098,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260714.1 '@cloudflare/workerd-windows-64': 1.20260714.1 - wrangler@4.112.0(@cloudflare/workers-types@5.20260719.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260714.1) @@ -12109,7 +12109,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260714.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260719.1 + '@cloudflare/workers-types': 5.20260721.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From e449e9509422b27b109bb683a493af9e6f7c25fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:03:37 +0000 Subject: [PATCH 0374/1190] chore(deps-dev): bump the typescript-eslint group with 2 updates (#22776) Bumps the typescript-eslint group with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 8.64.0 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.64.0 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.65.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint - dependency-name: "@typescript-eslint/parser" dependency-version: 8.65.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 118 ++++++++++++++++++++++++------------------------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index 5d87924b51e6..4f5f1556f6ae 100644 --- a/package.json +++ b/package.json @@ -169,8 +169,8 @@ "@types/module-alias": "2.0.4", "@types/node": "26.1.1", "@types/sanitize-html": "2.16.1", - "@typescript-eslint/eslint-plugin": "8.64.0", - "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", "@vercel/nft": "1.10.2", "@vitest/coverage-v8": "4.1.10", "discord-api-types": "0.38.50", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e551dab8fc2..0c5933058f31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,11 +357,11 @@ importers: specifier: 2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: 8.64.0 - version: 8.64.0(@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) + specifier: 8.65.0 + version: 8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@typescript-eslint/parser': - specifier: 8.64.0 - version: 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + specifier: 8.65.0 + version: 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) '@vercel/nft': specifier: 1.10.2 version: 1.10.2(rollup@4.62.2) @@ -2797,39 +2797,39 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.64.0': - resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==} + '@typescript-eslint/eslint-plugin@8.65.0': + resolution: {integrity: sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.64.0 + '@typescript-eslint/parser': ^8.65.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.64.0': - resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==} + '@typescript-eslint/parser@8.65.0': + resolution: {integrity: sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.64.0': - resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} + '@typescript-eslint/project-service@8.65.0': + resolution: {integrity: sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.64.0': - resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} + '@typescript-eslint/scope-manager@8.65.0': + resolution: {integrity: sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.64.0': - resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} + '@typescript-eslint/tsconfig-utils@8.65.0': + resolution: {integrity: sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.64.0': - resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==} + '@typescript-eslint/type-utils@8.65.0': + resolution: {integrity: sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2839,25 +2839,25 @@ packages: resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.64.0': - resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} + '@typescript-eslint/types@8.65.0': + resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.64.0': - resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} + '@typescript-eslint/typescript-estree@8.65.0': + resolution: {integrity: sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.64.0': - resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} + '@typescript-eslint/utils@8.65.0': + resolution: {integrity: sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.64.0': - resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} + '@typescript-eslint/visitor-keys@8.65.0': + resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': @@ -8242,14 +8242,14 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/scope-manager': 8.64.0 - '@typescript-eslint/type-utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/visitor-keys': 8.64.0 + '@typescript-eslint/parser': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/type-utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/visitor-keys': 8.65.0 eslint: 10.7.0 ignore: 7.0.6 natural-compare: 1.4.0 @@ -8258,41 +8258,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: - '@typescript-eslint/scope-manager': 8.64.0 - '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/visitor-keys': 8.64.0 + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/visitor-keys': 8.65.0 debug: 4.4.3 eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.64.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.65.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.64.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/tsconfig-utils': 8.65.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.65.0 debug: 4.4.3 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.64.0': + '@typescript-eslint/scope-manager@8.65.0': dependencies: - '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/visitor-keys': 8.64.0 + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/visitor-keys': 8.65.0 - '@typescript-eslint/tsconfig-utils@8.64.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/tsconfig-utils@8.65.0(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/type-utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: - '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) debug: 4.4.3 eslint: 10.7.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8302,14 +8302,14 @@ snapshots: '@typescript-eslint/types@8.63.0': {} - '@typescript-eslint/types@8.64.0': {} + '@typescript-eslint/types@8.65.0': {} - '@typescript-eslint/typescript-estree@8.64.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@8.65.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/project-service': 8.64.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/tsconfig-utils': 8.64.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/visitor-keys': 8.64.0 + '@typescript-eslint/project-service': 8.65.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.65.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/visitor-keys': 8.65.0 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -8319,20 +8319,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.64.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) - '@typescript-eslint/scope-manager': 8.64.0 - '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/scope-manager': 8.65.0 + '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) eslint: 10.7.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.64.0': + '@typescript-eslint/visitor-keys@8.65.0': dependencies: - '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/types': 8.65.0 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': From e238dae4ce9adaf683466cac1c5af8b8ef329d3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:10:54 +0000 Subject: [PATCH 0375/1190] chore(deps): bump devenv from `5f1cf17` to `29fcc8a` (#22782) Bumps [devenv](https://github.com/cachix/devenv) from `5f1cf17` to `29fcc8a`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/5f1cf17be0fc48689bd0ecb810de6d2e06d259a1...29fcc8a9a778c2e3f822ef961cc9667122e79dd5) --- updated-dependencies: - dependency-name: devenv dependency-version: 29fcc8a9a778c2e3f822ef961cc9667122e79dd5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9c645e227eb6..49abb8ee7546 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784325378, - "narHash": "sha256-Gof6j4d43yX2qSLLp78JILke346IggDFIxTgl3ecVQE=", + "lastModified": 1784586378, + "narHash": "sha256-F+K6FU/IBSsaxDu23imNkcpJzMbzYXNjn7bBP/22N3I=", "owner": "cachix", "repo": "devenv", - "rev": "5f1cf17be0fc48689bd0ecb810de6d2e06d259a1", + "rev": "29fcc8a9a778c2e3f822ef961cc9667122e79dd5", "type": "github" }, "original": { From c1dd27180d14bc02d9eacff45a62776a13a8ebff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:13:41 +0800 Subject: [PATCH 0376/1190] chore(deps): bump @sentry/node from 10.66.0 to 10.67.0 (#22777) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.66.0 to 10.67.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.66.0...10.67.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.67.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 71 +++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 4f5f1556f6ae..343ba94feed5 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.11.11", - "@sentry/node": "10.66.0", + "@sentry/node": "10.67.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c5933058f31..8d6fe5daef31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,8 +86,8 @@ importers: specifier: 0.11.11 version: 0.11.11(hono@4.12.31) '@sentry/node': - specifier: 10.66.0 - version: 10.66.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) + specifier: 10.67.0 + version: 10.67.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -497,8 +497,8 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@apm-js-collab/code-transformer-bundler-plugins@0.6.2': - resolution: {integrity: sha512-5vBrtIEL+UVbO0YWWoyYG4QMgR+ZfnIL3xlteIkAmU7YaAPhc28k3md/NM14tnkfXKjKOn9yUzEA9AYUmNpvJg==} + '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': + resolution: {integrity: sha512-Yidf5GOl60db80UxUtNdKK3pnY7obU/gs0xOfA0SCdnvVLMCvfYIer/egC3TqpPiT0Jg22eg3RlzcO+zKfPMcA==} engines: {node: '>=18.0.0'} '@apm-js-collab/code-transformer@0.18.0': @@ -2586,12 +2586,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.66.0': - resolution: {integrity: sha512-9UbgSvds7bMJsP561eWmeyMLcfOmnwxtnx2QuW3yLobzP2Ob7CyJCOzP4tGzlTAGDrzShkFEZhiyuBUKiEK2oQ==} + '@sentry/core@10.67.0': + resolution: {integrity: sha512-b6U3pJ8AUvN9aouq0vl+VZI8KT8RslBsfGMFuNwRr313zOmdmFJBZqTiUw9VGgJ2jGKxLO9alm9rlxBfX4hf+w==} engines: {node: '>=18'} - '@sentry/node-core@10.66.0': - resolution: {integrity: sha512-SUnXHROqSdSetKgZC1goDEKCuMz3OmQ1h4rxzWeexLyqan+pensZXfLouP6jzZXlA8e/HP7uQg78LnfqYDcZlQ==} + '@sentry/node-core@10.67.0': + resolution: {integrity: sha512-dBHHRwZyan1pOnFJ+sNBvR8TkXbZAfZU/jpxmALS3JZ2/8AGR7cQKL+b7SleKuJ7iUDZyklN3Nqi0i5JkcA+HA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2611,20 +2611,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.66.0': - resolution: {integrity: sha512-5Ow7iQiRjaSaEOmqEIkYV368hFFzShIZCXPoj+wX3JtOmKFrsNu6lr8/VJlQs7cyjFS6tzE50PrLrD8iAPS/8w==} + '@sentry/node@10.67.0': + resolution: {integrity: sha512-SFKpZGqOCEFSmP93NdDP6ikZp4NS7A/JR8+2ofK3jF6Y9Vyox7pX0pxdOnPLpFcPtMybMahfWqSAWJvsFs4RmA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.66.0': - resolution: {integrity: sha512-K5Y9IettN9yIOnpqCs40HRLGqaGUoaQ50+ZsLqX2kPCk3TzJVpKZ9icKwaJ4Nxm72QgGVT5Ovfr/9FXbgd3b/Q==} + '@sentry/opentelemetry@10.67.0': + resolution: {integrity: sha512-oLTOrAK1rOqmYRktOJZwz37B1seXPx1W2FTMVtzTVNjMFA/LZwGzePeZzhUOgzZgfLHixMd/ceWtGqoxAndcjQ==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.66.0': - resolution: {integrity: sha512-h9EM9Wz9Mc6w2Vn7Fyh6ozjy4JC2UbUvWf9Ra1HYA4KMeYqjFA5/o0oB4pg4w/TF+rb+9OF/HNqxjuczxpudpA==} + '@sentry/server-utils@10.67.0': + resolution: {integrity: sha512-GQ9t+RSTx5s3b/aZrLFuL4nrwPLMah5NiZk5cjxJmgmOSgm3nMdO8gdqCedDch5B13F3YsxtaQYjSJnzLz8M5A==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -4345,8 +4345,8 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@3.3.1: - resolution: {integrity: sha512-0rymlHSFLwZ0ixx8DaQkoIyZojJPY2a0K2nEYslhKJ6jIYO/m0IcCb7iQsFPmS7WmKwISZiIrv5Icstrw/CmqA==} + import-in-the-middle@3.3.2: + resolution: {integrity: sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==} engines: {node: '>=18'} import-without-cache@0.4.0: @@ -6497,7 +6497,7 @@ snapshots: '@actions/io@3.0.2': {} - '@apm-js-collab/code-transformer-bundler-plugins@0.6.2': + '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': dependencies: '@apm-js-collab/code-transformer': 0.18.0 es-module-lexer: 2.3.1 @@ -7361,7 +7361,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 - import-in-the-middle: 3.3.1 + import-in-the-middle: 3.3.2 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -8006,16 +8006,16 @@ snapshots: '@sentry/conventions@0.16.0': {} - '@sentry/core@10.66.0': + '@sentry/core@10.67.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.66.0 - '@sentry/opentelemetry': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - import-in-the-middle: 3.3.1 + '@sentry/core': 10.67.0 + '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + import-in-the-middle: 3.3.2 optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) @@ -8023,37 +8023,36 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.66.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.67.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.66.0 - '@sentry/node-core': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.66.0 - import-in-the-middle: 3.3.1 + '@sentry/core': 10.67.0 + '@sentry/node-core': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.67.0 + import-in-the-middle: 3.3.2 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.66.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.66.0 + '@sentry/core': 10.67.0 - '@sentry/server-utils@10.66.0': + '@sentry/server-utils@10.67.0': dependencies: - '@apm-js-collab/code-transformer': 0.18.0 - '@apm-js-collab/code-transformer-bundler-plugins': 0.6.2 + '@apm-js-collab/code-transformer-bundler-plugins': 0.7.1 '@apm-js-collab/tracing-hooks': 0.13.0 '@sentry/conventions': 0.16.0 - '@sentry/core': 10.66.0 + '@sentry/core': 10.67.0 transitivePeerDependencies: - supports-color @@ -9824,7 +9823,7 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@3.3.1: + import-in-the-middle@3.3.2: dependencies: cjs-module-lexer: 2.2.0 es-module-lexer: 2.3.1 From 8bc0674c9316da3bf36c03f2ddf045eb68add0a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:19:49 +0800 Subject: [PATCH 0377/1190] chore(deps): bump undici from 8.7.0 to 8.8.0 (#22781) Bumps [undici](https://github.com/nodejs/undici) from 8.7.0 to 8.8.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.7.0...v8.8.0) --- updated-dependencies: - dependency-name: undici dependency-version: 8.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 343ba94feed5..703f0e9e146a 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "tsx": "4.23.1", "twitter-api-v2": "1.29.0", "ufo": "1.6.4", - "undici": "8.7.0", + "undici": "8.8.0", "uuid": "14.0.1", "winston": "3.19.0", "xxhash-wasm": "1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d6fe5daef31..1f5f495f1e33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,7 +135,7 @@ importers: version: 10.0.0 http-cookie-agent: specifier: 8.0.0 - version: 8.0.0(tough-cookie@6.0.2)(undici@8.7.0) + version: 8.0.0(tough-cookie@6.0.2)(undici@8.8.0) https-proxy-agent: specifier: 9.1.0 version: 9.1.0 @@ -257,8 +257,8 @@ importers: specifier: 1.6.4 version: 1.6.4 undici: - specifier: 8.7.0 - version: 8.7.0 + specifier: 8.8.0 + version: 8.8.0 uuid: specifier: 14.0.1 version: 14.0.1 @@ -421,7 +421,7 @@ importers: version: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 - version: 1.0.30(undici@8.7.0)(utf-8-validate@5.0.10) + version: 1.0.30(undici@8.8.0)(utf-8-validate@5.0.10) oxc-parser: specifier: 0.140.0 version: 0.140.0 @@ -6045,8 +6045,8 @@ packages: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} - undici@8.7.0: - resolution: {integrity: sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ==} + undici@8.8.0: + resolution: {integrity: sha512-ubshXMXwF3MQIMF1y/WxZdNBnjEKeSg2wF5mcGUtU55YTw34tnVVpKRlLf7ruDXZ5344KokPVX4RBx1wJm64Bw==} engines: {node: '>=22.19.0'} unenv@2.0.0-rc.24: @@ -9729,12 +9729,12 @@ snapshots: http-cache-semantics@4.2.0: {} - http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.7.0): + http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.8.0): dependencies: agent-base: 9.0.0 tough-cookie: 6.0.2 optionalDependencies: - undici: 8.7.0 + undici: 8.8.0 http-proxy-agent@9.1.0: dependencies: @@ -10692,13 +10692,13 @@ snapshots: dependencies: write-file-atomic: 1.3.4 - node-network-devtools@1.0.30(undici@8.7.0)(utf-8-validate@5.0.10): + node-network-devtools@1.0.30(undici@8.8.0)(utf-8-validate@5.0.10): dependencies: bufferutil: 4.1.0 iconv-lite: 0.7.3 inspector: 0.5.0 open: 8.4.2 - undici: 8.7.0 + undici: 8.8.0 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -11837,7 +11837,7 @@ snapshots: undici@7.28.0: {} - undici@8.7.0: {} + undici@8.8.0: {} unenv@2.0.0-rc.24: dependencies: From 016e0f7dd002566f88286cf4b3a4aac4d5971911 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:22:59 +0000 Subject: [PATCH 0378/1190] chore(deps): bump nixpkgs from `61b7c44` to `241313f` (#22783) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `61b7c44` to `241313f`. - [Commits](https://github.com/NixOS/nixpkgs/compare/61b7c44c4073f0b827768aff0049561b5110ea5a...241313f4e8e508cb9b13278c2b0fa25b9ca27163) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: 241313f4e8e508cb9b13278c2b0fa25b9ca27163 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 49abb8ee7546..b3add4a311a9 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784356753, - "narHash": "sha256-12KrbMiWLcf8m7pCvAtZh1ZrgF85ZXDXvfR/fWTKy84=", + "lastModified": 1784497964, + "narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "61b7c44c4073f0b827768aff0049561b5110ea5a", + "rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", "type": "github" }, "original": { From 737cf6063bf1f57d4086213b7b941bb5c69cfd53 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Wed, 22 Jul 2026 01:21:36 +0800 Subject: [PATCH 0379/1190] feat(route): add Guandong Museum Exhibitions and news route (#22729) * feat:add Guandong Museum Exhibitions and news * fix: update selector --- lib/routes/gdmuseum/exhibition.tsx | 126 +++++++++++++++++++++++++++++ lib/routes/gdmuseum/namespace.ts | 10 +++ lib/routes/gdmuseum/news.ts | 58 +++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 lib/routes/gdmuseum/exhibition.tsx create mode 100644 lib/routes/gdmuseum/namespace.ts create mode 100644 lib/routes/gdmuseum/news.ts diff --git a/lib/routes/gdmuseum/exhibition.tsx b/lib/routes/gdmuseum/exhibition.tsx new file mode 100644 index 000000000000..b42baebd3fbf --- /dev/null +++ b/lib/routes/gdmuseum/exhibition.tsx @@ -0,0 +1,126 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import type { Context } from 'hono'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +// convert exhibition date string to YYYY-MM-DD format, e.g. "2023年5月1日" or "2023/5/1" => "2023-05-01" +const formatExhibitionDate = (d?: string) => { + if (!d) { + return; + } + return dayjs(d.replaceAll(/[年月/.]/g, '-').replaceAll('日', '')).format('YYYY-MM-DD'); +}; + +const parseExhibitionDuration = (duration?: string) => { + const allDates = duration?.match(/\d{4}[./年-]\d{1,2}[./月-]\d{1,2}日?/g) || []; + return { + startDate: formatExhibitionDate(allDates[0]), + endDate: formatExhibitionDate(allDates[1]), + }; +}; + +export const route: Route = { + path: '/exhibition/:type?', + categories: ['travel'], + example: '/gdmuseum/exhibition/temp', + parameters: { + type: 'Exhibition type, supported values: temp(临时展览), default: All exhibitions.', + }, + name: 'Current Exhibitions', + maintainers: ['magazian'], + radar: [ + { + source: ['www.gdmuseum.org.cn/col9/list'], + target: '/exhibition', + }, + ], + handler: async (ctx: Context) => { + const typeParam = ctx.req.param('type') || 'all'; + + const museumName = namespace.zh?.name || namespace.name; + const baseUrl = 'https://www.gdmuseum.org.cn'; + const url = `${baseUrl}/col9/list`; + + const response = await got(url); + const $ = load(response.data); + + const list = $('.ULLIST li a[href^="/cn/col"]') + .toArray() + .map((el) => { + const $item = $(el); + const title = $item.find('.divtt.qui-dot').text(); + + if (typeParam === 'temp') { + if (title.includes('常设展览')) { + return null; + } + } else if (typeParam !== 'all') { + return null; + } + + const rawLink = $item.attr('href'); + const itemLink = rawLink ? new URL(rawLink, baseUrl).href : ''; + + const rawSrc = $item.find('img').attr('src'); + const imgUrl = rawSrc ? new URL(rawSrc, baseUrl).href : undefined; + + const textDurationAndLocation = $item.find('.quitxt.qui-dot').text().trim(); + const [fullDuration = '', location = ''] = textDurationAndLocation.split('|').map((p) => p.trim()); + + const { startDate, endDate } = parseExhibitionDuration(fullDuration); + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( +
    + {imgUrl && } +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + + return { + title, + link: itemLink, + pubDate, + description, + // For further .ics file processing + _extra: { + museumName, + location, + startDate, + endDate, + }, + } as DataItem; + }) + .filter((i): i is DataItem => i !== null); + + return { + title: `${museumName} - 正在热展${typeParam === 'temp' ? ' - 临时展览' : ''}`, + link: url, + language: 'zh-CN', + item: list, + }; + }, +}; diff --git a/lib/routes/gdmuseum/namespace.ts b/lib/routes/gdmuseum/namespace.ts new file mode 100644 index 000000000000..c37b3988de5d --- /dev/null +++ b/lib/routes/gdmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Guangdong Museum', + url: 'gdmuseum.org.cn', + + zh: { + name: '广东省博物馆', + }, +}; diff --git a/lib/routes/gdmuseum/news.ts b/lib/routes/gdmuseum/news.ts new file mode 100644 index 000000000000..2271f9b77144 --- /dev/null +++ b/lib/routes/gdmuseum/news.ts @@ -0,0 +1,58 @@ +import { load } from 'cheerio'; + +import type { DataItem, Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +export const route: Route = { + path: '/information', + categories: ['travel'], + example: '/gdmuseum/information', + name: 'Information', + maintainers: ['magazian'], + radar: [ + { + source: ['www.gdmuseum.org.cn/cn/col51/list'], + target: '/information', + }, + ], + handler: async () => { + const baseUrl = 'https://www.gdmuseum.org.cn'; + const apiUrl = `${baseUrl}/cn/col51/list`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got(apiUrl); + const $ = load(response.data); + + const list = $('.ULLIST li a[href^="/cn/col"]') + .toArray() + .map((el) => { + const $item = $(el); + + const rawLink = $item.attr('href') || ''; + const itemLink = new URL(rawLink, baseUrl).href; + + const title = $item.find('h3.h3.qui-dot').text(); + const day = $item.find('time b').text().trim(); + const yearMonth = $item.find('time i').text().trim(); + + const pubDate = timezone(parseDate(`${yearMonth}-${day}`), 8); + + return { + title, + link: itemLink, + pubDate, + } as DataItem; + }); + + return { + title: `${museumName} - 公告`, + link: apiUrl, + language: 'zh-CN', + item: list, + }; + }, +}; From 0eb05b56a9055c5a36aae0169f2da1aee71f2de7 Mon Sep 17 00:00:00 2001 From: Ananya <84459091+ananyatimalsina@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:00:44 +0200 Subject: [PATCH 0380/1190] fix(route/pawchive): add enclosure support and fix media URLs (#22739) * fix(route): pawchive - add enclosure support and fix media URLs * Address github-advanced-security problems * Change namespace to pawchive.pw * Fix pawchive domain in radar --- lib/routes/pawchive/const.ts | 12 ++++++-- lib/routes/pawchive/index.tsx | 52 +++++++++++++++++++++++++------- lib/routes/pawchive/namespace.ts | 2 +- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/lib/routes/pawchive/const.ts b/lib/routes/pawchive/const.ts index b56caaf74030..f060dd1b6b2a 100644 --- a/lib/routes/pawchive/const.ts +++ b/lib/routes/pawchive/const.ts @@ -1,4 +1,10 @@ -export const baseUrl = 'https://pawchive.st'; +export const baseUrl = 'https://pawchive.pw'; export const apiBaseUrl = `${baseUrl}/api/v1`; -export const thumbnailUrl = 'https://img.pawchive.st/thumbnail/data'; -export const fileUrl = 'https://file.pawchive.st/data'; +export const thumbnailUrl = 'https://img.pawchive.pw/thumbnail/data'; +export const fileUrl = 'https://file.pawchive.pw/data'; + +export const MIME_TYPE_MAP = { + m4a: 'audio/mp4', + mp3: 'audio/mpeg', + mp4: 'video/mp4', +} as const; diff --git a/lib/routes/pawchive/index.tsx b/lib/routes/pawchive/index.tsx index 9403b57190b4..5228b75e1bb5 100644 --- a/lib/routes/pawchive/index.tsx +++ b/lib/routes/pawchive/index.tsx @@ -1,3 +1,4 @@ +import { load } from 'cheerio'; import type { Context } from 'hono'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; @@ -7,9 +8,34 @@ import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { apiBaseUrl, baseUrl, fileUrl, thumbnailUrl } from './const'; +import { apiBaseUrl, baseUrl, fileUrl, MIME_TYPE_MAP, thumbnailUrl } from './const'; import type { PawchiveFile, PawchivePost } from './types'; +function generateEnclosureInfo(htmlContent: string): { enclosure_url?: string; enclosure_type?: string } { + const $ = load(htmlContent); + let enclosureInfo = {}; + + $('audio source, video source').each((_, el) => { + const src = $(el).attr('src'); + if (!src) { + return; + } + + const extension = src.replace(/.*\./, '').toLowerCase(); + const mimeType = MIME_TYPE_MAP[extension as keyof typeof MIME_TYPE_MAP]; + + if (mimeType) { + enclosureInfo = { + enclosure_url: src, + enclosure_type: mimeType, + }; + return false; + } + }); + + return enclosureInfo; +} + export const route: Route = { path: '/:service/:id', categories: ['anime'], @@ -29,11 +55,11 @@ export const route: Route = { }, radar: [ { - source: ['pawchive.st/'], + source: ['pawchive.pw/'], target: '', }, { - source: ['pawchive.st/:service/user/:id'], + source: ['pawchive.pw/:service/user/:id'], target: '/:service/:id', }, ], @@ -119,14 +145,18 @@ async function handler(ctx: Context) { return data.name || 'Unknown User'; })) as Promise; - const items = response.map((post) => ({ - title: post.title || 'Untitled Post', - description: render(post, processPostFiles(post)), - author, - pubDate: parseDate(post.published), - link: `${baseUrl}/${post.service}/user/${post.user}/post/${post.id}`, - guid: `pawchive:${post.service}:${post.user}:post:${post.id}`, - })); + const items = response.map((post) => { + const description = render(post, processPostFiles(post)); + return { + title: post.title || 'Untitled Post', + description, + author, + pubDate: parseDate(post.published), + link: `${baseUrl}/${post.service}/user/${post.user}/post/${post.id}`, + guid: `pawchive:${post.service}:${post.user}:post:${post.id}`, + ...generateEnclosureInfo(description), + }; + }); return { title: `Posts of ${author} from ${service} | Pawchive`, diff --git a/lib/routes/pawchive/namespace.ts b/lib/routes/pawchive/namespace.ts index 127e5537d921..3ee8cf763c7d 100644 --- a/lib/routes/pawchive/namespace.ts +++ b/lib/routes/pawchive/namespace.ts @@ -2,6 +2,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Pawchive', - url: 'pawchive.st', + url: 'pawchive.pw', lang: 'en', }; From a0a388b51b06d2c6a8c33f2ee6db96d71142d65d Mon Sep 17 00:00:00 2001 From: aaro-n <45537680+aaro-n@users.noreply.github.com> Date: Wed, 22 Jul 2026 03:12:50 +0800 Subject: [PATCH 0381/1190] fix(route/xiaoyuzhou): add POST body to editor-pick/list request (#22759) * fix(route/xiaoyuzhou): add POST body to editor-pick/list request * docs: add back route metadata --- lib/routes/xiaoyuzhou/pickup.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/routes/xiaoyuzhou/pickup.ts b/lib/routes/xiaoyuzhou/pickup.ts index 44843a06304c..c84883f49906 100644 --- a/lib/routes/xiaoyuzhou/pickup.ts +++ b/lib/routes/xiaoyuzhou/pickup.ts @@ -42,6 +42,7 @@ const ProcessFeed = async () => { ...headers, 'x-jike-access-token': token_updated.data['x-jike-access-token'], }, + json: {}, }); const data = response.data.data; @@ -87,10 +88,15 @@ export const route: Route = { target: '', }, ], - name: 'Unknown', + name: '发现', + example: '/xiaoyuzhou', + categories: ['multimedia'], maintainers: ['prnake', 'Maecenas'], handler, url: 'xiaoyuzhoufm.com/', + description: `::: warning +小宇宙的 api 需要验证 \`x-jike-device-id\`、\`x-jike-access-token\` 和 \`x-jike-refresh-token\` 。必要时需要自行配置,具体见部署文档。 +:::`, }; async function handler() { From 2c5929347a8283005df2a7b771f40ee324c38cfe Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 22 Jul 2026 04:43:03 +0800 Subject: [PATCH 0382/1190] fix(route/daily): remove innerSharedContent parameter (#22786) --- lib/routes/daily/discussed.ts | 20 +++++++++----------- lib/routes/daily/popular.ts | 14 +++----------- lib/routes/daily/source.ts | 14 +++----------- lib/routes/daily/squads.ts | 16 +++------------- lib/routes/daily/upvoted.ts | 14 +++----------- lib/routes/daily/user.ts | 15 ++------------- lib/routes/daily/utils.tsx | 22 +++++++--------------- 7 files changed, 30 insertions(+), 85 deletions(-) diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts index 6b012d0c88e9..1baec6df5a98 100644 --- a/lib/routes/daily/discussed.ts +++ b/lib/routes/daily/discussed.ts @@ -21,6 +21,13 @@ const query = /* GraphQL */ ` fragment FeedPost on Post { ...SharedPostInfo + type + sharedPost { + title + summary + image + permalink + } } fragment SharedPostInfo on Post { @@ -51,7 +58,7 @@ const query = /* GraphQL */ ` `; export const route: Route = { - path: '/discussed/:period?/:innerSharedContent?/:dateSort?', + path: '/discussed/:period?/:dateSort?', example: '/daily/discussed/30', view: ViewType.Articles, radar: [ @@ -64,14 +71,6 @@ export const route: Route = { handler, url: 'app.daily.dev/discussed', parameters: { - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, dateSort: { description: 'Sort posts by publication date instead of popularity', default: 'true', @@ -94,7 +93,6 @@ export const route: Route = { async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; const period = ctx.req.param('period') ? Number(ctx.req.param('period')) : 7; @@ -108,7 +106,7 @@ async function handler(ctx) { period, }, }); - const items = getList(data, innerSharedContent, dateSort); + const items = getList(data, dateSort); return { title: 'Real-time discussions in the developer community | daily.dev', diff --git a/lib/routes/daily/popular.ts b/lib/routes/daily/popular.ts index 8b48490beb64..d4331342a3ce 100644 --- a/lib/routes/daily/popular.ts +++ b/lib/routes/daily/popular.ts @@ -29,6 +29,7 @@ const query = /* GraphQL */ ` sharedPost { id title + summary image readTime permalink @@ -110,7 +111,7 @@ const query = /* GraphQL */ ` `; export const route: Route = { - path: '/popular/:innerSharedContent?/:dateSort?', + path: '/popular/:dateSort?', example: '/daily/popular', view: ViewType.Articles, radar: [ @@ -119,14 +120,6 @@ export const route: Route = { }, ], parameters: { - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, dateSort: { description: 'Sort posts by publication date instead of popularity', default: 'true', @@ -145,7 +138,6 @@ export const route: Route = { async function handler(ctx) { const link = `${baseUrl}/posts`; const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 15; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; const data = await getData({ @@ -156,7 +148,7 @@ async function handler(ctx) { first: limit, }, }); - const items = getList(data, innerSharedContent, dateSort); + const items = getList(data, dateSort); return { title: 'Popular posts on daily.dev', diff --git a/lib/routes/daily/source.ts b/lib/routes/daily/source.ts index 104b5fdebe64..3f06cbb2eaf8 100644 --- a/lib/routes/daily/source.ts +++ b/lib/routes/daily/source.ts @@ -42,6 +42,7 @@ const sourceFeedQuery = /* GraphQL */ ` sharedPost { id title + summary image readTime permalink @@ -121,18 +122,10 @@ const sourceFeedQuery = /* GraphQL */ ` `; export const route: Route = { - path: '/source/:sourceId/:innerSharedContent?', + path: '/source/:sourceId', example: '/daily/source/hn', parameters: { sourceId: 'The source id', - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, }, radar: [ { @@ -148,7 +141,6 @@ export const route: Route = { async function handler(ctx) { const sourceId = ctx.req.param('sourceId'); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const link = `${baseUrl}/sources/${sourceId}`; const buildId = await getBuildId(); @@ -172,7 +164,7 @@ async function handler(ctx) { loggedIn: false, }, }); - return getList(edges, innerSharedContent, true); + return getList(edges, true); }, config.cache.routeExpire, false diff --git a/lib/routes/daily/squads.ts b/lib/routes/daily/squads.ts index c71880192536..783316d90072 100644 --- a/lib/routes/daily/squads.ts +++ b/lib/routes/daily/squads.ts @@ -107,6 +107,7 @@ const query = /* GraphQL */ ` sharedPost { id title + summary image readTime permalink @@ -188,19 +189,9 @@ const query = /* GraphQL */ ` `; export const route: Route = { - path: '/squads/:squads/:innerSharedContent?', + path: '/squads/:squads', example: '/daily/squads/watercooler', view: ViewType.Articles, - parameters: { - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, - }, radar: [ { source: ['app.daily.dev/squads/:squads'], @@ -214,7 +205,6 @@ export const route: Route = { async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const squads = ctx.req.param('squads'); const link = `${baseUrl}/squads/${squads}`; @@ -239,7 +229,7 @@ async function handler(ctx) { first: limit, }, }); - const items = getList(data, innerSharedContent, true); + const items = getList(data, true); return { title: `${name} - daily.dev`, diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts index 43f9f27238e3..5f17c2f34175 100644 --- a/lib/routes/daily/upvoted.ts +++ b/lib/routes/daily/upvoted.ts @@ -29,6 +29,7 @@ const query = /* GraphQL */ ` sharedPost { id title + summary image readTime permalink @@ -110,7 +111,7 @@ const query = /* GraphQL */ ` `; export const route: Route = { - path: '/upvoted/:period?/:innerSharedContent?/:dateSort?', + path: '/upvoted/:period?/:dateSort?', example: '/daily/upvoted/7', view: ViewType.Articles, radar: [ @@ -119,14 +120,6 @@ export const route: Route = { }, ], parameters: { - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, dateSort: { description: 'Sort posts by publication date instead of popularity', default: 'true', @@ -154,7 +147,6 @@ export const route: Route = { async function handler(ctx) { const link = `${baseUrl}/posts/upvoted`; const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const dateSort = ctx.req.param('dateSort') ? JSON.parse(ctx.req.param('dateSort')) : true; const period = ctx.req.param('period') ? Number(ctx.req.param('period')) : 7; @@ -166,7 +158,7 @@ async function handler(ctx) { first: limit, }, }); - const items = getList(data, innerSharedContent, dateSort); + const items = getList(data, dateSort); return { title: 'Most upvoted posts for developers | daily.dev', diff --git a/lib/routes/daily/user.ts b/lib/routes/daily/user.ts index d3eda7ba1698..8e7eca68e6ea 100644 --- a/lib/routes/daily/user.ts +++ b/lib/routes/daily/user.ts @@ -135,23 +135,13 @@ const userPostQuery = /* GraphQL */ ` `; export const route: Route = { - path: '/user/:userId/:innerSharedContent?', + path: '/user/:userId', example: '/daily/user/kramer', radar: [ { source: ['app.daily.dev/:userId/posts', 'app.daily.dev/:userId'], }, ], - parameters: { - innerSharedContent: { - description: 'Where to Fetch inner Shared Posts instead of original', - default: 'false', - options: [ - { value: 'false', label: 'False' }, - { value: 'true', label: 'True' }, - ], - }, - }, name: 'User Posts', maintainers: ['TonyRL'], handler, @@ -161,7 +151,6 @@ export const route: Route = { async function handler(ctx) { const userId = ctx.req.param('userId'); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 7; - const innerSharedContent = ctx.req.param('innerSharedContent') ? JSON.parse(ctx.req.param('innerSharedContent')) : false; const buildId = await getBuildId(); const userData = await cache.tryGet(`daily:user:${userId}`, async () => { @@ -185,7 +174,7 @@ async function handler(ctx) { loggedIn: false, }, }); - return getList(edges, innerSharedContent, true); + return getList(edges, true); }, config.cache.routeExpire, false diff --git a/lib/routes/daily/utils.tsx b/lib/routes/daily/utils.tsx index fb1fbfcf8274..bcc8bf9717f3 100644 --- a/lib/routes/daily/utils.tsx +++ b/lib/routes/daily/utils.tsx @@ -46,29 +46,21 @@ const render = ({ image, content }: { image?: string; content?: string }) => ); -export const getList = (edges, innerSharedContent: boolean, dateSort: boolean) => +export const getList = (edges, dateSort: boolean) => edges.map(({ node }) => { - let link: string; - let title: string; - if (innerSharedContent && node.type === 'share') { - link = node.sharedPost.permalink; - title = node.sharedPost.title; - } else { - link = node.commentsPermalink ?? node.permalink; - title = node.title; - } + const post = node.type === 'share' ? node.sharedPost : node; return { id: node.id, - title, - link, + title: post.title, + link: node.commentsPermalink ?? node.permalink, guid: node.permalink, description: render({ - image: node.image, - content: node.contentHtml?.replaceAll('\n', '
    ') ?? node.summary, + image: post.image?.includes('/public/Placeholder') ? undefined : post.image, + content: node.contentHtml?.replaceAll('\n', '
    ') ?? post.summary, }), author: node.author?.name, - itunes_item_image: node.image, + itunes_item_image: post.image, pubDate: dateSort ? parseDate(node.createdAt) : '', upvotes: node.numUpvotes, comments: node.numComments, From e3e96490029d3b5bd513561518f36b4040a2969e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:17:14 +0000 Subject: [PATCH 0383/1190] chore(deps): bump imapflow from 1.4.7 to 1.4.8 (#22795) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.4.7 to 1.4.8. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.4.7...v1.4.8) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.4.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 703f0e9e146a..2ada9ba6bb31 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.4.7", + "imapflow": "1.4.8", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f5f495f1e33..ec9b7f37eb69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.4.7 - version: 1.4.7 + specifier: 1.4.8 + version: 1.4.8 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -3669,7 +3669,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -4338,8 +4338,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.4.7: - resolution: {integrity: sha512-nK03WfN16inwm0//0Q70T21G0g4H9ArMVyzKDRjAshOVV8iw71PP9HTpAXNmWB9giZWfPiS+ltHByi37cqwSgg==} + imapflow@1.4.8: + resolution: {integrity: sha512-cHa23g7h0X04a2v+H9luLg0GWMQ0vApmQ+UxL5swglOgBypNpqxurLMa1JS9mjpTekCsQhKrCZwaziPRBbHp8Q==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -5327,8 +5327,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.20: - resolution: {integrity: sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==} + postcss@8.5.21: + resolution: {integrity: sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -9806,7 +9806,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.4.7: + imapflow@1.4.8: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 @@ -11045,7 +11045,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.20: + postcss@8.5.21: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -11974,7 +11974,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.20 + postcss: 8.5.21 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: From 86a3168bfcc2111ab0276f00b8de90bbc71de6dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:17:27 +0000 Subject: [PATCH 0384/1190] chore(deps-dev): bump lint-staged from 17.1.0 to 17.1.1 (#22793) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 17.1.0 to 17.1.1. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.1.0...v17.1.1) --- updated-dependencies: - dependency-name: lint-staged dependency-version: 17.1.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 2ada9ba6bb31..14bddc0bad02 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "globals": "17.7.0", "husky": "9.1.7", "js-beautify": "2.0.3", - "lint-staged": "17.1.0", + "lint-staged": "17.1.1", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec9b7f37eb69..8a7b769a1a1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -411,8 +411,8 @@ importers: specifier: 2.0.3 version: 2.0.3 lint-staged: - specifier: 17.1.0 - version: 17.1.0 + specifier: 17.1.1 + version: 17.1.1 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -4700,8 +4700,8 @@ packages: linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} - lint-staged@17.1.0: - resolution: {integrity: sha512-d7UQRu/9ZPgfu4+hu/k0wny5GEaIxo+2jb2LJqQDkE7cHRTm1HGqNUDq5UOwsGPpjpaNAFmgAsYo3TR+i9cSJw==} + lint-staged@17.1.1: + resolution: {integrity: sha512-FnHWpSe5cPRtrDG+soOuNdBxb4XQb2gN5EqpEWKdweyqyOfpl4QSjbrz3ilcIf0WXmkiNQGZZRQ23R5YtB3TEw==} engines: {node: '>=22.22.1'} hasBin: true @@ -10165,7 +10165,7 @@ snapshots: dependencies: uc.micro: 2.1.0 - lint-staged@17.1.0: + lint-staged@17.1.1: dependencies: picomatch: 4.0.5 string-argv: 0.3.2 From 82769b801735ddeeec4b2541eb30d6f2b91385d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:18:24 +0000 Subject: [PATCH 0385/1190] chore(deps-dev): bump tsdown from 0.22.12 to 0.22.13 (#22794) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.12 to 0.22.13. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.12...v0.22.13) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.13 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 232 ++++++++++++++++++++++++------------------------- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/package.json b/package.json index 14bddc0bad02..5520cb422b77 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.12", + "tsdown": "0.22.13", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8a7b769a1a1e..e4cacae62a28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -450,8 +450,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.12 - version: 0.22.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) + specifier: 0.22.13 + version: 0.22.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -3027,130 +3027,130 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.7.0': - resolution: {integrity: sha512-RLfjSuJUolQJ04zYq3kM2vSUk9BkFFSOhmOWARb7Pc7Gnf0vMLfj/fepnn9IdsyE2FsJjoCJPKM5bBze4ld5pQ==} + '@yuku-codegen/binding-darwin-arm64@0.7.3': + resolution: {integrity: sha512-hbssEr7iLNCWWdUbEt8cuOjUQ9loI+U/BVmdhNQ4CV1wAFGvWDO3niK9JD2WwTC9iib7E498qgZ3L/xJnoSb3A==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.7.0': - resolution: {integrity: sha512-OI9z6U69j2TvaWgDzheUNlMPSrlNQs8PD3isfyuGxQVbO8Dqi3F7PRT1JnG7pkId+IKvmEu+40sjXWDHRbtlMA==} + '@yuku-codegen/binding-darwin-x64@0.7.3': + resolution: {integrity: sha512-WqwST3vVcNBTd1zaT1vP6pU8NkjJlLWN37Kqomc2c6TG9eky6sbAfHIhZEjSSyqFrMqsAt3mIu0jQZAODi401g==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.7.0': - resolution: {integrity: sha512-nhoH3yXu2e6itB1Hbpj1+kZJ6yTJtsXBN5dWYm20TKvs1Iq79di6hdCKBlHE12RHFW39aQKcnn+vlFF1J1RGsw==} + '@yuku-codegen/binding-freebsd-x64@0.7.3': + resolution: {integrity: sha512-mCci4UPkZXYflXuHnPGl5sDsotBPLaBryFylDwZcqposktmumoOBIKp2gpzEmxQL/XlGdVWksmpYw9IRY43FNQ==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.7.0': - resolution: {integrity: sha512-bgmapvrk/f/1bOSl+XA9KTLKb7vmxuXhqgCchlhTvgNOWnHB4rPLfzu0TkQU5CjVFzZQmHoEmhObf9DKUzcDMA==} + '@yuku-codegen/binding-linux-arm-gnu@0.7.3': + resolution: {integrity: sha512-F1QIaRH5SeahrYjZVrVvIb5nHCZQMF7+YWAy6yTJh0Y2r2wLgqOQuhbOWcoKk+AyBiTOO2nAsFHNpVqRq2GjGw==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.7.0': - resolution: {integrity: sha512-HpVSId+bxtqSkIsbBUP78J0j4ZUnCvqj14WGmAjKDP25oaQ0SZuFtVIZJmTisuKVSM4AIx1CVBYlJ2CCIm/vsQ==} + '@yuku-codegen/binding-linux-arm-musl@0.7.3': + resolution: {integrity: sha512-JvkWNmN8MFbboX/5grRsAldGaB8ArRFgAaUYfQoohsqcuJZee8SLtc6itvwuntPbN9MGStn4GzdWk2lmSURqbA==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.7.0': - resolution: {integrity: sha512-oL8OGWo99U0arObIl5UKov3ZvsoNGWUMWRJrlXP99P4LpVz+gZC9KwfYCzxlhGnNk9rpp/fcTUUHYHbTskuZwQ==} + '@yuku-codegen/binding-linux-arm64-gnu@0.7.3': + resolution: {integrity: sha512-DUnMTE/HCA7j1BAipWclGZCYfLZ/4SV5lldmV2kDmVMIywcw4PhtQ4Rfd/tO1K82JkjhQ+4a8XApcOIksOacnQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.7.0': - resolution: {integrity: sha512-ULsFt9PnI5vBMCG5pGir1YQtJ03o0Sb5SsRTDYRSeKaVaxdog+kA9Guwnk8q8OMPFsI3s40Fhu/+45cQXHSZ8Q==} + '@yuku-codegen/binding-linux-arm64-musl@0.7.3': + resolution: {integrity: sha512-pNnXMnm9dlqd3V8C7nHaXgZnMLwP/CyM8B4mSAnkqVlhj2t3b8CMjesEV85SCbFlagzJk8FapfSghfxcXKuBow==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.7.0': - resolution: {integrity: sha512-l1TP6G39gnF4eiHEApjViyA12n+YHT18A0y3FDUy1jF5hkZt2RWlBNrU4HPhzTd6kQlozJEWyjhVNja2J3/eBw==} + '@yuku-codegen/binding-linux-x64-gnu@0.7.3': + resolution: {integrity: sha512-o5GRppYvYvPl4UJbgRSZpXXDh6rOWQzX0yLB+tDzv31T7mOuLitU6zgQZxzw5rwhPJDffCt/AVKMbtizEo+UtA==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.7.0': - resolution: {integrity: sha512-h8GZdSNSaBWySA4p8eYFWkH8OWdqA4peOAFeWs+DltJo8r9ZXSvVB0XBypMxkLwA0qLDqwtWoVssg2LK1zJKdw==} + '@yuku-codegen/binding-linux-x64-musl@0.7.3': + resolution: {integrity: sha512-GdiyLD1bM3lhTUeGkA9ZZLjqU+xt5uVihgPm0e6TdBjCi6DMZORWe02oDlRbn9OZaV7AZosngXAq2jLk3TrBEw==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.7.0': - resolution: {integrity: sha512-hVkxv4UTPXex7q5ldvqelSMvvYc6bCAYlDSMMg3wmttHZF9yQzPb4yYw69eWvR4coEMFaUv63ADJNjiGjNxkYg==} + '@yuku-codegen/binding-win32-arm64@0.7.3': + resolution: {integrity: sha512-uQrxQDBQFIAUgHJIxcB/FAAUk0Wkyj3xGpHrp/C258nXnCTAH0KDaqyK5RN/TCkVYrpH+uCl+rGnwDLSGnPBkQ==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.7.0': - resolution: {integrity: sha512-g3YQpqvsTbDHjjBDnNGhaO4ejN+m2GpsWc50dcGPR/RUx+yt++uDEXKSEyiAaCpEM2p5PuDIW/YEjXx0oVdsoQ==} + '@yuku-codegen/binding-win32-x64@0.7.3': + resolution: {integrity: sha512-UoF7tqMnVMUmIXPgDjFLhRezea6HaypwLDWNenCFxfJCzQNEa41lcHpxq1AN2Xl6GLzIDfAb+lQDi/O+zPCfyg==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.7.0': - resolution: {integrity: sha512-LoZ945MxFzc6+ltMenaFtXhJ4rdj11j1IwkRWLR7WPim2jdXUURTfWhAm7VzQDtSCHtnDz8PAH55eIHmNilTlA==} + '@yuku-parser/binding-darwin-arm64@0.7.3': + resolution: {integrity: sha512-JcFQSNEnjtyHQRjXO0G5rQt5xHq5MR+9nHqh+wt5MP30XIiccUYlcMIa3s7CBmj8E8WPGZC08k7LNGrA1OISTA==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.7.0': - resolution: {integrity: sha512-HVS2bgNGqCl5oU5wP16tZUMmXAO1avgxp/uzDOJcNqA7WOlV1PqTWD3P/1GXaMAijIZNu3VLSXkU1dovwwPpVA==} + '@yuku-parser/binding-darwin-x64@0.7.3': + resolution: {integrity: sha512-U0kNeI3VygP/+8sTOLTeLTjUyTYZfl9Wuq6xVKWPXHb2K7oI0sm8JXJ5xlhAbkUDQRi0N6yK5TKxS0sQT5M6UQ==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.7.0': - resolution: {integrity: sha512-nVZgLmiuEdkRb8oJsXBoQphfZwsMTcatSEB5t1QL9aH92/hx2TxAGayZy/g326l5rGVMmieCHc9FYxu/MnVFHQ==} + '@yuku-parser/binding-freebsd-x64@0.7.3': + resolution: {integrity: sha512-Xc08FQTvxovy6pX+Co9fgv3N1H0OpLpph/ClbwnJkcdphY3kzN2GgIfir35kpPp8mOzlQgtAVQdeDyAYyjwx4w==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.7.0': - resolution: {integrity: sha512-rIq85RCqwMQFtQSBIvUbmEAkNw0pu89PsrikSt+uVMCxjN1ZjbekTWw4hi0NVax8kAb5t2Xgs6kQrFoapyQ3Xg==} + '@yuku-parser/binding-linux-arm-gnu@0.7.3': + resolution: {integrity: sha512-6bHeDiUd+0bmoJJ5wZVG+PcJkXQUdjy/AzHsOVcOSUtHtRTX2H3Z5RIHyPAh5OveLtT4RHyqaO3fEQGnHGX5/Q==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.7.0': - resolution: {integrity: sha512-BNoI0mP8o3iV6dJEgfJq1U6cAMn7iSYe3gSYkN05DL1FyOU6ni2NwcDScsV6RBBQHj4V1s5123F85JLPhhXfqA==} + '@yuku-parser/binding-linux-arm-musl@0.7.3': + resolution: {integrity: sha512-trGERNLJGvXkkyvdWBKspTOBATd4lcmpPFPcy5HI5kC3rC3ZMHQDJ356OMSVUsR2NQnl++F0ZTZQucao9hrbeQ==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.7.0': - resolution: {integrity: sha512-YrFNrrc0bq5B+cV2EuxDU4VPhuH0RHkV0M1rne8UTxPbqjPmarWxLcl1JEeyaXcL0856kwnaV728GMeB1o/Gdg==} + '@yuku-parser/binding-linux-arm64-gnu@0.7.3': + resolution: {integrity: sha512-p1ELmzhqAX7SFUvH/tR5zSb8NsWZQ8ulw7PqatfXNmJMy34bkivtL0z2jPpVoMJXf5o2+TwBN29Z31CLL0wvkA==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.7.0': - resolution: {integrity: sha512-WaDi6BsjZ/vrO7EgX36C4sLN9fUPZd/vM0Nh+82uOjygCxgFtiRf5NsFxdaLSzqMi+sbGsg+hWM/De4vkana3A==} + '@yuku-parser/binding-linux-arm64-musl@0.7.3': + resolution: {integrity: sha512-dZ79SrJ002ZXfBDmpQ47SF+06Q5bGOjFeoSK8xO97ter/bjLBgxSO4d7i9hhf+uj4xkrTPc/OFNVaeBAF3L4dA==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.7.0': - resolution: {integrity: sha512-gxlbaqglGr7ir9UZLF0945JKTImI9MFDZiny57mRiqktCTDVQPnb5Lmj0okKJ6w0nBX1NzwviMX6v4i+rwLSWw==} + '@yuku-parser/binding-linux-x64-gnu@0.7.3': + resolution: {integrity: sha512-LjT64hVGWWbOmOYS+Fd8qScgyRxgUbudhFJbw8aeUVnhiNR1np36KnE0VFWrJeu7rTdNxuzVyV5o9JLqhBDTVQ==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.7.0': - resolution: {integrity: sha512-n7pCgW5iNoaKEpt8K3UTkg7ACX87MueWkJQD7cQSOgxyu/Qx0BCdwi0iOs60Gjj88wyKwqkYIBPMN3wKE5P7Yw==} + '@yuku-parser/binding-linux-x64-musl@0.7.3': + resolution: {integrity: sha512-5Nw3v5BqYbOi0ZdV+SImgVYa0KDBVAY/ZNPilTsJJU4phqa2cqEW4+aMgyieE/4bqpojt/B931kM+7pipazP9g==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.7.0': - resolution: {integrity: sha512-kyuC7W1mGIMbJp6t86hIhDjnptFxZiTaOrbHhYkCpfsXPF6pszWNK03DUMc//Udnw3Af3d5w93CRJlRNpCb5eg==} + '@yuku-parser/binding-win32-arm64@0.7.3': + resolution: {integrity: sha512-lFYXgHiq8tJKa6/e1/q8Su+IJVV4/CjoXbIJ7/GdPXPp9Hx0gh/8HqmGSsrwlY2w8/ohBcar9wxjm1rQ8D16bg==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.7.0': - resolution: {integrity: sha512-xEm5rwtETud7iNbxSocACgXzPrv2x4vkk339BtAqZAKoCS+edaO767EKAGcUO3xo8STBgCud1cAA5oIyzN8APA==} + '@yuku-parser/binding-win32-x64@0.7.3': + resolution: {integrity: sha512-wy/fs4OrHBhKW8LDyZJRMyMQ3QOfbSQDp0WtMbtzWCVui/vTQEUJMGnwvRCppjchS/qIQfbWzIw/HSjogJqZFA==} cpu: [x64] os: [win32] - '@yuku-toolchain/types@0.7.0': - resolution: {integrity: sha512-kKleXJmXcZnJ5LUDTeYvK350SB+BXdpoHUwT78GGyOXOhCZ0tWf+seDPAvVnCrjoJhf+FhqtR5HRUfWW+yILQw==} + '@yuku-toolchain/types@0.7.3': + resolution: {integrity: sha512-ezarjq3dcl8Nx3iJ9VeAc7vhzvHyRoSvr7bLX76T+ljRq73IbZ11X2RFCblfK6YxW2DsjkzU6MPnr3JWDYqYYQ==} '@zone-eu/mailsplit@5.4.14': resolution: {integrity: sha512-rz0FQOhN3Vq1XrSeSSa9+dPcaFbBxmQPjiZm6zS9oxdVHV7rOWIAYX3yP2YAUf0qBncY8CI+NogzPCmMVrMXcw==} @@ -5921,14 +5921,14 @@ packages: typescript: optional: true - tsdown@0.22.12: - resolution: {integrity: sha512-IzGRfGwSsufXJWOcQ0tmECKMG/dbxhUwDOySTS7JE1AM8pkB9+bpcTPs8bTxxSNrSvGocSczrBlOh97tZObq9Q==} + tsdown@0.22.13: + resolution: {integrity: sha512-XaYFhtiKRUvTpXv/YAehsHdbEb3LN/iMlzjSINbjlaATtXN2zVPKox2STKhcyFPlh++8Zg7suNN27E679IfAUA==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.12 - '@tsdown/exe': 0.22.12 + '@tsdown/css': 0.22.13 + '@tsdown/exe': 0.22.13 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6446,14 +6446,14 @@ packages: youtubei.js@17.2.0: resolution: {integrity: sha512-XLNsgRKO1h7t4i9tIMWSQSeWdD7Ujkk5v1m5YCaumaHMhu/xuLqtO3M0Hq7CXNup9HlJ1NGrT1Y+HLIHnL6Ujg==} - yuku-ast@0.7.0: - resolution: {integrity: sha512-W5UzqYg/Xuyz41cAyxwo/TIPpDX221OuC9U5f44+NulDAHDwtzvGE3M3Xz3mdcLEutWOmTc/WP/y7NO6ANA2gw==} + yuku-ast@0.7.3: + resolution: {integrity: sha512-occyAXtcU4hcl5UPEclWwLqz4J1ZAV8Us4LllmTqAe1YjL5aMbh3GCpzHt6O0sOF+dwBI3BZAcFQqbyX1RiaWg==} - yuku-codegen@0.7.0: - resolution: {integrity: sha512-RJgoLaIU2AGKRkUHqMtw6gQhYm+NXZm/AFnfn+5YAHgRfApIc/PNJABJHihHoxWltayjbwEOCa68zqxdJafgfA==} + yuku-codegen@0.7.3: + resolution: {integrity: sha512-hhyJW0TIEwm4kex8XVCS4CZIXHS/3TWWNUum+95Ji5/4W+iaLfUnvwSxJwyNfTzS8cxmd1np+EZ4b8ObLguKEA==} - yuku-parser@0.7.0: - resolution: {integrity: sha512-72HXJhlPOolJN4ER0xZy1wtAeWZEG4uXfZnzEm2uD7yAWt32VE/52FwIfVGi2Hs2P0JRZK2+sPwULQMTuEzYtw==} + yuku-parser@0.7.3: + resolution: {integrity: sha512-5kZ7HR+W+OsNpVtZ9LHruQsgmcoJl4TETrffPq2GbliThsFiy+zHzbMLmSxQJrokzyJqfJ/TPfFkdUDOodJCgA==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -8473,73 +8473,73 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.7.0': + '@yuku-codegen/binding-darwin-arm64@0.7.3': optional: true - '@yuku-codegen/binding-darwin-x64@0.7.0': + '@yuku-codegen/binding-darwin-x64@0.7.3': optional: true - '@yuku-codegen/binding-freebsd-x64@0.7.0': + '@yuku-codegen/binding-freebsd-x64@0.7.3': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.7.0': + '@yuku-codegen/binding-linux-arm-gnu@0.7.3': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.7.0': + '@yuku-codegen/binding-linux-arm-musl@0.7.3': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.7.0': + '@yuku-codegen/binding-linux-arm64-gnu@0.7.3': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.7.0': + '@yuku-codegen/binding-linux-arm64-musl@0.7.3': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.7.0': + '@yuku-codegen/binding-linux-x64-gnu@0.7.3': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.7.0': + '@yuku-codegen/binding-linux-x64-musl@0.7.3': optional: true - '@yuku-codegen/binding-win32-arm64@0.7.0': + '@yuku-codegen/binding-win32-arm64@0.7.3': optional: true - '@yuku-codegen/binding-win32-x64@0.7.0': + '@yuku-codegen/binding-win32-x64@0.7.3': optional: true - '@yuku-parser/binding-darwin-arm64@0.7.0': + '@yuku-parser/binding-darwin-arm64@0.7.3': optional: true - '@yuku-parser/binding-darwin-x64@0.7.0': + '@yuku-parser/binding-darwin-x64@0.7.3': optional: true - '@yuku-parser/binding-freebsd-x64@0.7.0': + '@yuku-parser/binding-freebsd-x64@0.7.3': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.7.0': + '@yuku-parser/binding-linux-arm-gnu@0.7.3': optional: true - '@yuku-parser/binding-linux-arm-musl@0.7.0': + '@yuku-parser/binding-linux-arm-musl@0.7.3': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.7.0': + '@yuku-parser/binding-linux-arm64-gnu@0.7.3': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.7.0': + '@yuku-parser/binding-linux-arm64-musl@0.7.3': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.7.0': + '@yuku-parser/binding-linux-x64-gnu@0.7.3': optional: true - '@yuku-parser/binding-linux-x64-musl@0.7.0': + '@yuku-parser/binding-linux-x64-musl@0.7.3': optional: true - '@yuku-parser/binding-win32-arm64@0.7.0': + '@yuku-parser/binding-win32-arm64@0.7.3': optional: true - '@yuku-parser/binding-win32-x64@0.7.0': + '@yuku-parser/binding-win32-x64@0.7.3': optional: true - '@yuku-toolchain/types@0.7.0': {} + '@yuku-toolchain/types@0.7.3': {} '@zone-eu/mailsplit@5.4.14': dependencies: @@ -11296,9 +11296,9 @@ snapshots: get-tsconfig: 5.0.0-beta.5 obug: 2.1.4 rolldown: 1.2.0 - yuku-ast: 0.7.0 - yuku-codegen: 0.7.0 - yuku-parser: 0.7.0 + yuku-ast: 0.7.3 + yuku-codegen: 0.7.3 + yuku-parser: 0.7.3 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11722,7 +11722,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): + tsdown@0.22.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -12225,42 +12225,42 @@ snapshots: fflate: 0.8.3 meriyah: 6.1.4 - yuku-ast@0.7.0: + yuku-ast@0.7.3: dependencies: - '@yuku-toolchain/types': 0.7.0 + '@yuku-toolchain/types': 0.7.3 - yuku-codegen@0.7.0: + yuku-codegen@0.7.3: dependencies: - '@yuku-toolchain/types': 0.7.0 + '@yuku-toolchain/types': 0.7.3 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.7.0 - '@yuku-codegen/binding-darwin-x64': 0.7.0 - '@yuku-codegen/binding-freebsd-x64': 0.7.0 - '@yuku-codegen/binding-linux-arm-gnu': 0.7.0 - '@yuku-codegen/binding-linux-arm-musl': 0.7.0 - '@yuku-codegen/binding-linux-arm64-gnu': 0.7.0 - '@yuku-codegen/binding-linux-arm64-musl': 0.7.0 - '@yuku-codegen/binding-linux-x64-gnu': 0.7.0 - '@yuku-codegen/binding-linux-x64-musl': 0.7.0 - '@yuku-codegen/binding-win32-arm64': 0.7.0 - '@yuku-codegen/binding-win32-x64': 0.7.0 - - yuku-parser@0.7.0: - dependencies: - '@yuku-toolchain/types': 0.7.0 - yuku-ast: 0.7.0 + '@yuku-codegen/binding-darwin-arm64': 0.7.3 + '@yuku-codegen/binding-darwin-x64': 0.7.3 + '@yuku-codegen/binding-freebsd-x64': 0.7.3 + '@yuku-codegen/binding-linux-arm-gnu': 0.7.3 + '@yuku-codegen/binding-linux-arm-musl': 0.7.3 + '@yuku-codegen/binding-linux-arm64-gnu': 0.7.3 + '@yuku-codegen/binding-linux-arm64-musl': 0.7.3 + '@yuku-codegen/binding-linux-x64-gnu': 0.7.3 + '@yuku-codegen/binding-linux-x64-musl': 0.7.3 + '@yuku-codegen/binding-win32-arm64': 0.7.3 + '@yuku-codegen/binding-win32-x64': 0.7.3 + + yuku-parser@0.7.3: + dependencies: + '@yuku-toolchain/types': 0.7.3 + yuku-ast: 0.7.3 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.7.0 - '@yuku-parser/binding-darwin-x64': 0.7.0 - '@yuku-parser/binding-freebsd-x64': 0.7.0 - '@yuku-parser/binding-linux-arm-gnu': 0.7.0 - '@yuku-parser/binding-linux-arm-musl': 0.7.0 - '@yuku-parser/binding-linux-arm64-gnu': 0.7.0 - '@yuku-parser/binding-linux-arm64-musl': 0.7.0 - '@yuku-parser/binding-linux-x64-gnu': 0.7.0 - '@yuku-parser/binding-linux-x64-musl': 0.7.0 - '@yuku-parser/binding-win32-arm64': 0.7.0 - '@yuku-parser/binding-win32-x64': 0.7.0 + '@yuku-parser/binding-darwin-arm64': 0.7.3 + '@yuku-parser/binding-darwin-x64': 0.7.3 + '@yuku-parser/binding-freebsd-x64': 0.7.3 + '@yuku-parser/binding-linux-arm-gnu': 0.7.3 + '@yuku-parser/binding-linux-arm-musl': 0.7.3 + '@yuku-parser/binding-linux-arm64-gnu': 0.7.3 + '@yuku-parser/binding-linux-arm64-musl': 0.7.3 + '@yuku-parser/binding-linux-x64-gnu': 0.7.3 + '@yuku-parser/binding-linux-x64-musl': 0.7.3 + '@yuku-parser/binding-win32-arm64': 0.7.3 + '@yuku-parser/binding-win32-x64': 0.7.3 zod@3.25.76: {} From 34dd80b3963b0876f8c87c2c32387304b363a633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:01:17 +0000 Subject: [PATCH 0386/1190] chore(deps-dev): bump the cloudflare group with 3 updates (#22789) Bumps the cloudflare group with 3 updates: [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-pool-workers` from 0.18.6 to 0.18.7 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.18.7/packages/vitest-pool-workers) Updates `@cloudflare/workers-types` from 5.20260721.1 to 5.20260722.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.112.0 to 4.113.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.113.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-pool-workers" dependency-version: 0.18.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260722.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.113.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 102 ++++++++++++++++++++++++------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 5520cb422b77..f7f7625da9ff 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", - "@cloudflare/vitest-pool-workers": "0.18.6", - "@cloudflare/workers-types": "5.20260721.1", + "@cloudflare/vitest-pool-workers": "0.18.7", + "@cloudflare/workers-types": "5.20260722.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.74.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", - "wrangler": "4.112.0", + "wrangler": "4.113.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4cacae62a28..c360b475983e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,11 +294,11 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/vitest-pool-workers': - specifier: 0.18.6 - version: 0.18.6(@cloudflare/workers-types@5.20260721.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + specifier: 0.18.7 + version: 0.18.7(@cloudflare/workers-types@5.20260722.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260721.1 - version: 5.20260721.1 + specifier: 5.20260722.1 + version: 5.20260722.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -471,8 +471,8 @@ importers: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: - specifier: 4.112.0 - version: 4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.113.0 + version: 4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -604,45 +604,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.18.6': - resolution: {integrity: sha512-6JGqaQsQRZIVq/6jEC4ouJnShZriPIJ2X0yGndwMm+SiPP93pJi5Dp30dYAoztNrNJC7wWK7ec5slLfMBMZ8jA==} + '@cloudflare/vitest-pool-workers@0.18.7': + resolution: {integrity: sha512-PGYToiFoGpuRV2Uh33S4fI7JcmSkDxk6LQeneYtgfsITEkVi5iGxc3Vms6yW9Jr6uSzVK4Be9XZfdyZDr5GWYw==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260714.1': - resolution: {integrity: sha512-ZWXqAN8G7Cx9hMRQuk+59ziJhR3j1F4iO+Qs8aHdfKZ3Dq5Yi/57xvkJTgCGBnW1YU/L78r8f6HEy51bwbTpNw==} + '@cloudflare/workerd-darwin-64@1.20260721.1': + resolution: {integrity: sha512-VivNMhiEdZIB4JBWxf1RMJGROErv53qmQ+dvhjA1evrCouvqRYW718VqDideU3PSV7Ythl5Df48NqZYWoaEHpQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260714.1': - resolution: {integrity: sha512-tueWxWC3wyCbMG6zRAxsMXX0YLgrRWbiAPYFQ2uJ7dUH8G+5E7UTWaQS9B1HdJ0bpKFW1NWxhs1o2noKVFSUYg==} + '@cloudflare/workerd-darwin-arm64@1.20260721.1': + resolution: {integrity: sha512-k7oye1ZiuwnnBBA2eTMduconr/ud5ZxFtRNTsYwMdmJeeeislw2+M72otrHxxvybCP7JWPPlJ38uhfajpcyhOA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260714.1': - resolution: {integrity: sha512-1VChTZRb0l0F7R4e1G5RtLKV4oFi6x+rQgxh2+yu887j3l/3TLgatuv1L8/5zhc9gKEhATTxOh0e52Rtd9dDWQ==} + '@cloudflare/workerd-linux-64@1.20260721.1': + resolution: {integrity: sha512-hon0lW4ZQ4boAVgaw+0ZFTNS8v5MWPWvK0HZnt4tDpKYnDUviLZawtUW3KqvFmCQTipVHl1S34j3J8Eqb93hGQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260714.1': - resolution: {integrity: sha512-rMm3G+NirG2UdgHIRDdF1asNC6FqgIzZzkRG+VDhhDGcVxAQwvrMT1E38BivEvHr3G04MB4AfhcOczX0+GtRkQ==} + '@cloudflare/workerd-linux-arm64@1.20260721.1': + resolution: {integrity: sha512-nAl+HRQqpX5b7xVwWcvLPZmCk8NQ2yjI0yvJTWcHiRswbMEg1ZZckVmjJUAn0PHzZARbCSyIV7v3UjM+SPRmIQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260714.1': - resolution: {integrity: sha512-cGqnU3Hg2YZS/k3SAqrMp1DjpdsyFde72tWltdl6ZT9+SFz/Zrk/8gyTU1TcxC4YApXeNVH5TyU5cOGPgUJ0pg==} + '@cloudflare/workerd-windows-64@1.20260721.1': + resolution: {integrity: sha512-9paFG5cMTKz/CRixnEEnZbe5uvFPBFSDthxJHANfCWhUtBj49GSL1FPIokIg+Q+H8DGJEExU0lL92LtxD0lTxQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260721.1': - resolution: {integrity: sha512-J6HZRuQOP3gVe9G5rxHQVS8kQRjo7NIaF+Lz4kO2lVmaCkQ1E78APOOthaI7KyCQzp+A2NbXBQI6QLRIswdWbA==} + '@cloudflare/workers-types@5.20260722.1': + resolution: {integrity: sha512-8+kivCgFGzwrAfNOWgSpzy/VDvmT/i5KWBgQhnygv3d1kajNn6mCYTbLKpouG0aY8mXjhv+IQm1a8r2K/H4pqQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4940,8 +4940,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260714.0: - resolution: {integrity: sha512-MYlTCLdWCPqvrYY2uLwOjXwmglXuiHE3TGGkbOW4BwjUPa1r07E0iuHwrNDIs/sxK21r+o90Jx58AV2KeNdJZw==} + miniflare@4.20260721.0: + resolution: {integrity: sha512-fBLaCxZ2i/nPH8iyLzvza0C8/sSF4sjD1ma1Skf+pkZVK0TlaW5ujHJlUHwcwR66v2JZt+Q28d4DCX/oaLG0cA==} engines: {node: '>=22.0.0'} hasBin: true @@ -6321,17 +6321,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260714.1: - resolution: {integrity: sha512-oIbQzfdyl9UQUnG6XLegcSq0Mgt/7WKDbFOoqGgOWCS+/fhyGB460uKEgdAQQ9RHCO/ttcNCX/KiMIQzdoeu3Q==} + workerd@1.20260721.1: + resolution: {integrity: sha512-b/DWhpV0jTudzQpLhDovcOgBz233386q+3Hbari7CLCNT9UXxjQziSTZ9yCoKdT2K3TSx5jrwlOisq8hlLWXYg==} engines: {node: '>=16'} hasBin: true - wrangler@4.112.0: - resolution: {integrity: sha512-5H+XUD0TySCv1LuktFHDIEOkboH2nTfQs+35L+USt3MtntjDTMVIJprLgQcL2WBjulOyjxpd1vyTiSTJVW5MjQ==} + wrangler@4.113.0: + resolution: {integrity: sha512-ROGzSloJv0y21It6Oc9LaruNcu1tdiQ/XzL3Jc3YkFjzXEMXzTqVhA8vQaGMTdZHTjFP0PVcwAHNgaw3gXu4wA==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260714.1 + '@cloudflare/workers-types': ^5.20260721.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6617,43 +6617,43 @@ snapshots: '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260714.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260721.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260714.1 + workerd: 1.20260721.1 - '@cloudflare/vitest-pool-workers@0.18.6(@cloudflare/workers-types@5.20260721.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.7(@cloudflare/workers-types@5.20260722.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260714.1': + '@cloudflare/workerd-darwin-64@1.20260721.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260714.1': + '@cloudflare/workerd-darwin-arm64@1.20260721.1': optional: true - '@cloudflare/workerd-linux-64@1.20260714.1': + '@cloudflare/workerd-linux-64@1.20260721.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260714.1': + '@cloudflare/workerd-linux-arm64@1.20260721.1': optional: true - '@cloudflare/workerd-windows-64@1.20260714.1': + '@cloudflare/workerd-windows-64@1.20260721.1': optional: true - '@cloudflare/workers-types@5.20260721.1': {} + '@cloudflare/workers-types@5.20260722.1': {} '@colors/colors@1.6.0': {} @@ -10586,12 +10586,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.28.0 - workerd: 1.20260714.1 + workerd: 1.20260721.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -12089,26 +12089,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260714.1: + workerd@1.20260721.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260714.1 - '@cloudflare/workerd-darwin-arm64': 1.20260714.1 - '@cloudflare/workerd-linux-64': 1.20260714.1 - '@cloudflare/workerd-linux-arm64': 1.20260714.1 - '@cloudflare/workerd-windows-64': 1.20260714.1 + '@cloudflare/workerd-darwin-64': 1.20260721.1 + '@cloudflare/workerd-darwin-arm64': 1.20260721.1 + '@cloudflare/workerd-linux-64': 1.20260721.1 + '@cloudflare/workerd-linux-arm64': 1.20260721.1 + '@cloudflare/workerd-windows-64': 1.20260721.1 - wrangler@4.112.0(@cloudflare/workers-types@5.20260721.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260714.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260721.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260714.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260714.1 + workerd: 1.20260721.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260721.1 + '@cloudflare/workers-types': 5.20260722.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From dd23111183ff0b15fd1ddd51cde25729fcd86957 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:13:15 +0800 Subject: [PATCH 0387/1190] chore(deps): bump devenv from `29fcc8a` to `ef185b5` (#22792) Bumps [devenv](https://github.com/cachix/devenv) from `29fcc8a` to `ef185b5`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/29fcc8a9a778c2e3f822ef961cc9667122e79dd5...ef185b5a38445777194bc8469fc1cdebd99e7dbc) --- updated-dependencies: - dependency-name: devenv dependency-version: ef185b5a38445777194bc8469fc1cdebd99e7dbc dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index b3add4a311a9..c72b5d9330b6 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784586378, - "narHash": "sha256-F+K6FU/IBSsaxDu23imNkcpJzMbzYXNjn7bBP/22N3I=", + "lastModified": 1784696310, + "narHash": "sha256-EeZH7UbjqoJHHrN1DkV32DxZ8OMGh/2kla94lpO17eE=", "owner": "cachix", "repo": "devenv", - "rev": "29fcc8a9a778c2e3f822ef961cc9667122e79dd5", + "rev": "ef185b5a38445777194bc8469fc1cdebd99e7dbc", "type": "github" }, "original": { @@ -135,11 +135,11 @@ "ghostty": { "flake": false, "locked": { - "lastModified": 1782866021, - "narHash": "sha256-BOLtzL5iAHmtCOg9/DXtcfw+K86QWol088K72chJB04=", + "lastModified": 1784602798, + "narHash": "sha256-298x90knBUWX5GHGXh2SKsAKvStjU2ri9UgOGoF79/8=", "owner": "ghostty-org", "repo": "ghostty", - "rev": "e1d31deaaed21aa9225afca78d778fb373c95852", + "rev": "88b4cd047fa627cdca6781bc7e7dc8b75a2cecb9", "type": "github" }, "original": { From 1c29f61c6fd23a0c2893b43750933dfd5db2e9b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:20:45 +0800 Subject: [PATCH 0388/1190] chore(deps): bump the opentelemetry group with 5 updates (#22790) Bumps the opentelemetry group with 5 updates: | Package | From | To | | --- | --- | --- | | [@opentelemetry/exporter-prometheus](https://github.com/open-telemetry/opentelemetry-js) | `0.220.0` | `0.221.0` | | [@opentelemetry/exporter-trace-otlp-http](https://github.com/open-telemetry/opentelemetry-js) | `0.220.0` | `0.221.0` | | [@opentelemetry/resources](https://github.com/open-telemetry/opentelemetry-js) | `2.9.0` | `2.10.0` | | [@opentelemetry/sdk-metrics](https://github.com/open-telemetry/opentelemetry-js) | `2.9.0` | `2.10.0` | | [@opentelemetry/sdk-trace-base](https://github.com/open-telemetry/opentelemetry-js) | `2.9.0` | `2.10.0` | Updates `@opentelemetry/exporter-prometheus` from 0.220.0 to 0.221.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/experimental/v0.220.0...experimental/v0.221.0) Updates `@opentelemetry/exporter-trace-otlp-http` from 0.220.0 to 0.221.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/experimental/v0.220.0...experimental/v0.221.0) Updates `@opentelemetry/resources` from 2.9.0 to 2.10.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/v2.9.0...v2.10.0) Updates `@opentelemetry/sdk-metrics` from 2.9.0 to 2.10.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/v2.9.0...v2.10.0) Updates `@opentelemetry/sdk-trace-base` from 2.9.0 to 2.10.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/v2.9.0...v2.10.0) --- updated-dependencies: - dependency-name: "@opentelemetry/exporter-prometheus" dependency-version: 0.221.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: opentelemetry - dependency-name: "@opentelemetry/exporter-trace-otlp-http" dependency-version: 0.221.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: opentelemetry - dependency-name: "@opentelemetry/resources" dependency-version: 2.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: opentelemetry - dependency-name: "@opentelemetry/sdk-metrics" dependency-version: 2.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: opentelemetry - dependency-name: "@opentelemetry/sdk-trace-base" dependency-version: 2.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: opentelemetry ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +-- pnpm-lock.yaml | 166 +++++++++++++++++++++++++------------------------ 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/package.json b/package.json index f7f7625da9ff..9dd7b09f77d6 100644 --- a/package.json +++ b/package.json @@ -69,11 +69,11 @@ "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.2", "@opentelemetry/api": "1.9.1", - "@opentelemetry/exporter-prometheus": "0.220.0", - "@opentelemetry/exporter-trace-otlp-http": "0.220.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-metrics": "2.9.0", - "@opentelemetry/sdk-trace-base": "2.9.0", + "@opentelemetry/exporter-prometheus": "0.221.0", + "@opentelemetry/exporter-trace-otlp-http": "0.221.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0", "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.11.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c360b475983e..9cb81ea3e7d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,20 +62,20 @@ importers: specifier: 1.9.1 version: 1.9.1 '@opentelemetry/exporter-prometheus': - specifier: 0.220.0 - version: 0.220.0(@opentelemetry/api@1.9.1) + specifier: 0.221.0 + version: 0.221.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.220.0 - version: 0.220.0(@opentelemetry/api@1.9.1) + specifier: 0.221.0 + version: 0.221.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) + specifier: 2.10.0 + version: 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) + specifier: 2.10.0 + version: 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) + specifier: 2.10.0 + version: 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': specifier: 1.43.0 version: 1.43.0 @@ -87,7 +87,7 @@ importers: version: 0.11.11(hono@4.12.31) '@sentry/node': specifier: 10.67.0 - version: 10.67.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)) + version: 10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -1529,24 +1529,28 @@ packages: resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.221.0': + resolution: {integrity: sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} - '@opentelemetry/core@2.9.0': - resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} + '@opentelemetry/core@2.10.0': + resolution: {integrity: sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-prometheus@0.220.0': - resolution: {integrity: sha512-JZD5DL/NBpVd2BHefvYosm3G40UZ/KzExLv5tc0eZe0CtrsHHtcOk3YPUxR2EINmUeBf8+w5UReTV8fFPn95lA==} + '@opentelemetry/exporter-prometheus@0.221.0': + resolution: {integrity: sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-http@0.220.0': - resolution: {integrity: sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==} + '@opentelemetry/exporter-trace-otlp-http@0.221.0': + resolution: {integrity: sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -1557,44 +1561,44 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.220.0': - resolution: {integrity: sha512-CXYo8UD5Mn9YbgebO2EL4wejtA+gxLmLiu6HCk2KH2BR7XhFN6/6p1UlCb23DYCjeYkndevLHuejCCN1yx4+OQ==} + '@opentelemetry/otlp-exporter-base@0.221.0': + resolution: {integrity: sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.220.0': - resolution: {integrity: sha512-lXGrv7KXZ0gNH9SVNUaa6vv6phVYGvJxfXAlMbzbakiXru75f5MZl8Z7oqiMMQD77riVHJCFlQvbZs/VVN2/4A==} + '@opentelemetry/otlp-transformer@0.221.0': + resolution: {integrity: sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/resources@2.9.0': - resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + '@opentelemetry/resources@2.10.0': + resolution: {integrity: sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.220.0': - resolution: {integrity: sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==} + '@opentelemetry/sdk-logs@0.221.0': + resolution: {integrity: sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@2.9.0': - resolution: {integrity: sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==} + '@opentelemetry/sdk-metrics@2.10.0': + resolution: {integrity: sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.9.0': - resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} + '@opentelemetry/sdk-trace-base@2.10.0': + resolution: {integrity: sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace@2.9.0': - resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} + '@opentelemetry/sdk-trace@2.10.0': + resolution: {integrity: sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -7333,29 +7337,31 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs@0.221.0': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api@1.9.1': {} - '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/exporter-prometheus@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-prometheus@0.221.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.221.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': dependencies: @@ -7366,55 +7372,55 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-exporter-base@0.221.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.221.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-transformer@0.221.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.220.0 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.221.0 + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.221.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-logs@0.221.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.220.0 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.221.0 + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-metrics@2.10.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace@2.10.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/semantic-conventions@1.43.0': {} @@ -8010,28 +8016,28 @@ snapshots: dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 '@sentry/core': 10.67.0 - '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.3.2 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-trace-otlp-http': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.221.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.67.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 '@sentry/core': 10.67.0 - '@sentry/node-core': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/node-core': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) '@sentry/server-utils': 10.67.0 import-in-the-middle: 3.3.2 transitivePeerDependencies: @@ -8039,11 +8045,11 @@ snapshots: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 '@sentry/core': 10.67.0 From e757839be2f1ca06df4686a7cf49541c19a986b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:09:32 +0800 Subject: [PATCH 0389/1190] chore(deps-dev): bump the oxc group across 1 directory with 6 updates (#22791) Bumps the oxc group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.74.0` | `1.75.0` | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.140.0` | `0.141.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.59.0` | `0.60.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.74.0` | `1.75.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.74.0` | `1.75.0` | | [oxlint-tsgolint](https://github.com/oxc-project/tsgolint) | `0.25.0` | `7.0.2001` | Updates `@oxlint/plugins` from 1.74.0 to 1.75.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.75.0/npm/oxlint-plugins) Updates `oxc-parser` from 0.140.0 to 0.141.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.141.0/napi/parser) Updates `oxfmt` from 0.59.0 to 0.60.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.60.0/npm/oxfmt) Updates `oxlint` from 1.74.0 to 1.75.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.75.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.74.0 to 1.75.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.75.0/npm/oxlint-plugin-eslint) Updates `oxlint-tsgolint` from 0.25.0 to 7.0.2001 - [Release notes](https://github.com/oxc-project/tsgolint/releases) - [Commits](https://github.com/oxc-project/tsgolint/compare/v0.25.0...v7.0.2001) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.75.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.141.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.60.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.75.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.75.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-tsgolint dependency-version: 7.0.2001 dependency-type: direct:development update-type: version-update:semver-major dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 12 +- pnpm-lock.yaml | 599 +++++++++++++++++++++++++------------------------ 2 files changed, 308 insertions(+), 303 deletions(-) diff --git a/package.json b/package.json index 9dd7b09f77d6..838e11ba7636 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "@cloudflare/workers-types": "5.20260722.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.74.0", + "@oxlint/plugins": "1.75.0", "@stylistic/eslint-plugin": "5.10.0", "@types/babel__preset-env": "7.10.0", "@types/crypto-js": "4.2.2", @@ -191,11 +191,11 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.140.0", - "oxfmt": "0.59.0", - "oxlint": "1.74.0", - "oxlint-plugin-eslint": "1.74.0", - "oxlint-tsgolint": "0.25.0", + "oxc-parser": "0.141.0", + "oxfmt": "0.60.0", + "oxlint": "1.75.0", + "oxlint-plugin-eslint": "1.75.0", + "oxlint-tsgolint": "7.0.2001", "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9cb81ea3e7d7..169f2b85851a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,8 +306,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.7.0) '@oxlint/plugins': - specifier: 1.74.0 - version: 1.74.0 + specifier: 1.75.0 + version: 1.75.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.7.0) @@ -423,20 +423,20 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.8.0)(utf-8-validate@5.0.10) oxc-parser: - specifier: 0.140.0 - version: 0.140.0 + specifier: 0.141.0 + version: 0.141.0 oxfmt: - specifier: 0.59.0 - version: 0.59.0 + specifier: 0.60.0 + version: 0.60.0 oxlint: - specifier: 1.74.0 - version: 1.74.0(oxlint-tsgolint@0.25.0) + specifier: 1.75.0 + version: 1.75.0(oxlint-tsgolint@7.0.2001) oxlint-plugin-eslint: - specifier: 1.74.0 - version: 1.74.0 + specifier: 1.75.0 + version: 1.75.0 oxlint-tsgolint: - specifier: 0.25.0 - version: 0.25.0 + specifier: 7.0.2001 + version: 7.0.2001 remark: specifier: 15.0.1 version: 15.0.1 @@ -1629,129 +1629,129 @@ packages: '@otplib/uri@13.4.1': resolution: {integrity: sha512-xaIm7bvICMhoB2rZIR5luiaMdssWR5nY5nXnR1fdezUgZuEO58D6zrGzLp7pQuBmlpmL0HagnscDQFoskp9yiA==} - '@oxc-parser/binding-android-arm-eabi@0.140.0': - resolution: {integrity: sha512-ZfjDZ422mo7eo3b3VltqNsV9kmv1qt/sPEAMSl64iOSwhVfd0eIZ9LB79Mbs1xYXJnk7WSROwzBCKDIiVxPTvQ==} + '@oxc-parser/binding-android-arm-eabi@0.141.0': + resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.140.0': - resolution: {integrity: sha512-Ia8jSvikUX6Sf+Ht+KOCUF/k1HpR0VlmqIYymubmWDebOEGtsyliHDR6JxsZ4IX3/c/GbrB1uh09aVGQv/LQmQ==} + '@oxc-parser/binding-android-arm64@0.141.0': + resolution: {integrity: sha512-a4XDQ27ZT7e7zwAlxJDTiCA7IBGWDuy2+MhFq85Of7XlBSmpkfcBFml11q0Zx6f7RMuI0B4xCtt2ytBS4yOptg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.140.0': - resolution: {integrity: sha512-G6VK0nK61pH0d0mBjUqSZbVxGqqO5uzeginLDQj+gOO6ObfJjXRwgkD/ol0w1INcnFeAb6YGGO7qc3ueGHaycQ==} + '@oxc-parser/binding-darwin-arm64@0.141.0': + resolution: {integrity: sha512-m/kVk6rzYmBeHYnz+1Y5fod00AVTTxMbC71azFfm/zjx1j9XxwKtA0+VfkKuVMC8rbghb9TtfevnuWZa9OuPEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.140.0': - resolution: {integrity: sha512-HazBOuZzd2pO1C2uMmp8Gv7mhzMHqKSKDS1OZfcLEvpIcgA+48J92HEtNanVHDIzRD9PRPCV6aS6fkZIWOVl8Q==} + '@oxc-parser/binding-darwin-x64@0.141.0': + resolution: {integrity: sha512-o0X+6KZlfucWU/v5oKRQPwdFXsXAjW8jmpo/Gpw/qyKsbKtlfkHoeH9Bjp/m13TwjewvJnCkwF0DWzgpC4HjTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.140.0': - resolution: {integrity: sha512-9hSUU+HmTUyOe4JzMHxNGgLWNY7rrO+6ShicZwImNJacEAACDMIkuEQQkvXSL+WJN50jaNtLYJv8s4OcBdpyUQ==} + '@oxc-parser/binding-freebsd-x64@0.141.0': + resolution: {integrity: sha512-W5KbTnNkTMMMylqj6dYqnsXvkmESVPodPKYLJ5zdzIPdl9fUJtolkpUeSzYEbGGYB4a4A4avl3EePnZ/wLIdJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0': - resolution: {integrity: sha512-RAEuQsYtS0KcDFqN0ABTjyyNlokS91JeuDuoW9tEbG0JTbRNXnpQUdbYc/16JoA6Z/2ALbNrE3KmxtqDiuIjCQ==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + resolution: {integrity: sha512-g3dtbJa8zeOGK36Sr9cQavsdi5H/ie2hVjrSjIxsNAR1qZA40ZYVXnfdfoMAlq8CmB9qFL1yhsSCUHeNmdmt8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.140.0': - resolution: {integrity: sha512-c4CkHvPvqfojouredJ0w3e6+jiBq0SbFyhH61kr/zPb/7XsaYTNKQ54vmlSsopfdQbNDX40ZeK9Abs2Qet6wcw==} + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + resolution: {integrity: sha512-e6hwQqd+3lvP13G2jxvFpoA7dzHcFLN+Mq47JCVMtdNHbbyBRo756JCtbbJH6ca8inTfyqZoqBmS3vhQlzAK2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.140.0': - resolution: {integrity: sha512-yrjmLj8ixPB25yqvPGr28meGjb+keed7m1GqqY/0uqkhZIoT4t9zmfwUgFEtC33C7dtE+UQ7TU0IaVxf97SWJg==} + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + resolution: {integrity: sha512-vXz2BLAuypA+4MLyBg94pzEo6THVnzYnCtAjXoihIIQo0t2pnp/AmW+SH1EI+4VbuJnC//KplIJ5yyaCGua4jA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.140.0': - resolution: {integrity: sha512-ggGMQTN8Agwxp2WiLMpdY671dt0qTDJWiWlJeig3HnUwTnerRl0J2JdGVghWBeDcss2D9S2V2Js6dZHEiVabVA==} + '@oxc-parser/binding-linux-arm64-musl@0.141.0': + resolution: {integrity: sha512-jMkS/EztNW34HKsXIaT/SoHcmtocq/vWhwFOVduF9kduuuRIVwfwQ6uxzIO+qPKSXdd2TXt54of0BJ2zFMXnmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.140.0': - resolution: {integrity: sha512-IgTs8xYAFgAUGNmR65tIqjlJ8vKgrfXzC515e9goSdfMyKQV4aJpd2pUUudU4u51G64H0/DSEJEXKOraxm9ZCA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + resolution: {integrity: sha512-vo+MR+n3zQJ6Mq92hiP084NZcgDv5iJlVR02gMf28neMvVT1tKVm7VeiW/DxhdqOi3QLeaXIk9cUcLL1qrkngw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.140.0': - resolution: {integrity: sha512-A1x+PMWZmSGaFVOx2YeNTFau8uD+QO14/vLP4GrcuvUPs3+nBkUOjy9Lus86ftHsDojjYMbvBelmKc3F7Rv08g==} + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + resolution: {integrity: sha512-oh80w+7RuiO5gBp9Jnoa/H8Qlt3JsHL2MkW+0dwEdlDMdslVZX/YsekSK6EeyEenY66/mhCfypsNATQ7Ph3qlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.140.0': - resolution: {integrity: sha512-zBqpfRo2myWPrPo5xUjeZqlnPXPXsX8BcWtWff66/eGRQdbPjhzPgXa/F+AtxT2afUViPxbuDlwscMKzQ5tg+g==} + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + resolution: {integrity: sha512-LOyEmFA8sCnYbEXP1+iQvCC/P1YXHMA/t6x1Ksp0Y9VwhLFsiBJFzV1zIxrOIE2LKaGGhDjQ29xq9cbq6omDXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.140.0': - resolution: {integrity: sha512-2M1DPm/8w9I//YzFlFC9qXw+r2tJFh5CYwRlYTq2vUJQS7qoQftEDeCZ8EnN7KHtvSiXvYj8mZI5pR7DpXmcEw==} + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + resolution: {integrity: sha512-3wnwk/l1CvszVE5TJR1wSl/zSEfydRqrNhn6s7Vr9IzSJpUQIroqVsIoPARHRFA+FQwkxAFDAHDAasa7v8OobQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.140.0': - resolution: {integrity: sha512-8aRDbZ/U/jO8N7go1MO72jtbpb4uswV8d7vOkMvt/BPgZiyEYvl1VIWK4ESxZZhnJ4tqwVldgX7dNiP/eB1Jdg==} + '@oxc-parser/binding-linux-x64-gnu@0.141.0': + resolution: {integrity: sha512-qtyQVAAebFq57B2tifTlel3TgGqUtsYNI/e+p6aya9rN9lOZVTDvr215fGYSA9XWooxzMxDiVxkBLk2jQHbsOQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.140.0': - resolution: {integrity: sha512-xRqpeI8U2sQQS1W5BMWRyMTxtagkuLG2dEWruet5lFsWHTvBth11/TpSaJatHdqVVwHN0q3uuoS9zRsGinq8hg==} + '@oxc-parser/binding-linux-x64-musl@0.141.0': + resolution: {integrity: sha512-SkGV1nKw40roEc94pv5EaaeH2ay14G6+roe8Q0wIUC1LcEKxzKW921h7+ZuZX0D3q2Mb/7aSFmxEVqnko3lPRw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.140.0': - resolution: {integrity: sha512-GbGRe26MqAKciFRvXeHNQJ6VAHYs9R4miP89sEAncysM3n+f4lnyLWgsa9kklJNpfnxdq2yRoNYHFqwBckVimw==} + '@oxc-parser/binding-openharmony-arm64@0.141.0': + resolution: {integrity: sha512-cVgDM7n8QziQqOaP5hNgUYfMG7S/ZeuPxFWXnnHRv7rh025COk0rfQ6eEdKG3j/GaUuyvNZN4ifF1J8KmuXLLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.140.0': - resolution: {integrity: sha512-vFiC1hqys+hkX1GnQkIoiTQJNiUm43Z0lO35ETKXTw0YtpW7+cN58YRRXFAQQ+TgpkIi3lrhcxdlnqz+Oi3ptQ==} + '@oxc-parser/binding-wasm32-wasi@0.141.0': + resolution: {integrity: sha512-HggH++Fkn3OilBn+bs3jpgIFQa34oMAyUUHy0vpGum+gt1Eb5nyLc8dNU/RAPSw6lsLrx7ncKtHSZE+3Sp0l2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.140.0': - resolution: {integrity: sha512-fGSQldwEYKhM+H8uLt76Op8hh5+FYaR6lvvQ1Txw3Mhn86DyQXLcI0fi1EkFlTK7F+46OCk/j0AJMzZQm6g5Xg==} + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + resolution: {integrity: sha512-KLSEH9GwgbrqbJOjtGHt9STw96s+78yDzp7IDN8Lno+7Ut9sNBfZ4jYZIz4mD50qmWUjoOI7i9I6UENbhNbMZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.140.0': - resolution: {integrity: sha512-sDS2Bai+g3ZWYwfZqmosiSuFDBcVnZ3Ta6pszzsiJoLMqsJEWKcxXXbGa7b7yXr++W2lQNPb3ZRJ8czseqL7RA==} + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + resolution: {integrity: sha512-9UVWUOOCI/1YkiSSNjg2zyBJYM9E/t1A/8GNobd48JDn/fQ6mzxcVO3H08jb3rAaW/B1VBf8eCORTvSsO9T08g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.140.0': - resolution: {integrity: sha512-kHbE1zWyb5OQgJA6/5P4WjiuB01sYdQwtZnSSyE58FQEXDAMnyeeq4vj7KgN75i5SlBzOs8A5MrtlD3gOlDKqQ==} + '@oxc-parser/binding-win32-x64-msvc@0.141.0': + resolution: {integrity: sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1762,6 +1762,9 @@ packages: '@oxc-project/types@0.140.0': resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + '@oxc-project/types@0.141.0': + resolution: {integrity: sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] @@ -1865,282 +1868,282 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.59.0': - resolution: {integrity: sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==} + '@oxfmt/binding-android-arm-eabi@0.60.0': + resolution: {integrity: sha512-1q4q4Jc8FlOMVojEisyFAVyl8h1yawNv6phjgmhGVEDeyeOdsSnSr9x0+D4mOnEKvpO5L4mxKZ/DP9X6U3A/Mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.59.0': - resolution: {integrity: sha512-R/Sn7z52QtdAKNqQLLY0EK7hVMjXiz3XUlvoCFCm/60jgIzAnQtiqLKBCFaBkimCQL5rs2ezPMcicpjCsrl54Q==} + '@oxfmt/binding-android-arm64@0.60.0': + resolution: {integrity: sha512-tD41I6nCt9k8SQXft0CSjjU9jg6SwG7uMu7PxodSEHXl+GDW0868oy6tTtoJkyUze8YKFgTpz/k5LuPUnFiGLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.59.0': - resolution: {integrity: sha512-vm/ynUqE4HjC0ZIEjmXv1UJu1/GngccQ+T+TJudTMxUxm6r+GQTg1TO3E5jJfI71pBaXxSzs1+vWHIwuilGHhw==} + '@oxfmt/binding-darwin-arm64@0.60.0': + resolution: {integrity: sha512-TTpzPug96Zxdyb46KvTyIUQDdsqbumXh2TKG9C23PCT0kF7JkW56Z/quPuG9rqOFKQIi1gpRNZ7DX18LwxXPnw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.59.0': - resolution: {integrity: sha512-uTtYDpLN/obfKVWGpgEc8BqYlLZBQTPz2uYEvLRy3HPZxjZ34wiFzukUBU2bf64JuCYZI//GTV1EOMmWlPjf/w==} + '@oxfmt/binding-darwin-x64@0.60.0': + resolution: {integrity: sha512-CnOoWgQ7L+JL/YQaRJ+NyATciSfcftncm7y3kqyte1cGtFEGnStaCd1TAyrinkfQ7nRBfHrTs1/vTwUJr3WF2Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.59.0': - resolution: {integrity: sha512-e2UnxL/ifStSPy8ffBCDbdy595SYsGy+U1pur4G65TuMmWxAMBzYGG7atZo/3mp515p8rZdsflxVD/E1FAdPLQ==} + '@oxfmt/binding-freebsd-x64@0.60.0': + resolution: {integrity: sha512-ychJo7S3hZxdO6eDZ9zM6F2lM9fpJS3EKS5CAUSWyprdLYxTu4gbaUKV/VBPTcMJwQa2Bpo+643y3OJ537pihA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.59.0': - resolution: {integrity: sha512-LtdeZ1l0urxte3VNi3g8cocZwv1xGM1NKHSgF/fJEEVhyQmlgGh7WFWKFd/pNuO7djfvPNtNO1+MS+FEWkgVSA==} + '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': + resolution: {integrity: sha512-36IH5o55T2Fx7E0feDttt+mifxN6yk9pWv4KfhAIsP0dFnUq27331OwbpOsZdoXF9soOLWm7mQUz5+UUmyec4g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.59.0': - resolution: {integrity: sha512-dBTciSsj9GTMl7p+h2gMSI0hoPn2ijfc/dUsbnWsP0RbwgPl2r0C/5zkMb3Pb+gGj17LH7f1o4qLo9aes/pAvA==} + '@oxfmt/binding-linux-arm-musleabihf@0.60.0': + resolution: {integrity: sha512-G1Ve7lAa6sFBolVI2LWHfEAqy0YKh4vnioH8uYO9kAEdgM7mR40IksIx9/Zk4+vbYew/sGa4J9Q4tZ3n9gXDHA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.59.0': - resolution: {integrity: sha512-tXVdJ/JINsNWdponPHN0OuKHtC+HdpyoS9sd6IDPNiiEYsRki8b7tefRZ1iMnRkdbyT4SEbguWsr6o+5awvbPQ==} + '@oxfmt/binding-linux-arm64-gnu@0.60.0': + resolution: {integrity: sha512-LTQdRBf6uzj/h7Xk6lKzbGD2hrF/fK4YI9LIN1c0509tPUn8wRa3mCmrFQpEWJPLYGFrLFFMTYW1Ljj6VqW2Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.59.0': - resolution: {integrity: sha512-RRTq38i2zT5fnw6XGHjvT6w2mh6x/G3m6AZcAZ56OTDTT/lsOeYnG3SVjwmH40z5kPqF+lf+o35e6m6PpKy9Dw==} + '@oxfmt/binding-linux-arm64-musl@0.60.0': + resolution: {integrity: sha512-2JMo3XPxMPx3hiqddSZYyaH+fKJm6cz0u8n1naYjP/CdOQOZW34i8lKBUfmbWiuFvd6KoYXLmhAyBuvojsYS7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.59.0': - resolution: {integrity: sha512-lD3k7glAJSaXW0D6xzu8VOZbYbosvy+0ktOVkfLEoQF5HJlMSxTQ2KNW0JO+08ccP/1ElOKktVEMI0fqRbVB4w==} + '@oxfmt/binding-linux-ppc64-gnu@0.60.0': + resolution: {integrity: sha512-L3C+nBD13lr306tr/PjM3RMll+BVqgFrIgUyoeHuai5oueJrRLgO3j+GO5/Cbhtkf5PSlHYTI1JY7iqBd1qa6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.59.0': - resolution: {integrity: sha512-WH5ZP1RbuHKBO/yfPRQKpNO/ijHcEDNbnmC4VPf/Bcd3+mbMAZpRiJWRa1PL5bREdIZZHo343mk3sqlc9x7Usw==} + '@oxfmt/binding-linux-riscv64-gnu@0.60.0': + resolution: {integrity: sha512-M4MsmvqlxFiPtSRGyBYQSZxchEf463AOyd+Dh4/9xDpjWBsRtDUTDMFN5EdHinjVK1/eDJQ8MLpcYjpYayaCnA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.59.0': - resolution: {integrity: sha512-743wOiaI9RZY4QVGkWkfGRavD5ZJUJ6gscFjVrVu1dP8AZh9jM+a6v3NhlR+OIzHdS6DhLM96w+gcVskskz7rw==} + '@oxfmt/binding-linux-riscv64-musl@0.60.0': + resolution: {integrity: sha512-OH+9UskYuxRB+GxqdGkVN8f5UpwhqG8YscNo1wl8+KJ62cd7wZdGga6iGLJIf8kibF1WBwvlfDUx3cez/VXwFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.59.0': - resolution: {integrity: sha512-xjRXQsRnrRZCcCkIEnbd2lmsQNobtwwkJxdy2bWXhZ1lIN0ouZwsBXRsoovW3yATuziAYwr9HMiQuR/Cc75NIw==} + '@oxfmt/binding-linux-s390x-gnu@0.60.0': + resolution: {integrity: sha512-y7AAFutt9wFWBFOAn6+BHaV39usZmcr3YYH2385f+NHgPNpIF9HpqKp0jgUxPaUOCyG3oaX5VhJduL1Nw164rw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.59.0': - resolution: {integrity: sha512-4hNjqq/Rbr9B+StY9zMMAfm72+mtM4v80xYL5Qkb59Qd72g2vJMI0iFlPj3kf6miMsie/yJ7rt4urJT292HBgA==} + '@oxfmt/binding-linux-x64-gnu@0.60.0': + resolution: {integrity: sha512-yKZ9+CXAI+1RO5nH/4Z/9M6DAsfOzd5bw/gtWk81KB4mpalMaRRSXfouc5/tHxazDmBek55HNPepNYBgaCew0Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.59.0': - resolution: {integrity: sha512-NH579iN8EVQYsWowUB8B5vFchcylJtwPVJ7NmUAqEQHNLfhPbDT3K56KrECNAkUN4QpF4qiMgN2vsfZwVvjm7g==} + '@oxfmt/binding-linux-x64-musl@0.60.0': + resolution: {integrity: sha512-bCUGaF6hJOYnQzLJdHLZbvGsOd5oSvGAyJhPAKum2uyLYUuXmP8vqg690DWi2hqcnIoYpqSqCrjzE5aiUAgwQg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.59.0': - resolution: {integrity: sha512-mzZy3Z5Aj1D75Aq9FVlmoRQH5ei8Ga4o/NZmlXkKyeZ5EmPrUXRR7c6BMBteV1ZuZ/356UYDuLRLjAMxTDTiBA==} + '@oxfmt/binding-openharmony-arm64@0.60.0': + resolution: {integrity: sha512-GrUeZOvzP30ExxfCuQiyofuUGI+OmvAgFwOO5w5p9mGPlxcyuqI+6Sy9fAKFFfLQrqKYWFgc5sYA2Unj/29nPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.59.0': - resolution: {integrity: sha512-0CpDJ1gE3jN1Gk6xms1Ie6LPfPcOtY4FAtoOmVLHQoAf8DvO2wd0DW2dIX2f7YTp5dxrr0ND8JeUEjm3DP3k5g==} + '@oxfmt/binding-win32-arm64-msvc@0.60.0': + resolution: {integrity: sha512-WD4Q954kUl2TDJV/6q7UnE2rlKk047kXLJsr4bJ2mXRaAqNXcmV3nwKUsGCc3mz/jYDBnXtJEaBErJEybK8iQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.59.0': - resolution: {integrity: sha512-zwdKBu3pt87uW0bRcywZb0oGMS7C6n87qogwRYFUgmk44T90ZzYlPjtlFYXs/DnBFrgNCvlHwCuWKfVWLeE7kw==} + '@oxfmt/binding-win32-ia32-msvc@0.60.0': + resolution: {integrity: sha512-HqDekjr8JXzVDUP1YthDZ1Y3CBEcuZT4WX3B+1kaxj8CvZA8Y2YhcEsXqoSop3tVsgjACxjnFQFDkBo0r/jq1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.59.0': - resolution: {integrity: sha512-dUUbZkKgWrmAeI/puzv4bxN8lzcYaFnQVwFTFtwO2Gp8M7lZGSE2qJjC58g518+1bltJ8mizjYwD0BGHym0l/w==} + '@oxfmt/binding-win32-x64-msvc@0.60.0': + resolution: {integrity: sha512-tz78yhmGPKboTMHCHSaUqXK8JrmoSejgDcWeqAtg2s07ZGKQ3rH5Jn8NuXPGNG33CDbY2e9NoQWXIVEmKO21Rw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.25.0': - resolution: {integrity: sha512-87opKlwFP8qS9WHAeETV+kA0fC9Oyj4sg7OxWdI4xQY0WC7zlN6BgG66uE5mvtN5mahkt/gL0i/AVEnX6POq2Q==} + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.25.0': - resolution: {integrity: sha512-HJmuZexsrhqp4WmETn+Soq7Ogt5F0jirv+cYRSniIPe+d/x5beQzLX69xOLhQRE+8FLGETe7FahWMVP8x0dW4g==} + '@oxlint-tsgolint/darwin-x64@7.0.2001': + resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.25.0': - resolution: {integrity: sha512-aNyYsPREvCJi3qjfBA0sQB7DhT3y/W5Ac2JI2D8IJynoTOAhVZj401Si6901oDajlBWyqJqqojudn0VgHB6+7A==} + '@oxlint-tsgolint/linux-arm64@7.0.2001': + resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.25.0': - resolution: {integrity: sha512-+60+VjK9Mch3uA5WlTdNHuAm5+WA7wPPjuWdPWlU0F6JJpYpGZXUpO1RPKuFEWsBpNbLcLeJ0LbCJ1doWu58NA==} + '@oxlint-tsgolint/linux-x64@7.0.2001': + resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.25.0': - resolution: {integrity: sha512-r53TO+eHp/t53nnUkQJfrYYXODPAxmtf3RUFQG5XsE2hD21IunliOaAdZXP2UwzCx+r/fbNaEelqTaAHcDr57w==} + '@oxlint-tsgolint/win32-arm64@7.0.2001': + resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.25.0': - resolution: {integrity: sha512-vqe66B+gL9HarhyHemdlfC2VWT7eoA+o/ufZ7zT6AGHv64boyDZIJS3U+rpZo+ey4O7wZtiS/vYR2fWqDoFeBw==} + '@oxlint-tsgolint/win32-x64@7.0.2001': + resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.74.0': - resolution: {integrity: sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==} + '@oxlint/binding-android-arm-eabi@1.75.0': + resolution: {integrity: sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.74.0': - resolution: {integrity: sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==} + '@oxlint/binding-android-arm64@1.75.0': + resolution: {integrity: sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.74.0': - resolution: {integrity: sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==} + '@oxlint/binding-darwin-arm64@1.75.0': + resolution: {integrity: sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.74.0': - resolution: {integrity: sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==} + '@oxlint/binding-darwin-x64@1.75.0': + resolution: {integrity: sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.74.0': - resolution: {integrity: sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==} + '@oxlint/binding-freebsd-x64@1.75.0': + resolution: {integrity: sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.74.0': - resolution: {integrity: sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==} + '@oxlint/binding-linux-arm-gnueabihf@1.75.0': + resolution: {integrity: sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.74.0': - resolution: {integrity: sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==} + '@oxlint/binding-linux-arm-musleabihf@1.75.0': + resolution: {integrity: sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.74.0': - resolution: {integrity: sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==} + '@oxlint/binding-linux-arm64-gnu@1.75.0': + resolution: {integrity: sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.74.0': - resolution: {integrity: sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==} + '@oxlint/binding-linux-arm64-musl@1.75.0': + resolution: {integrity: sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.74.0': - resolution: {integrity: sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==} + '@oxlint/binding-linux-ppc64-gnu@1.75.0': + resolution: {integrity: sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.74.0': - resolution: {integrity: sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==} + '@oxlint/binding-linux-riscv64-gnu@1.75.0': + resolution: {integrity: sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.74.0': - resolution: {integrity: sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==} + '@oxlint/binding-linux-riscv64-musl@1.75.0': + resolution: {integrity: sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.74.0': - resolution: {integrity: sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==} + '@oxlint/binding-linux-s390x-gnu@1.75.0': + resolution: {integrity: sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.74.0': - resolution: {integrity: sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==} + '@oxlint/binding-linux-x64-gnu@1.75.0': + resolution: {integrity: sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.74.0': - resolution: {integrity: sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==} + '@oxlint/binding-linux-x64-musl@1.75.0': + resolution: {integrity: sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.74.0': - resolution: {integrity: sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==} + '@oxlint/binding-openharmony-arm64@1.75.0': + resolution: {integrity: sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.74.0': - resolution: {integrity: sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==} + '@oxlint/binding-win32-arm64-msvc@1.75.0': + resolution: {integrity: sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.74.0': - resolution: {integrity: sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==} + '@oxlint/binding-win32-ia32-msvc@1.75.0': + resolution: {integrity: sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.74.0': - resolution: {integrity: sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==} + '@oxlint/binding-win32-x64-msvc@1.75.0': + resolution: {integrity: sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.74.0': - resolution: {integrity: sha512-3tQlMDPt5hrRYedKl+M+Xq+fRjAEocMCRJaXH6ypLWu8+Oui5GMj51vjaYf/rzj6HT+JzQoUlY/I7Im2VNXzbw==} + '@oxlint/plugins@1.75.0': + resolution: {integrity: sha512-dNQBRuvkeecm9nxi1cXRxXA1oAyqJqHof6cdnjY/WDBU1nZ9V07rp9X9gG7+JgShrjJW4VR2dTo7t2EcX4XD8g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -3673,7 +3676,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -5154,15 +5157,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.140.0: - resolution: {integrity: sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==} + oxc-parser@0.141.0: + resolution: {integrity: sha512-uFkGGr1KMWd6aWv9UAqooYrN78trw8MWWmoPvgWokfBEUq1+eiIQ+qfj3wokhy0fxtZWZk+0dHoS7/yRTJtd6w==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.59.0: - resolution: {integrity: sha512-Xqk6cPZS1yMvVa7OAuenaDZUsgMDutvvbZ9/L5gSvAfW64+WN4HVhgipLj5rVERbYQt8fLs9TopyZ1rU1XEG/w==} + oxfmt@0.60.0: + resolution: {integrity: sha512-fViX6i+gJuZWY+jI/fnR6WRbRj70GZ9RlCd30MygJrHTUNc4DxvKHWw8vBjMjffv3PgU5qWDR0AzmojQByqaZA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5174,20 +5177,20 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.74.0: - resolution: {integrity: sha512-AtZ4w3owo4xoY5fe/Juy9hmzEnekVPd/DDiPlOVGfR2linDTp/XVu0xK7ODNK4CCLvnnfCob/UeWN6kSiqm2CQ==} + oxlint-plugin-eslint@1.75.0: + resolution: {integrity: sha512-pgVC0ctNXubXVG7ObuW85kyAf9jHBDPIZm1uKCDwP8JPP2nCjXFtuEpCapmr4JgzW9F84z8x4fik+OpBep81gQ==} engines: {node: ^20.19.0 || >=22.12.0} - oxlint-tsgolint@0.25.0: - resolution: {integrity: sha512-7DBpqyLZCfyoXiivyfzt9Xmju/K1RcN+Y1W7buEwrgRCWWF11v9alypPqWGZBmh2erDkKL/kVyhKUH2Px+t13A==} + oxlint-tsgolint@7.0.2001: + resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.74.0: - resolution: {integrity: sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==} + oxlint@1.75.0: + resolution: {integrity: sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.24.0' + oxlint-tsgolint: '>=7.0.2001' vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: @@ -5331,8 +5334,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.21: - resolution: {integrity: sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==} + postcss@8.5.22: + resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -7454,74 +7457,76 @@ snapshots: dependencies: '@otplib/core': 13.4.1 - '@oxc-parser/binding-android-arm-eabi@0.140.0': + '@oxc-parser/binding-android-arm-eabi@0.141.0': optional: true - '@oxc-parser/binding-android-arm64@0.140.0': + '@oxc-parser/binding-android-arm64@0.141.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.140.0': + '@oxc-parser/binding-darwin-arm64@0.141.0': optional: true - '@oxc-parser/binding-darwin-x64@0.140.0': + '@oxc-parser/binding-darwin-x64@0.141.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.140.0': + '@oxc-parser/binding-freebsd-x64@0.141.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.140.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.140.0': + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.140.0': + '@oxc-parser/binding-linux-arm64-musl@0.141.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.140.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.140.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.140.0': + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.140.0': + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.140.0': + '@oxc-parser/binding-linux-x64-gnu@0.141.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.140.0': + '@oxc-parser/binding-linux-x64-musl@0.141.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.140.0': + '@oxc-parser/binding-openharmony-arm64@0.141.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.140.0': + '@oxc-parser/binding-wasm32-wasi@0.141.0': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.140.0': + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.140.0': + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.140.0': + '@oxc-parser/binding-win32-x64-msvc@0.141.0': optional: true '@oxc-project/types@0.139.0': {} '@oxc-project/types@0.140.0': {} + '@oxc-project/types@0.141.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7583,139 +7588,139 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.59.0': + '@oxfmt/binding-android-arm-eabi@0.60.0': optional: true - '@oxfmt/binding-android-arm64@0.59.0': + '@oxfmt/binding-android-arm64@0.60.0': optional: true - '@oxfmt/binding-darwin-arm64@0.59.0': + '@oxfmt/binding-darwin-arm64@0.60.0': optional: true - '@oxfmt/binding-darwin-x64@0.59.0': + '@oxfmt/binding-darwin-x64@0.60.0': optional: true - '@oxfmt/binding-freebsd-x64@0.59.0': + '@oxfmt/binding-freebsd-x64@0.60.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.59.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.59.0': + '@oxfmt/binding-linux-arm-musleabihf@0.60.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.59.0': + '@oxfmt/binding-linux-arm64-gnu@0.60.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.59.0': + '@oxfmt/binding-linux-arm64-musl@0.60.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.59.0': + '@oxfmt/binding-linux-ppc64-gnu@0.60.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.59.0': + '@oxfmt/binding-linux-riscv64-gnu@0.60.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.59.0': + '@oxfmt/binding-linux-riscv64-musl@0.60.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.59.0': + '@oxfmt/binding-linux-s390x-gnu@0.60.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.59.0': + '@oxfmt/binding-linux-x64-gnu@0.60.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.59.0': + '@oxfmt/binding-linux-x64-musl@0.60.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.59.0': + '@oxfmt/binding-openharmony-arm64@0.60.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.59.0': + '@oxfmt/binding-win32-arm64-msvc@0.60.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.59.0': + '@oxfmt/binding-win32-ia32-msvc@0.60.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.59.0': + '@oxfmt/binding-win32-x64-msvc@0.60.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.25.0': + '@oxlint-tsgolint/darwin-arm64@7.0.2001': optional: true - '@oxlint-tsgolint/darwin-x64@0.25.0': + '@oxlint-tsgolint/darwin-x64@7.0.2001': optional: true - '@oxlint-tsgolint/linux-arm64@0.25.0': + '@oxlint-tsgolint/linux-arm64@7.0.2001': optional: true - '@oxlint-tsgolint/linux-x64@0.25.0': + '@oxlint-tsgolint/linux-x64@7.0.2001': optional: true - '@oxlint-tsgolint/win32-arm64@0.25.0': + '@oxlint-tsgolint/win32-arm64@7.0.2001': optional: true - '@oxlint-tsgolint/win32-x64@0.25.0': + '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.74.0': + '@oxlint/binding-android-arm-eabi@1.75.0': optional: true - '@oxlint/binding-android-arm64@1.74.0': + '@oxlint/binding-android-arm64@1.75.0': optional: true - '@oxlint/binding-darwin-arm64@1.74.0': + '@oxlint/binding-darwin-arm64@1.75.0': optional: true - '@oxlint/binding-darwin-x64@1.74.0': + '@oxlint/binding-darwin-x64@1.75.0': optional: true - '@oxlint/binding-freebsd-x64@1.74.0': + '@oxlint/binding-freebsd-x64@1.75.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.74.0': + '@oxlint/binding-linux-arm-gnueabihf@1.75.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.74.0': + '@oxlint/binding-linux-arm-musleabihf@1.75.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.74.0': + '@oxlint/binding-linux-arm64-gnu@1.75.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.74.0': + '@oxlint/binding-linux-arm64-musl@1.75.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.74.0': + '@oxlint/binding-linux-ppc64-gnu@1.75.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.74.0': + '@oxlint/binding-linux-riscv64-gnu@1.75.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.74.0': + '@oxlint/binding-linux-riscv64-musl@1.75.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.74.0': + '@oxlint/binding-linux-s390x-gnu@1.75.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.74.0': + '@oxlint/binding-linux-x64-gnu@1.75.0': optional: true - '@oxlint/binding-linux-x64-musl@1.74.0': + '@oxlint/binding-linux-x64-musl@1.75.0': optional: true - '@oxlint/binding-openharmony-arm64@1.74.0': + '@oxlint/binding-openharmony-arm64@1.75.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.74.0': + '@oxlint/binding-win32-arm64-msvc@1.75.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.74.0': + '@oxlint/binding-win32-ia32-msvc@1.75.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.74.0': + '@oxlint/binding-win32-x64-msvc@1.75.0': optional: true - '@oxlint/plugins@1.74.0': {} + '@oxlint/plugins@1.75.0': {} '@pinojs/redact@0.4.0': {} @@ -10808,30 +10813,30 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.140.0: + oxc-parser@0.141.0: dependencies: - '@oxc-project/types': 0.140.0 + '@oxc-project/types': 0.141.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.140.0 - '@oxc-parser/binding-android-arm64': 0.140.0 - '@oxc-parser/binding-darwin-arm64': 0.140.0 - '@oxc-parser/binding-darwin-x64': 0.140.0 - '@oxc-parser/binding-freebsd-x64': 0.140.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.140.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.140.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.140.0 - '@oxc-parser/binding-linux-arm64-musl': 0.140.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.140.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.140.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.140.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.140.0 - '@oxc-parser/binding-linux-x64-gnu': 0.140.0 - '@oxc-parser/binding-linux-x64-musl': 0.140.0 - '@oxc-parser/binding-openharmony-arm64': 0.140.0 - '@oxc-parser/binding-wasm32-wasi': 0.140.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.140.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.140.0 - '@oxc-parser/binding-win32-x64-msvc': 0.140.0 + '@oxc-parser/binding-android-arm-eabi': 0.141.0 + '@oxc-parser/binding-android-arm64': 0.141.0 + '@oxc-parser/binding-darwin-arm64': 0.141.0 + '@oxc-parser/binding-darwin-x64': 0.141.0 + '@oxc-parser/binding-freebsd-x64': 0.141.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.141.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.141.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.141.0 + '@oxc-parser/binding-linux-arm64-musl': 0.141.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.141.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-musl': 0.141.0 + '@oxc-parser/binding-openharmony-arm64': 0.141.0 + '@oxc-parser/binding-wasm32-wasi': 0.141.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.141.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.141.0 + '@oxc-parser/binding-win32-x64-msvc': 0.141.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10855,63 +10860,63 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.59.0: + oxfmt@0.60.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.59.0 - '@oxfmt/binding-android-arm64': 0.59.0 - '@oxfmt/binding-darwin-arm64': 0.59.0 - '@oxfmt/binding-darwin-x64': 0.59.0 - '@oxfmt/binding-freebsd-x64': 0.59.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.59.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.59.0 - '@oxfmt/binding-linux-arm64-gnu': 0.59.0 - '@oxfmt/binding-linux-arm64-musl': 0.59.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.59.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.59.0 - '@oxfmt/binding-linux-riscv64-musl': 0.59.0 - '@oxfmt/binding-linux-s390x-gnu': 0.59.0 - '@oxfmt/binding-linux-x64-gnu': 0.59.0 - '@oxfmt/binding-linux-x64-musl': 0.59.0 - '@oxfmt/binding-openharmony-arm64': 0.59.0 - '@oxfmt/binding-win32-arm64-msvc': 0.59.0 - '@oxfmt/binding-win32-ia32-msvc': 0.59.0 - '@oxfmt/binding-win32-x64-msvc': 0.59.0 - - oxlint-plugin-eslint@1.74.0: {} - - oxlint-tsgolint@0.25.0: + '@oxfmt/binding-android-arm-eabi': 0.60.0 + '@oxfmt/binding-android-arm64': 0.60.0 + '@oxfmt/binding-darwin-arm64': 0.60.0 + '@oxfmt/binding-darwin-x64': 0.60.0 + '@oxfmt/binding-freebsd-x64': 0.60.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.60.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.60.0 + '@oxfmt/binding-linux-arm64-gnu': 0.60.0 + '@oxfmt/binding-linux-arm64-musl': 0.60.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.60.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.60.0 + '@oxfmt/binding-linux-riscv64-musl': 0.60.0 + '@oxfmt/binding-linux-s390x-gnu': 0.60.0 + '@oxfmt/binding-linux-x64-gnu': 0.60.0 + '@oxfmt/binding-linux-x64-musl': 0.60.0 + '@oxfmt/binding-openharmony-arm64': 0.60.0 + '@oxfmt/binding-win32-arm64-msvc': 0.60.0 + '@oxfmt/binding-win32-ia32-msvc': 0.60.0 + '@oxfmt/binding-win32-x64-msvc': 0.60.0 + + oxlint-plugin-eslint@1.75.0: {} + + oxlint-tsgolint@7.0.2001: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.25.0 - '@oxlint-tsgolint/darwin-x64': 0.25.0 - '@oxlint-tsgolint/linux-arm64': 0.25.0 - '@oxlint-tsgolint/linux-x64': 0.25.0 - '@oxlint-tsgolint/win32-arm64': 0.25.0 - '@oxlint-tsgolint/win32-x64': 0.25.0 - - oxlint@1.74.0(oxlint-tsgolint@0.25.0): + '@oxlint-tsgolint/darwin-arm64': 7.0.2001 + '@oxlint-tsgolint/darwin-x64': 7.0.2001 + '@oxlint-tsgolint/linux-arm64': 7.0.2001 + '@oxlint-tsgolint/linux-x64': 7.0.2001 + '@oxlint-tsgolint/win32-arm64': 7.0.2001 + '@oxlint-tsgolint/win32-x64': 7.0.2001 + + oxlint@1.75.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.74.0 - '@oxlint/binding-android-arm64': 1.74.0 - '@oxlint/binding-darwin-arm64': 1.74.0 - '@oxlint/binding-darwin-x64': 1.74.0 - '@oxlint/binding-freebsd-x64': 1.74.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.74.0 - '@oxlint/binding-linux-arm-musleabihf': 1.74.0 - '@oxlint/binding-linux-arm64-gnu': 1.74.0 - '@oxlint/binding-linux-arm64-musl': 1.74.0 - '@oxlint/binding-linux-ppc64-gnu': 1.74.0 - '@oxlint/binding-linux-riscv64-gnu': 1.74.0 - '@oxlint/binding-linux-riscv64-musl': 1.74.0 - '@oxlint/binding-linux-s390x-gnu': 1.74.0 - '@oxlint/binding-linux-x64-gnu': 1.74.0 - '@oxlint/binding-linux-x64-musl': 1.74.0 - '@oxlint/binding-openharmony-arm64': 1.74.0 - '@oxlint/binding-win32-arm64-msvc': 1.74.0 - '@oxlint/binding-win32-ia32-msvc': 1.74.0 - '@oxlint/binding-win32-x64-msvc': 1.74.0 - oxlint-tsgolint: 0.25.0 + '@oxlint/binding-android-arm-eabi': 1.75.0 + '@oxlint/binding-android-arm64': 1.75.0 + '@oxlint/binding-darwin-arm64': 1.75.0 + '@oxlint/binding-darwin-x64': 1.75.0 + '@oxlint/binding-freebsd-x64': 1.75.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.75.0 + '@oxlint/binding-linux-arm-musleabihf': 1.75.0 + '@oxlint/binding-linux-arm64-gnu': 1.75.0 + '@oxlint/binding-linux-arm64-musl': 1.75.0 + '@oxlint/binding-linux-ppc64-gnu': 1.75.0 + '@oxlint/binding-linux-riscv64-gnu': 1.75.0 + '@oxlint/binding-linux-riscv64-musl': 1.75.0 + '@oxlint/binding-linux-s390x-gnu': 1.75.0 + '@oxlint/binding-linux-x64-gnu': 1.75.0 + '@oxlint/binding-linux-x64-musl': 1.75.0 + '@oxlint/binding-openharmony-arm64': 1.75.0 + '@oxlint/binding-win32-arm64-msvc': 1.75.0 + '@oxlint/binding-win32-ia32-msvc': 1.75.0 + '@oxlint/binding-win32-x64-msvc': 1.75.0 + oxlint-tsgolint: 7.0.2001 p-cancelable@4.0.1: {} @@ -11051,7 +11056,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.21: + postcss@8.5.22: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -11980,7 +11985,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.21 + postcss: 8.5.22 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: From 1d8bac78eac9afa580c1b819fc7ee154f484a4c6 Mon Sep 17 00:00:00 2001 From: TonyRL Date: Wed, 22 Jul 2026 22:20:49 +0800 Subject: [PATCH 0390/1190] fix(config): fix macOS ua version --- lib/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/config.ts b/lib/config.ts index 077dcf19a642..f93bf95cc752 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -769,7 +769,7 @@ const calculateValue = () => { disableIPv6: toBoolean(envs.DISABLE_IPV6, false), requestRetry: toInt(envs.REQUEST_RETRY, 2), // 请求失败重试次数 requestTimeout: toInt(envs.REQUEST_TIMEOUT, 30000), // Milliseconds to wait for the server to end the response before aborting the request - ua: envs.UA || (toBoolean(envs.NO_RANDOM_UA, false) ? TRUE_UA : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_6_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'), + ua: envs.UA || (toBoolean(envs.NO_RANDOM_UA, false) ? TRUE_UA : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'), isDefaultUA: !envs.UA && !toBoolean(envs.NO_RANDOM_UA, false), trueUA: TRUE_UA, allowOrigin: envs.ALLOW_ORIGIN, From 8a9a64ad16cec6b1ea8fa1e7f33f9ce808d03d64 Mon Sep 17 00:00:00 2001 From: TonyRL Date: Wed, 22 Jul 2026 22:30:24 +0800 Subject: [PATCH 0391/1190] chore: fix reopening pr --- .github/workflows/lint.yml | 7 +++++++ scripts/workflow/test-route/identify.mjs | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a22f82fa636e..94cb10f25ade 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -81,6 +81,7 @@ jobs: timeout-minutes: 5 steps: - name: Check if PR author is denounced + id: vouch uses: mitchellh/vouch/action/check-pr@d66fa29a64600490892131ad87597c30c91fcac4 # v1.5.0 with: pr-number: ${{ github.event.pull_request.number }} @@ -88,6 +89,12 @@ jobs: require-vouch: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Label denounced PR as spam + if: ${{ steps.vouch.outputs.status == 'closed' }} + run: gh pr edit ${{ github.event.pull_request.number }} --add-label spam + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} agentscan: name: AgentScan diff --git a/scripts/workflow/test-route/identify.mjs b/scripts/workflow/test-route/identify.mjs index a9e12159a685..ba947996a435 100644 --- a/scripts/workflow/test-route/identify.mjs +++ b/scripts/workflow/test-route/identify.mjs @@ -102,6 +102,10 @@ export default async function identify({ github, context, core }, body, number, }; if (isPR) { + if (issue.labels.some((e) => e.name === 'spam')) { + core.info('PR labeled as spam, skipping'); + return; + } if (issue.state === 'closed') { await updatePrState('open'); } From 588cb9682a92d16fe34db686e90e43cff642dc01 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Thu, 23 Jul 2026 00:51:03 +0800 Subject: [PATCH 0392/1190] feat(route): add Chongqing China Three Gorges Museum temp exhibition route (#22755) --- lib/routes/3gmuseum/exhibition.tsx | 121 +++++++++++++++++++++++++++++ lib/routes/3gmuseum/namespace.ts | 10 +++ 2 files changed, 131 insertions(+) create mode 100644 lib/routes/3gmuseum/exhibition.tsx create mode 100644 lib/routes/3gmuseum/namespace.ts diff --git a/lib/routes/3gmuseum/exhibition.tsx b/lib/routes/3gmuseum/exhibition.tsx new file mode 100644 index 000000000000..eb428c792e69 --- /dev/null +++ b/lib/routes/3gmuseum/exhibition.tsx @@ -0,0 +1,121 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +export const route: Route = { + path: '/tempexhibition', + categories: ['travel'], + example: '/3gmuseum/tempexhibition', + name: 'Temporary Exhibition', + maintainers: ['magazian'], + radar: [ + { + source: ['www.3gmuseum.cn/web/column/col5009287.html'], + target: '/tempexhibition', + }, + ], + handler: async () => { + const baseUrl = 'https://www.3gmuseum.cn'; + const apiUrl = `${baseUrl}/web/column/col5009287.html`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: apiUrl, + }); + + // The data is embedded in the HTML as a JSON string inside a script tag, find the 'var newsPageTotal = {...}' part + const idx = response.data.indexOf('var newsPageTotal = '); + const endIdx = response.data.indexOf('\n', idx); + const str = response.data.slice(idx + 'var newsPageTotal = '.length, endIdx).trim(); + + const data = JSON.parse(str); + const list: any[] = data?.list; + + const items = list.map((item) => { + const article = item.article; + const itemLink = article.pcUrl; + const $listContent = load(article.content); + const locationText = $listContent('p:contains("地点:")').text(); + const location = locationText.replace(/.*?(?:展览|展出)?地点:/, '').trim(); + const fullDurationText = $listContent('p:contains("时间:")').text(); + const fullDuration = fullDurationText.replace(/.*?(?:展览|展出)?时间:?/, '').trim(); + + let startDate; + let endDate; + + if (fullDuration) { + // Typical format: 2026年2月10日-5月18日 or 2026年1月27日——2027年1月17日 or 2025.6.18-2026.1.5 + const dateMatch = fullDuration.match(/(\d{4}[年.]\d{1,2}[月.]\d{1,2}日?)\s*[—\-~至]+\s*(\d{4}[年.])?(\d{1,2}[月.]\d{1,2}日?)/); + if (dateMatch) { + const startStr = dateMatch[1].replaceAll(/[年月.]/g, '-').replace('日', ''); + const endYear = dateMatch[2] ? dateMatch[2].replaceAll(/[年.]/g, '-') : startStr.slice(0, 5); + const endStr = endYear + dateMatch[3].replaceAll(/[月.]/g, '-').replace('日', ''); + + startDate = dayjs(startStr).format('YYYY-MM-DD'); + endDate = dayjs(endStr).format('YYYY-MM-DD'); + } + } else { + // extend_field_starttime may differ from the fullDuration, usually 1 day earlier, so parse fullDuration first, then fallback to extend_field_starttime and extend_field_endtime if fullDuration is not available + const formMap = article.formMap; + startDate = dayjs(formMap.extend_field_starttime).format('YYYY-MM-DD'); + endDate = dayjs(formMap.extend_field_endtime).format('YYYY-MM-DD'); + } + + const pubDate = timezone(parseDate(item.pubTime), 8); + const title = item.listTitle; + const imgUrl = `https:${item.listImage}`; + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + + return { + title, + link: itemLink, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }; + }); + + return { + title: `${museumName} - 临时展览`, + link: apiUrl, + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/3gmuseum/namespace.ts b/lib/routes/3gmuseum/namespace.ts new file mode 100644 index 000000000000..06ba5da30374 --- /dev/null +++ b/lib/routes/3gmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Chongqing China Three Gorges Museum', + url: 'www.3gmuseum.cn', + + zh: { + name: '重庆中国三峡博物馆', + }, +}; From 5424351562c1b70d7f8395761e3d9769430d4335 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Thu, 23 Jul 2026 05:37:50 +0800 Subject: [PATCH 0393/1190] feat(route): add Tianjin Museum News and Exhibit route (#22785) --- lib/routes/tjbwg/exhibition.tsx | 133 ++++++++++++++++++++++++++++++++ lib/routes/tjbwg/namespace.ts | 9 +++ lib/routes/tjbwg/news.ts | 53 +++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 lib/routes/tjbwg/exhibition.tsx create mode 100644 lib/routes/tjbwg/namespace.ts create mode 100644 lib/routes/tjbwg/news.ts diff --git a/lib/routes/tjbwg/exhibition.tsx b/lib/routes/tjbwg/exhibition.tsx new file mode 100644 index 000000000000..b26c66614382 --- /dev/null +++ b/lib/routes/tjbwg/exhibition.tsx @@ -0,0 +1,133 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +// Convert YYYY年M月D日 to YYYY-MM-DD +const formatDate = (dateString: string | undefined) => { + if (!dateString) { + return; + } + + const date = dayjs(dateString, 'YYYY年M月D日'); + return date.format('YYYY-MM-DD'); +}; + +// used for 2026年7月1日-9月30日【在展】 or 2026年5月18日【在展】 +const parseExhibitionDuration = (duration: string) => { + if (!duration) { + return { startDate: undefined, endDate: undefined }; + } + + const cleanStr = duration.replaceAll(/【.*?】/g, '').replaceAll(/\s+/g, ''); // Remove【在展】 + const parts = cleanStr.split('-'); + const startDateRaw = parts[0]; + let endDateRaw = parts[1]; + + if (endDateRaw && !endDateRaw.includes('年')) { + const yearMatch = startDateRaw.match(/^\d{4}年/); + if (yearMatch) { + endDateRaw = yearMatch[0] + endDateRaw; + } + } + + return { startDate: formatDate(startDateRaw), endDate: formatDate(endDateRaw) }; +}; + +export const route: Route = { + path: '/exhibition', + categories: ['travel'], + example: '/tjbwg/exhibition', + radar: [ + { + source: ['www.tjbwg.cn/cn/ExhibitionList.aspx'], + target: '/exhibition', + }, + ], + name: 'Temporary Exhibition', + maintainers: ['magazian'], + handler: async () => { + const baseUrl = 'https://www.tjbwg.cn'; + const listUrl = `${baseUrl}/cn/ExhibitionList.aspx?TypeId=10939`; + const museumName = namespace.zh?.name ?? namespace.name; + + const response = await ofetch(listUrl); + const $ = load(response); + + const items: DataItem[] = $('.exhList2 ul li') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('a'); + const href = a.attr('href') ?? ''; + const link = new URL(href, `${baseUrl}/cn/`).href; + const title = a.find('.text h3').text(); + const rawImgSrc = a.find('.img img').attr('src') ?? ''; + const imgUrl = new URL(rawImgSrc, baseUrl).href ?? ''; + const location = a + .find('p') + .first() + .text() + .trim() + .replace(/^地点:/, ''); + const fullDuration = a + .find('p') + .last() + .text() + .trim() + .replace(/^展期:/, ''); + + const { startDate, endDate } = parseExhibitionDuration(fullDuration); + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + + return { + title, + link, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + } as DataItem; + }); + + return { + title: `${museumName} - 临时展览`, + link: listUrl, + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/tjbwg/namespace.ts b/lib/routes/tjbwg/namespace.ts new file mode 100644 index 000000000000..a7d3c60c900d --- /dev/null +++ b/lib/routes/tjbwg/namespace.ts @@ -0,0 +1,9 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Tianjin Museum', + url: 'www.tjbwg.cn', + zh: { + name: '天津博物馆', + }, +}; diff --git a/lib/routes/tjbwg/news.ts b/lib/routes/tjbwg/news.ts new file mode 100644 index 000000000000..e28750ff1690 --- /dev/null +++ b/lib/routes/tjbwg/news.ts @@ -0,0 +1,53 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +export const route: Route = { + path: '/news', + categories: ['travel'], + example: '/tjbwg/news', + name: 'News', + maintainers: ['magazian'], + radar: [ + { + source: ['www.tjbwg.cn/cn/NewsList.aspx'], + target: '/news', + }, + ], + handler: async () => { + const baseUrl = 'https://www.tjbwg.cn'; + const listUrl = `${baseUrl}/cn/NewsList.aspx?TypeId=10926`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await ofetch(listUrl); + const $ = load(response); + + const list = $('.newsList1 ul li') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('a'); + const href = a.attr('href') || ''; + const link = new URL(href, `${baseUrl}/cn/`).href; + const title = a.find('h3').text(); + const dateText = a.find('.time').text().trim(); + + return { + title, + link, + pubDate: parseDate(dateText), + }; + }); + + return { + title: `${museumName} - 最新公告`, + link: listUrl, + language: 'zh-CN', + item: list, + }; + }, +}; From e431b726e90b6101a08a8b91faa57185be4a71e9 Mon Sep 17 00:00:00 2001 From: "Jagger.H" Date: Thu, 23 Jul 2026 05:58:04 +0800 Subject: [PATCH 0394/1190] fix(route/163): add radar rules for NetEase Cloud Music routes (#22788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 163 routes carry no radar rules, so RSSHub Radar cannot suggest them from a music.163.com page. This adds one rule per route, each pointing at the URL that route already builds in its own `link`. `user/playlist` declares `:uid` in its path while the page URL carries `?id=`, so its target binds `:id` — the same convention as acfun/video.ts (path `:uid`, source/target `:id`). --- lib/routes/163/music/artist-songs.ts | 7 +++++++ lib/routes/163/music/artist.ts | 7 +++++++ lib/routes/163/music/djradio.tsx | 7 +++++++ lib/routes/163/music/playlist.ts | 7 +++++++ lib/routes/163/music/userevents.tsx | 6 ++++++ lib/routes/163/music/userplaylist.tsx | 7 +++++++ 6 files changed, 41 insertions(+) diff --git a/lib/routes/163/music/artist-songs.ts b/lib/routes/163/music/artist-songs.ts index 3ebf5cb07b47..f1becb7ef578 100644 --- a/lib/routes/163/music/artist-songs.ts +++ b/lib/routes/163/music/artist-songs.ts @@ -12,10 +12,17 @@ export const route: Route = { requireConfig: false, requirePuppeteer: false, antiCrawler: false, + supportRadar: true, supportBT: false, supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['music.163.com/artist'], + target: '/music/artist/songs/:id', + }, + ], name: '歌手歌曲', maintainers: ['ZhongMingKun'], handler, diff --git a/lib/routes/163/music/artist.ts b/lib/routes/163/music/artist.ts index 4f4b6edeccb5..5b6e1dd3dba8 100644 --- a/lib/routes/163/music/artist.ts +++ b/lib/routes/163/music/artist.ts @@ -12,10 +12,17 @@ export const route: Route = { requireConfig: false, requirePuppeteer: false, antiCrawler: false, + supportRadar: true, supportBT: false, supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['music.163.com/artist/album'], + target: '/music/artist/:id', + }, + ], name: '歌手专辑', maintainers: ['metowolf'], handler, diff --git a/lib/routes/163/music/djradio.tsx b/lib/routes/163/music/djradio.tsx index ab876b54b95b..19cdcf1840b3 100644 --- a/lib/routes/163/music/djradio.tsx +++ b/lib/routes/163/music/djradio.tsx @@ -15,10 +15,17 @@ export const route: Route = { requireConfig: false, requirePuppeteer: false, antiCrawler: false, + supportRadar: true, supportBT: false, supportPodcast: true, supportScihub: false, }, + radar: [ + { + source: ['music.163.com/djradio'], + target: '/music/djradio/:id', + }, + ], name: '电台节目', maintainers: ['magic-akari'], handler, diff --git a/lib/routes/163/music/playlist.ts b/lib/routes/163/music/playlist.ts index 173b3b308b93..0c9eca8f2570 100644 --- a/lib/routes/163/music/playlist.ts +++ b/lib/routes/163/music/playlist.ts @@ -19,10 +19,17 @@ export const route: Route = { ], requirePuppeteer: false, antiCrawler: true, + supportRadar: true, supportBT: false, supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['music.163.com/playlist'], + target: '/music/playlist/:id', + }, + ], name: '歌单歌曲', maintainers: ['DIYgod'], handler, diff --git a/lib/routes/163/music/userevents.tsx b/lib/routes/163/music/userevents.tsx index 6174be14b4fd..911f0df8c403 100644 --- a/lib/routes/163/music/userevents.tsx +++ b/lib/routes/163/music/userevents.tsx @@ -23,6 +23,12 @@ const renderDescription = ({ description, pics }) => { export const route: Route = { path: '/music/user/events/:id', categories: ['multimedia'], + radar: [ + { + source: ['music.163.com/user/event'], + target: '/music/user/events/:id', + }, + ], name: '用户动态', maintainers: ['Master-Hash'], handler, diff --git a/lib/routes/163/music/userplaylist.tsx b/lib/routes/163/music/userplaylist.tsx index 4dcf677fb42d..381b08699aa8 100644 --- a/lib/routes/163/music/userplaylist.tsx +++ b/lib/routes/163/music/userplaylist.tsx @@ -31,10 +31,17 @@ export const route: Route = { requireConfig: false, requirePuppeteer: false, antiCrawler: false, + supportRadar: true, supportBT: false, supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['music.163.com/user/home'], + target: '/music/user/playlist/:id', + }, + ], name: '用户歌单', maintainers: ['DIYgod'], handler, From a6e8b8781dc2a345f7ddc5120302ee28edd38760 Mon Sep 17 00:00:00 2001 From: Zifan Hua Date: Thu, 23 Jul 2026 00:33:20 +0000 Subject: [PATCH 0395/1190] =?UTF-8?q?feat(route):=20add=20route=20for=20?= =?UTF-8?q?=E6=B5=99=E6=B1=9F=E5=A4=A7=E5=AD=A6=E6=95=B0=E5=AD=A6=E7=A7=91?= =?UTF-8?q?=E5=AD=A6=E9=99=A2=20(#22464)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(route): add route for 浙江大学数学科学院 * fix: adjust publication date parsing to account for timezone * fix: refine news item selection in fetchNewsItemsByCategory function * fix: simplify author and publication date extraction in enrichNewsItemWithDetails function * fix: update fetchNewsItemsByCategory to handle filter links only accessible from intranet * fix: simplify filtering logic in fetchNewsItemsByCategory function --- lib/routes/zju/math/index.ts | 144 +++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 lib/routes/zju/math/index.ts diff --git a/lib/routes/zju/math/index.ts b/lib/routes/zju/math/index.ts new file mode 100644 index 000000000000..9cc43bc85b6e --- /dev/null +++ b/lib/routes/zju/math/index.ts @@ -0,0 +1,144 @@ +import { load } from 'cheerio'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const base = 'http://www.math.zju.edu.cn/'; + +type NewsItem = { + item: DataItem; + intranetOnly: boolean; +}; + +const categoryMap = new Map([ + [0, { id: 'zytz/list.htm', title: '浙江大学数学科学院-重要通知' }], + [1, { id: 'bkstz/list.htm', title: '浙江大学数学科学院-本科生' }], + [2, { id: 'yjstz/list.htm', title: '浙江大学数学科学院-研究生' }], + [3, { id: 'kytz/list.htm', title: '浙江大学数学科学院-科研' }], + [4, { id: 'jxtz/list.htm', title: '浙江大学数学科学院-教学' }], + [5, { id: '38069/list.htm', title: '浙江大学数学科学院-人事' }], + [6, { id: 'gs/list.htm', title: '浙江大学数学科学院-公示' }], +]); + +async function fetchNewsItemsByCategory(categoryId: string): Promise { + const response = await got({ + method: 'get', + url: new URL(categoryId, base).href, + }); + + const $ = load(response.data); + + return $('.news_list #wp_news_w12 li') + .toArray() + .map((item): NewsItem | null => { + const element = $(item); + const link = element.find('a[href]').first().attr('href'); + const titleNode = element.find('.title'); + // if the title node contains an image with src "/_images/news/icon/unopen.gif", + // it indicates that the news item is only accessible from the intranet + const intranetOnly = titleNode.find('img[src="/_images/news/icon/unopen.gif"]').length > 0; + const title = titleNode.text().trim() || element.find('a[href]').first().attr('title'); + const dateText = `${element.find('.date .y').text().trim()}-${element.find('.date .d').text().trim()}`; + + // If the title or link is missing, we skip this item as it is likely not a valid news entry. + if (!(title && link)) { + return null; + } + + return { + item: { + title, + link: new URL(link, base).href, + pubDate: timezone(parseDate(dateText), 8), + }, + intranetOnly, + }; + }) + .filter(Boolean) as NewsItem[]; +} + +async function enrichNewsItemWithDetails(item: NewsItem, refererUrl: string): Promise { + const dataItem = item.item as DataItem; + + if (item.intranetOnly || !dataItem.link) { + return dataItem; + } + + return await cache.tryGet(dataItem.link, async () => { + try { + const response = await got({ + method: 'get', + url: dataItem.link, + headers: { + Referer: refererUrl, + }, + }); + + const $ = load(response.data); + const description = $('.wp_articlecontent').html(); + const infoText = $('.item_info').text(); + const [, author, pubDate] = infoText.match(/来源:([\s\S]*?)发布时间:(\d{4}-\d{2}-\d{2})/) ?? []; + + if (description) { + dataItem.description = description; + } + + if (author) { + dataItem.author = author; + } + + if (pubDate) { + dataItem.pubDate = timezone(parseDate(pubDate), 8); + } + + return dataItem; + } catch { + return dataItem; + } + }); +} + +export const route: Route = { + path: '/math/:type', + categories: ['university'], + example: '/zju/math/0', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '数学科学学院', + description: `| 重要通知 | 本科生 | 研究生 | 科研 | 教学 | 人事 | 公示 | +| -------- | ------ | ------ | ---- | ---- | ---- | ---- | +| 0 | 1 | 2 | 3 | 4 | 5 | 6 |`, + maintainers: ['Alex222222222222'], + handler, + url: 'www.math.zju.edu.cn', +}; + +async function handler(ctx: { req: { param: (arg0: string) => string } }) { + const type = Math.trunc(Number(ctx.req.param('type'))); + const categoryInfo = categoryMap.get(type); + + if (!categoryInfo) { + const validTypes = [...categoryMap.keys().toArray()].join(', '); + throw new Error(`Invalid type: ${type}. Valid types are: ${validTypes}`); + } + + const categoryUrl = new URL(categoryInfo.id, base).href; + const newsItems = await fetchNewsItemsByCategory(categoryInfo.id); + const items = await Promise.all(newsItems.map((item) => enrichNewsItemWithDetails(item, categoryUrl))); + + return { + title: categoryInfo.title, + link: categoryUrl, + item: items, + }; +} From 41929e66c21099119e3b175e3620612a3b28f11d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:04:32 +0800 Subject: [PATCH 0396/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22805) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260722.1 to 5.20260723.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260723.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 838e11ba7636..3f113deaf4cb 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.7", - "@cloudflare/workers-types": "5.20260722.1", + "@cloudflare/workers-types": "5.20260723.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.75.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 169f2b85851a..be5b5f38c4a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.7 - version: 0.18.7(@cloudflare/workers-types@5.20260722.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.7(@cloudflare/workers-types@5.20260723.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260722.1 - version: 5.20260722.1 + specifier: 5.20260723.1 + version: 5.20260723.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.113.0 - version: 4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -641,8 +641,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260722.1': - resolution: {integrity: sha512-8+kivCgFGzwrAfNOWgSpzy/VDvmT/i5KWBgQhnygv3d1kajNn6mCYTbLKpouG0aY8mXjhv+IQm1a8r2K/H4pqQ==} + '@cloudflare/workers-types@5.20260723.1': + resolution: {integrity: sha512-+P5tDo+ZjW5nmWq/zagLVIyV8U/mSk7n8n0mLUHjn3pOdY7kdEDmURUxvhwwYqLJ/VPnyMJPAYtZjHbqAnIpDg==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -6630,7 +6630,7 @@ snapshots: optionalDependencies: workerd: 1.20260721.1 - '@cloudflare/vitest-pool-workers@0.18.7(@cloudflare/workers-types@5.20260722.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.7(@cloudflare/workers-types@5.20260723.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6638,7 +6638,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6660,7 +6660,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260721.1': optional: true - '@cloudflare/workers-types@5.20260722.1': {} + '@cloudflare/workers-types@5.20260723.1': {} '@colors/colors@1.6.0': {} @@ -12108,7 +12108,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260721.1 '@cloudflare/workerd-windows-64': 1.20260721.1 - wrangler@4.113.0(@cloudflare/workers-types@5.20260722.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260721.1) @@ -12119,7 +12119,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260721.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260722.1 + '@cloudflare/workers-types': 5.20260723.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 33908a122750d9bdf82ce51833a36dcbdc617c5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:49:14 +0800 Subject: [PATCH 0397/1190] chore(deps): bump imapflow from 1.4.8 to 1.5.0 (#22806) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.4.8 to 1.5.0. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.4.8...v1.5.0) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 3f113deaf4cb..90e9baf09bd9 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.4.8", + "imapflow": "1.5.0", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be5b5f38c4a0..28c4742b1090 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.4.8 - version: 1.4.8 + specifier: 1.5.0 + version: 1.5.0 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -3676,7 +3676,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -4345,8 +4345,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.4.8: - resolution: {integrity: sha512-cHa23g7h0X04a2v+H9luLg0GWMQ0vApmQ+UxL5swglOgBypNpqxurLMa1JS9mjpTekCsQhKrCZwaziPRBbHp8Q==} + imapflow@1.5.0: + resolution: {integrity: sha512-ayj2xIpRpXT9nXlAQQDhfm694faQxEmfAzRYL881q3YGRR5ofyIWsG3l3Lf7oSThxgLwZ/EQ5kh/nLnKBud3LQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -9817,7 +9817,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.4.8: + imapflow@1.5.0: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 From eeff5a386963bd93ce6d1082d31c3f4bcee33fc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:55:58 +0800 Subject: [PATCH 0398/1190] chore(deps): bump devenv from `ef185b5` to `9767a1f` (#22807) Bumps [devenv](https://github.com/cachix/devenv) from `ef185b5` to `9767a1f`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/ef185b5a38445777194bc8469fc1cdebd99e7dbc...9767a1f458fbd99b487dbb600a130917d4cd2b13) --- updated-dependencies: - dependency-name: devenv dependency-version: 9767a1f458fbd99b487dbb600a130917d4cd2b13 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index c72b5d9330b6..c71b955dca92 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784696310, - "narHash": "sha256-EeZH7UbjqoJHHrN1DkV32DxZ8OMGh/2kla94lpO17eE=", + "lastModified": 1784758116, + "narHash": "sha256-vcjRSkJHM5/2rLmtCZBhms/WDJpyPNXW9Gi6xeSfc/Y=", "owner": "cachix", "repo": "devenv", - "rev": "ef185b5a38445777194bc8469fc1cdebd99e7dbc", + "rev": "9767a1f458fbd99b487dbb600a130917d4cd2b13", "type": "github" }, "original": { From d454ef8f4733f60275eaebf56360cdcbd6ff4f7d Mon Sep 17 00:00:00 2001 From: powerfullz <61956140+powerfullz@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:17:31 +0800 Subject: [PATCH 0399/1190] fix(route/csust): various fixes and improvements (#22704) * fix(route/csust): self-contained routes, full title extraction, absolute URLs - Remove factory pattern from utils.ts, keep only getNoticeContent helper - Each route (tggs/xkxs) now self-contained with inline list parsing - Extract full title from article page tag to avoid truncation - Convert relative URLs to absolute in article content * fix(route/csust): remove redundant logics --- lib/routes/csust/tggs.ts | 51 ++++++++++++++++------ lib/routes/csust/utils.ts | 92 +++++++-------------------------------- lib/routes/csust/xkxs.ts | 51 ++++++++++++++++------ 3 files changed, 90 insertions(+), 104 deletions(-) diff --git a/lib/routes/csust/tggs.ts b/lib/routes/csust/tggs.ts index 38d44b98a6c7..6da0a2579cc5 100644 --- a/lib/routes/csust/tggs.ts +++ b/lib/routes/csust/tggs.ts @@ -1,25 +1,48 @@ -import type { Route } from '@/types'; +import { load } from 'cheerio'; -import { createCsustHandler } from './utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; -const handler = createCsustHandler({ - listPath: '/tggs.htm', - feedTitle: '长沙理工大学 - 通告公示', - feedDescription: '长沙理工大学通告公示', -}); +import { getNoticeContent } from './utils'; + +const baseUrl = 'https://www.csust.edu.cn'; +const listPath = '/tggs.htm'; + +async function handler(): Promise<Data> { + const response = await got(`${baseUrl}${listPath}`); + const $ = load(response.body); + + const items: Array<DataItem & { link: string }> = $('.list ul li') + .toArray() + .map((li) => { + const $li = $(li); + + return { + title: $li.find('.newTitle').text().trim(), + link: new URL($li.find('a').attr('href')!, baseUrl).href, + pubDate: timezone(parseDate($li.find('.data1').text().trim(), '发布时间 : YYYY-MM-DD'), 8), + }; + }); + + const enrichedItems = await Promise.all(items.map((item) => cache.tryGet(item.link, () => getNoticeContent(item)))); + + return { + title: '长沙理工大学 - 通告公示', + link: `${baseUrl}${listPath}`, + description: '长沙理工大学通告公示', + item: enrichedItems, + }; +} export const route: Route = { path: '/tggs', categories: ['university'], example: '/csust/tggs', - parameters: {}, features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, - supportBT: false, - supportPodcast: false, - supportScihub: false, + supportRadar: true, }, radar: [ { diff --git a/lib/routes/csust/utils.ts b/lib/routes/csust/utils.ts index 5e69f612dc8b..ef9638ac1578 100644 --- a/lib/routes/csust/utils.ts +++ b/lib/routes/csust/utils.ts @@ -1,86 +1,26 @@ -import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; -import cache from '@/utils/cache'; +import type { DataItem } from '@/types'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; -// 抓取并清清理内容 -export async function getNoticeContent(item: any) { +type NoticeItem = DataItem & { link: string }; + +export async function getNoticeContent(item: NoticeItem): Promise<NoticeItem> { const response = await got(item.link); const $ = load(response.body); + const pageTitle = $('title').text(); const $content = $('.v_news_content'); - - if ($content.length) { - // 移除无用元素 - $content.find('script').remove(); - $content.find('style').remove(); - $content.find('.vsbcontent_end').remove(); - $content.find('iframe').remove(); - item.description = $content.html() || item.title; - } else { - item.description = item.title; - } - - return item; -} - -// 解析列表页面,返回包含标题、链接与发布日期的条目 -export function parseListItems($: CheerioAPI, baseUrl: string) { - return $('.list ul li') - .toArray() - .map((li) => { - const element = $(li); - const title = element.find('.newTitle').text().trim(); - const linkRaw = element.find('a').attr('href'); - const dateText = element.find('.data1').text().trim(); - - if (!linkRaw || !title) { - return null; - } - - const dateMatch = dateText.match(/发布时间\s*[::]\s*(\d{4}-\d{1,2}-\d{1,2})/); - const pubDate = dateMatch ? parseDate(dateMatch[1]) : null; - - // 使用 URL 构造函数确保正确拼接 URL - const link = linkRaw.startsWith('http') ? linkRaw : new URL(linkRaw, baseUrl).href; - - return { - title, - link, - pubDate, - } as any; - }) - .filter((i) => i !== null) as any[]; -} - -// 通用处理器工厂:根据栏目路径与标题信息生成 handler -export function createCsustHandler({ listPath, feedTitle, feedDescription }: { listPath: string; feedTitle: string; feedDescription: string }) { - const baseUrl = 'https://www.csust.edu.cn'; - return async function () { - const response = await got(`${baseUrl}${listPath}`); - const $ = load(response.body); - - const items = parseListItems($, baseUrl); - - const item = await Promise.all( - items.map((it) => - cache.tryGet(it.link, async () => { - try { - return await getNoticeContent(it); - } catch { - return it; - } - }) - ) - ); - - return { - title: feedTitle, - link: `${baseUrl}${listPath}`, - description: feedDescription, - item, - }; + $content.find('script, style, .vsbcontent_end').remove(); + $content.find('img[src], a[href]').each((_, element) => { + const $element = $(element); + const attribute = element.tagName === 'img' ? 'src' : 'href'; + $element.attr(attribute, new URL($element.attr(attribute)!, item.link).href); + }); + + return { + ...item, + title: pageTitle.slice(0, pageTitle.lastIndexOf('-')).trim(), + description: $content.html()!, }; } diff --git a/lib/routes/csust/xkxs.ts b/lib/routes/csust/xkxs.ts index fa641b293bcc..2149799f52b1 100644 --- a/lib/routes/csust/xkxs.ts +++ b/lib/routes/csust/xkxs.ts @@ -1,25 +1,48 @@ -import type { Route } from '@/types'; +import { load } from 'cheerio'; -import { createCsustHandler } from './utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; -const handler = createCsustHandler({ - listPath: '/xkxs.htm', - feedTitle: '长沙理工大学 - 学科学术', - feedDescription: '长沙理工大学学科学术', -}); +import { getNoticeContent } from './utils'; + +const baseUrl = 'https://www.csust.edu.cn'; +const listPath = '/xkxs.htm'; + +async function handler(): Promise<Data> { + const response = await got(`${baseUrl}${listPath}`); + const $ = load(response.body); + + const items: Array<DataItem & { link: string }> = $('.list ul li') + .toArray() + .map((li) => { + const $li = $(li); + + return { + title: $li.find('.newTitle').text().trim(), + link: new URL($li.find('a').attr('href')!, baseUrl).href, + pubDate: timezone(parseDate($li.find('.data1').text().trim(), '发布时间 : YYYY-MM-DD'), 8), + }; + }); + + const enrichedItems = await Promise.all(items.map((item) => cache.tryGet(item.link, () => getNoticeContent(item)))); + + return { + title: '长沙理工大学 - 学科学术', + link: `${baseUrl}${listPath}`, + description: '长沙理工大学学科学术', + item: enrichedItems, + }; +} export const route: Route = { path: '/xkxs', categories: ['university'], example: '/csust/xkxs', - parameters: {}, features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, - supportBT: false, - supportPodcast: false, - supportScihub: false, + supportRadar: true, }, radar: [ { From b0e556e1185b1fcae607744166d7f929f75a283c Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:32:56 +0800 Subject: [PATCH 0400/1190] feat(route): add capital museum route for exhibition and news (#22687) * feat: add capital museum route for exhibition and news * fix:update ExhibitionListItem * fix: update .html() to .text() and news apiUrl Co-authored-by: Tony <TonyRL@users.noreply.github.com> ------- --- lib/routes/capitalmuseum/exhibition.tsx | 170 ++++++++++++++++++++++++ lib/routes/capitalmuseum/namespace.ts | 10 ++ lib/routes/capitalmuseum/news.ts | 96 +++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 lib/routes/capitalmuseum/exhibition.tsx create mode 100644 lib/routes/capitalmuseum/namespace.ts create mode 100644 lib/routes/capitalmuseum/news.ts diff --git a/lib/routes/capitalmuseum/exhibition.tsx b/lib/routes/capitalmuseum/exhibition.tsx new file mode 100644 index 000000000000..b540ab5c9b85 --- /dev/null +++ b/lib/routes/capitalmuseum/exhibition.tsx @@ -0,0 +1,170 @@ +import { load } from 'cheerio'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +// parse exhibition date string like "2024年5月1日 - 2024年6月30日" or "2024年5月1日-" or "2024年5月1日 - 6月30日" +const parseExhibitionDates = (fullDuration: string | undefined) => { + let startDate: string | undefined; + let endDate: string | undefined; + + if (fullDuration) { + // Regex to capture start and optional end dates. + const dateRegex = /(\d{4})年(\d{1,2})月(\d{1,2})日(?:\D+(?:(\d{4})年)?(\d{1,2})月(\d{1,2})日)?/; + const match = fullDuration.match(dateRegex); + + if (match) { + const startYear = match[1]; + startDate = `${startYear}-${match[2].padStart(2, '0')}-${match[3].padStart(2, '0')}`; + + if (match[5] && match[6]) { + const endYear = match[4] || startYear; // If end year is not present, use start year + endDate = `${endYear}-${match[5].padStart(2, '0')}-${match[6].padStart(2, '0')}`; + } + } + } + + return { startDate, endDate }; +}; + +export const route: Route = { + path: '/exhibition/:type?', + categories: ['travel'], + example: '/capitalmuseum/exhibition', + parameters: { + type: 'Exhibition type, supported values: new(最新展览), review(展览回顾), default: All exhibitions.', + }, + name: 'Exhibitions', + maintainers: ['magazian'], + radar: [ + { + source: ['www.capitalmuseum.org.cn/exhibition'], + target: '/exhibition', + }, + ], + + handler: async (ctx) => { + const typeParam = ctx.req.param('type') || 'all'; + + const typeMap: Record<string, string> = { + new: '最新展览', + review: '展览回顾', + }; + + const baseUrl = 'https://www.capitalmuseum.org.cn'; + const apiUrl = `${baseUrl}/exhibition`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: apiUrl, + }); + + const $ = load(response.data); + const nuxtDataStr = $('#__NUXT_DATA__').text() || ''; // use __NUXT_DATA__ to get the data structure of the page + const nuxtData = JSON.parse(nuxtDataStr); + const targetType = typeMap[typeParam]; + + const exhibitionList = nuxtData.filter((item: any) => { + if (typeof item === 'object' && 'eid' in item) { + const itemType = nuxtData[item.cid]; + // when typeParam is 'all', include all items; otherwise, filter by the specific type + return typeParam === 'all' ? Object.values(typeMap).includes(itemType) : itemType === targetType; + } + return false; + }); + + interface ExhibitionListItem { + title: string; + itemlink: string; + imgUrl: string; + } + + const listItems: ExhibitionListItem[] = exhibitionList.map((item: any) => { + const eid = nuxtData[item.eid]; + const title = nuxtData[item.title]; + const link = `${apiUrl}/${eid}`; + const imgUrl = nuxtData[item.titileurl]; + + return { + title, + itemlink: link, + imgUrl, + }; + }); + + // get location and fullDuration from the detail page of each exhibition + const items: DataItem[] = await Promise.all( + listItems.map((item: ExhibitionListItem) => + cache.tryGet(item.itemlink, async () => { + const detailResponse = await got({ + method: 'get', + url: item.itemlink, + }); + + const detail$ = load(detailResponse.data); + const detailNuxtDataStr = detail$('#__NUXT_DATA__').text() || ''; + const detailNuxtData = JSON.parse(detailNuxtDataStr); + + const detailObj = detailNuxtData.find((obj: any) => typeof obj === 'object' && 'address' in obj); + + const location = detailNuxtData[detailObj.address]; + const fullDuration = detailNuxtData[detailObj.open_time]; + const { startDate, endDate } = parseExhibitionDates(fullDuration); + const pubDate = startDate ? timezone(parseDate(startDate, 'YYYY-MM-DD'), 8) : undefined; + + const description = renderToString( + <div> + <img src={item.imgUrl} /> + <br /> + <p> + <b>地点:</b> + {location || '参考详情'} + </p> + <p> + <b>开展:</b> + {startDate || '未定/常设'} + </p> + <p> + <b>闭展:</b> + {endDate || '未定/常设'} + </p> + {fullDuration && ( + <p> + <small>原始展期:{fullDuration}</small> + </p> + )} + </div> + ); + + return { + title: item.title, + link: item.itemlink, + pubDate, + description, + // For further .ics file processing + _extra: { + museumName, + location, + startDate, + endDate, + }, + } as DataItem; + }) + ) + ); + + return { + title: `${museumName} - 展览陈列${targetType ? ` - ${targetType}` : ''}`, + link: apiUrl, + language: 'zh-CN', + item: items as DataItem[], + }; + }, +}; diff --git a/lib/routes/capitalmuseum/namespace.ts b/lib/routes/capitalmuseum/namespace.ts new file mode 100644 index 000000000000..2b705a4421e7 --- /dev/null +++ b/lib/routes/capitalmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Capital Museum', + url: 'www.capitalmuseum.org.cn/', + + zh: { + name: '首都博物馆', + }, +}; diff --git a/lib/routes/capitalmuseum/news.ts b/lib/routes/capitalmuseum/news.ts new file mode 100644 index 000000000000..c78af5622e66 --- /dev/null +++ b/lib/routes/capitalmuseum/news.ts @@ -0,0 +1,96 @@ +import { load } from 'cheerio'; + +import type { DataItem, Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +export const route: Route = { + path: '/news/:type?', + categories: ['travel'], + example: '/capitalmuseum/news/notice', + parameters: { + type: 'News type, supported values: news(新闻资讯), notice(通知公告). Default: All news.', + }, + name: 'News', + maintainers: ['magazian'], + radar: [ + { + source: ['www.capitalmuseum.org.cn/news'], + target: '/news', + }, + ], + + handler: async (ctx) => { + const typeParam = ctx.req.param('type') || 'all'; + + const typeMap: Record<string, string> = { + notice: '通知公告', + news: '新闻资讯', + }; + + const targetType = typeMap[typeParam]; + + const baseUrl = 'https://www.capitalmuseum.org.cn'; + const apiUrl = `${baseUrl}/news`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await got({ + method: 'get', + url: apiUrl, + }); + + const $ = load(response.data); + const nuxtDataStr = $('#__NUXT_DATA__').text() || '{}'; // use __NUXT_DATA__ to get the data structure of the page + const nuxtData = JSON.parse(nuxtDataStr); + // get all the links of the news items by section name + const getLinksBySection = (sectionName: string) => { + // get the span element that contains the section name + const $titleSpan = $(`span:contains("${sectionName}")`); + + // for example, div class="oz09n6" is the container of the news items, which is the next sibling of the span element + const $container = $titleSpan.parent().nextAll('div').first(); + + // get all the links of the news items in the container + return $container + .find('a[href^="/news/"]') + .toArray() + .map((el) => $(el).attr('href') as string); + }; + + let targetHrefs: string[] = []; + + if (targetType) { + targetHrefs = getLinksBySection(targetType); + } else if (typeParam === 'all') { + targetHrefs = Object.values(typeMap).flatMap((sectionName) => getLinksBySection(sectionName)); + } + + // match the newsId in the href with the newsId in the nuxtData to get the title and publishtime + const items = targetHrefs.map((href) => { + const newsId = href.split('/').pop(); + const link = `${baseUrl}${href}`; + + const newsObj = nuxtData.find((item: any) => nuxtData[item.newsid] === newsId); + + const pubTime = nuxtData[newsObj.publishtime]; + const pubDate = timezone(parseDate(pubTime, 'YYYY-MM-DD'), 8); + const title = nuxtData[newsObj.title]; + + return { + title, + link, + pubDate, + } as DataItem; + }); + + return { + title: `${museumName} - 首博快讯${targetType ? ` - ${targetType}` : ''}`, + link: apiUrl, + language: 'zh-CN', + item: items, + }; + }, +}; From 16cac03373f2a6c1b54615a2049729ec5dc37890 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Fri, 24 Jul 2026 00:55:49 +0800 Subject: [PATCH 0401/1190] feat(route): add Sichuan Museum Exhibition route (#22756) * feat(route): add Sichuan Museum Exhibition route * fix: fix the radar target * fix: update key with original format and remove string() for startDate/endDate Co-authored-by: Tony <TonyRL@users.noreply.github.com> -------- --- lib/routes/scmuseum/exhibition.tsx | 130 +++++++++++++++++++++++++++++ lib/routes/scmuseum/namespace.ts | 10 +++ 2 files changed, 140 insertions(+) create mode 100644 lib/routes/scmuseum/exhibition.tsx create mode 100644 lib/routes/scmuseum/namespace.ts diff --git a/lib/routes/scmuseum/exhibition.tsx b/lib/routes/scmuseum/exhibition.tsx new file mode 100644 index 000000000000..2185dbd2c200 --- /dev/null +++ b/lib/routes/scmuseum/exhibition.tsx @@ -0,0 +1,130 @@ +import crypto from 'node:crypto'; + +import { renderToString } from 'hono/jsx/dom/server'; + +import type { Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +// Generate a dynamic token using AES-256-ECB with a static key and timestamp +function getToken() { + const key = 'TYYY3OT3-TOCBW7OW-T33QMVJ3-2IUUVC22'.replaceAll('-', ''); // this key is used for AES-256-ECB encryption, and it is hardcoded in the original code in https://www.scmuseum.cn/js/index.2af39792.chunk.js + const t = (Math.random() + '').slice(-6) + Date.now(); + const cipher = crypto.createCipheriv('aes-256-ecb', Buffer.from(key, 'utf8'), null); + cipher.setAutoPadding(true); + let token = cipher.update(t, 'utf8', 'base64'); + token += cipher.final('base64'); + return token; +} + +export const route: Route = { + path: '/exhibition/:type?', + categories: ['travel'], + example: '/scmuseum/exhibition/temp', + parameters: { type: 'Exhibition type, supported values: base (常设展览) or temp (临时展览), default is all exhibitions.' }, + name: 'Exhibition', + maintainers: ['magazian'], + radar: [ + { + source: ['www.scmuseum.cn/Visit/Exhibition'], + target: '/exhibition', + }, + ], + handler: async (ctx) => { + const typeParam = ctx.req.param('type'); + + const apiConfig = { + base: { endpoint: 'queryExhibitionBaseList', name: '常设展览', isBase: true }, + temp: { endpoint: 'queryExhibitionTempList', name: '临时展览', isBase: false }, + }; + + const fetchTypes = typeParam ? [typeParam] : ['base', 'temp']; + const museumName = namespace.zh?.name || namespace.name; + const titleTag = fetchTypes.length === 2 ? '' : apiConfig[fetchTypes[0] as keyof typeof apiConfig].name; + + const responses = await Promise.all( + fetchTypes.map(async (t) => { + const config = apiConfig[t as keyof typeof apiConfig]; + const apiUrl = `https://www.scmuseum.cn/japi/sw-cms-cloud/api/${config.endpoint}`; + + const response = await got({ + method: 'post', + url: apiUrl, + headers: { + Appid: '75d98ea426f4412eab623c9076365802', // fixed appid + Token: getToken(), + }, + json: { + entity: { + languageType: 'CN', + }, + param: { + pageNum: 1, + pageSize: 6, + }, + }, + }); + + const list = response.data?.data?.records || []; + + return list.map((item: any) => { + const isBase = config.isBase; + const title = isBase ? item.baseName : item.tempName; + const linkId = isBase ? item.exhibitionBaseId : item.exhibitionTempId; + const itemLink = isBase ? `https://www.scmuseum.cn/Visit/Exhibition/BaseExhibition/${linkId}` : `https://www.scmuseum.cn/Visit/Exhibition/ExhibitiomReview/${linkId}`; + + const pubDate = item.startTime ? timezone(parseDate(item.startTime), 8) : undefined; + const startDate = item.startTime ? item.startTime.split(' ', 1)[0] : ''; + const endDate = item.endTime ? item.endTime.split(' ', 1)[0] : ''; + const location = isBase ? item.basePlace : item.tempPlace; + const imgUrl = `https://www.scmuseum.cn/file/${item.thumb}`; + + const description = renderToString( + <div> + <img src={imgUrl} /> + <br /> + <p> + <b>地点:</b> + {location || '参考详情'} + </p> + <p> + <b>开展:</b> + {startDate || '未定/常设'} + </p> + <p> + <b>闭展:</b> + {endDate || '未定/常设'} + </p> + </div> + ); + + return { + title, + link: itemLink, + pubDate, + description, + // for further .ics process + _extra: { + museumName, + location, + startDate, // format: YYYY-MM-DD or '未定/常设' + endDate, // format: YYYY-MM-DD or '未定/常设' + }, + }; + }); + }) + ); + + const items = responses.flat(); + + return { + title: `${museumName} - 展览${titleTag ? ` - ${titleTag}` : ''}`, + link: 'https://www.scmuseum.cn/Visit/Exhibition', + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/scmuseum/namespace.ts b/lib/routes/scmuseum/namespace.ts new file mode 100644 index 000000000000..e5a1b29da3f8 --- /dev/null +++ b/lib/routes/scmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Sichuan Museum', + url: 'www.scmuseum.cn', + + zh: { + name: '四川博物院', + }, +}; From 0fafcbba7a99c9b9b0461f8a5376812e96d46c86 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Fri, 24 Jul 2026 01:30:29 +0800 Subject: [PATCH 0402/1190] feat(route): add Suzhou Museum Special Exhibit route (#22770) * feat(route): add Suzhou Museum Special Exhibit route * fix: patch the malformed header --- lib/routes/szmuseum/namespace.ts | 10 +++ lib/routes/szmuseum/temporary.tsx | 143 ++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 lib/routes/szmuseum/namespace.ts create mode 100644 lib/routes/szmuseum/temporary.tsx diff --git a/lib/routes/szmuseum/namespace.ts b/lib/routes/szmuseum/namespace.ts new file mode 100644 index 000000000000..2a8765072a2b --- /dev/null +++ b/lib/routes/szmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Suzhou Museum', + url: 'www.szmuseum.com', + + zh: { + name: '苏州博物馆', + }, +}; diff --git a/lib/routes/szmuseum/temporary.tsx b/lib/routes/szmuseum/temporary.tsx new file mode 100644 index 000000000000..88ec25bccc92 --- /dev/null +++ b/lib/routes/szmuseum/temporary.tsx @@ -0,0 +1,143 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { renderToString } from 'hono/jsx/dom/server'; +import { Agent, buildConnector } from 'undici'; + +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +// szmuseum.com responds with a malformed `Cache Control: no-cache` header +// (space instead of hyphen), which makes Node.js's strict HTTP parser throw. +// Patch the bytes on the socket before the parser sees them. +const connect = buildConnector({}); +const dispatcher = new Agent({ + connect(opts, cb) { + connect(opts, (err, socket) => { + if (err || !socket) { + cb(err, null); + return; + } + const read = socket.read.bind(socket); + socket.read = (...args) => { + const chunk = read(...args); + if (Buffer.isBuffer(chunk)) { + const index = chunk.indexOf('\r\nCache Control:'); + if (index !== -1) { + chunk.write('-', index + 7); + } + } + return chunk; + }; + cb(null, socket); + }); + }, +}); + +export const route: Route = { + path: '/temporary', + categories: ['travel'], + example: '/szmuseum/temporary', + name: 'Special Exhibition', + maintainers: ['magazian'], + radar: [ + { + source: ['www.szmuseum.com/Exhibition/Temporary'], + target: '/temporary', + }, + ], + handler: async () => { + const baseUrl = 'https://www.szmuseum.com'; + const apiUrl = `${baseUrl}/Exhibition/Temporary`; + const museumName = namespace.zh?.name || namespace.name; + + const content = await ofetch<string>(apiUrl, { dispatcher, parseResponse: (txt) => txt }); + + const $ = load(content); + + const list = $('.extemwrap ul li.clearfix') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('h1 a'); + const title = a.text(); + const link = new URL(a.attr('href') || '', baseUrl).href; + const imgUrl = new URL($item.find('.aleftimg img').attr('src') || '', baseUrl).href; + + const fullDuration = $item.find('.activity_r_detail p:nth-child(1) span:nth-child(2)').text().trim(); + const location = $item.find('.activity_r_detail p:nth-child(2) span:nth-child(2)').text().trim(); + + let startDate; + let endDate; + + if (fullDuration) { + const times = fullDuration.split('-'); + const startStr = times[0] + .replaceAll(/(.*?)/g, '') + .trim() + .replaceAll(/[年月/.]/g, '-') + .replace('日', ''); + let endStr = times[1] + .replaceAll(/(.*?)/g, '') + .trim() + .replaceAll(/[年月/.]/g, '-') + .replace('日', ''); + + if (startStr && endStr.split('-').length === 2) { + endStr = `${startStr.split('-', 1)[0]}-${endStr}`; + } + + startDate = dayjs(startStr).format('YYYY-MM-DD'); + endDate = dayjs(endStr).format('YYYY-MM-DD'); + } + + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( + <div> + <img src={imgUrl} /> + <br /> + <p> + <b>地点:</b> + {location || '参考详情'} + </p> + <p> + <b>开展:</b> + {startDate || '未定/常设'} + </p> + <p> + <b>闭展:</b> + {endDate || '未定/常设'} + </p> + {fullDuration && ( + <p> + <small>原始展期:{fullDuration}</small> + </p> + )} + </div> + ); + + return { + title, + link, + description, + pubDate, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }; + }); + + return { + title: `${museumName} - 临时展览`, + link: apiUrl, + language: 'zh-CN', + item: list, + }; + }, +}; From 534967c4ea475960ac342afdc9272ddea86358ae Mon Sep 17 00:00:00 2001 From: sirius60111 <sirius60111@users.noreply.github.com> Date: Fri, 24 Jul 2026 05:22:35 +0800 Subject: [PATCH 0403/1190] fix(route/xueqiu): serialize show.json requests and skip caching transient failures (#22708) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --- lib/routes/xueqiu/user.ts | 70 ++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index 97b0bd24469f..e3056c1e27dd 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -1,4 +1,5 @@ import sanitizeHtml from 'sanitize-html'; +import pMap from 'p-map'; import { parseToken } from '@/routes/xueqiu/cookies'; import type { Route } from '@/types'; @@ -115,36 +116,45 @@ async function handler(ctx) { const data = response.statuses.filter((s) => s.mark !== 1); // 去除置顶动态 - const items = await Promise.all( - data.map((item) => - cache.tryGet(item.target, async () => { - // legal_user_visible 为 true 时列表已含完整内容,无需再请求详情 - if (item.legal_user_visible) { - return buildListItem(item); - } - - try { - const detail = await ofetch(`${apiUrl}/statuses/show.json`, { - query: { - id: item.id, - }, - headers: { - Cookie: cookie, - Referer: link, - }, - }); - - return { - title: buildTitle(item, detail), - description: buildDescription(detail), - pubDate: parseDate(item.created_at), - link: rootUrl + item.target, - }; - } catch { - return buildListItem(item); - } - }) - ) + // Use p-map to limit concurrency and avoid triggering Xueqiu show.json rate limiting. + const items = await pMap( + data, + async (item) => { + try { + return await cache.tryGet(item.target, async () => { + // legal_user_visible 为 true 时列表已含完整内容,无需再请求详情 + if (item.legal_user_visible) { + return buildListItem(item); + } + + try { + const detail = await ofetch(`${apiUrl}/statuses/show.json`, { + query: { id: item.id }, + headers: { Cookie: cookie, Referer: link }, + }); + + return { + title: buildTitle(item, detail), + description: buildDescription(detail), + pubDate: parseDate(item.created_at), + link: rootUrl + item.target, + }; + } catch (error: any) { + // Permanent failures (post deleted / not found): cache the fallback. + const data = error.response?._data || error.data; + if (data && typeof data === 'object' && data.error_code) { + return buildListItem(item); + } + // Transient failures (rate limit / WAF): throw to skip caching, retry next request. + throw error; + } + }); + } catch { + // Transient failure: provide a fallback item without caching, retry next request. + return buildListItem(item); + } + }, + { concurrency: 3 }, ); const user = data[0]?.user; From e6812648b3ee1fa2a94f6ac146bdbf454a8b69bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:24:02 +0000 Subject: [PATCH 0404/1190] style: auto format --- lib/routes/xueqiu/user.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index e3056c1e27dd..2b97265a5679 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -1,5 +1,5 @@ -import sanitizeHtml from 'sanitize-html'; import pMap from 'p-map'; +import sanitizeHtml from 'sanitize-html'; import { parseToken } from '@/routes/xueqiu/cookies'; import type { Route } from '@/types'; @@ -154,7 +154,7 @@ async function handler(ctx) { return buildListItem(item); } }, - { concurrency: 3 }, + { concurrency: 3 } ); const user = data[0]?.user; From 38a66faa2a14f38be79e5610f906182da0fc3629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:14:56 +0000 Subject: [PATCH 0405/1190] chore(deps-dev): bump tsdown from 0.22.13 to 0.22.14 (#22814) Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.13 to 0.22.14. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.13...v0.22.14) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.22.14 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 248 ++++++++++++++++++++++++------------------------- 2 files changed, 125 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 90e9baf09bd9..493f199ce558 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.13", + "tsdown": "0.22.14", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28c4742b1090..17bfc3345c02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -450,8 +450,8 @@ importers: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.22.13 - version: 0.22.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) + specifier: 0.22.14 + version: 0.22.14(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -3034,130 +3034,130 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@yuku-codegen/binding-darwin-arm64@0.7.3': - resolution: {integrity: sha512-hbssEr7iLNCWWdUbEt8cuOjUQ9loI+U/BVmdhNQ4CV1wAFGvWDO3niK9JD2WwTC9iib7E498qgZ3L/xJnoSb3A==} + '@yuku-codegen/binding-darwin-arm64@0.7.4': + resolution: {integrity: sha512-fjkATm+fg4r6Ss8o82u3j33PfIqhSs4A0WEEw9kOwhMx/ui/RQ0ZAsCtF6e7UG2CWGOGXAJspLlfk2tr3rjjKQ==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.7.3': - resolution: {integrity: sha512-WqwST3vVcNBTd1zaT1vP6pU8NkjJlLWN37Kqomc2c6TG9eky6sbAfHIhZEjSSyqFrMqsAt3mIu0jQZAODi401g==} + '@yuku-codegen/binding-darwin-x64@0.7.4': + resolution: {integrity: sha512-Hj0KvHpS1RJY/bgM3BADzmXXkKO3+bq3M8bMq4b0j0LsVi1SeWYpD/hJLJFwHeNMS+jO4vlZ343yjb4DEb9XXQ==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.7.3': - resolution: {integrity: sha512-mCci4UPkZXYflXuHnPGl5sDsotBPLaBryFylDwZcqposktmumoOBIKp2gpzEmxQL/XlGdVWksmpYw9IRY43FNQ==} + '@yuku-codegen/binding-freebsd-x64@0.7.4': + resolution: {integrity: sha512-GLKBZvqVFvC1bvNPoPZRj0UxUaAZZKCzb8IPNyvRhXesOk+Af9UbhbVPwx9Do4o2AVf6R5FPzyRWWIihQdCp+A==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.7.3': - resolution: {integrity: sha512-F1QIaRH5SeahrYjZVrVvIb5nHCZQMF7+YWAy6yTJh0Y2r2wLgqOQuhbOWcoKk+AyBiTOO2nAsFHNpVqRq2GjGw==} + '@yuku-codegen/binding-linux-arm-gnu@0.7.4': + resolution: {integrity: sha512-CAjbJexBJUqHgIPgOCJO7EYRnFluNLt5VD3jNS40wmsNZqa04HbewVVcgfmzfzuBhjX6ookntLDG3lWieyTAVw==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.7.3': - resolution: {integrity: sha512-JvkWNmN8MFbboX/5grRsAldGaB8ArRFgAaUYfQoohsqcuJZee8SLtc6itvwuntPbN9MGStn4GzdWk2lmSURqbA==} + '@yuku-codegen/binding-linux-arm-musl@0.7.4': + resolution: {integrity: sha512-HQMuKBltnKFUqhUh8wNiivd76coRwDngdCxbcAULlatAlc2OXo8L6jLTkVnNeUuOw9JYzSq9LY2/7zvrg63Ddg==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.7.3': - resolution: {integrity: sha512-DUnMTE/HCA7j1BAipWclGZCYfLZ/4SV5lldmV2kDmVMIywcw4PhtQ4Rfd/tO1K82JkjhQ+4a8XApcOIksOacnQ==} + '@yuku-codegen/binding-linux-arm64-gnu@0.7.4': + resolution: {integrity: sha512-cnU7ZVxK/Oq/TIS2iq56rouy1dqnLYgWR2puWcrxGCtmbdxiMISANYhI5t2tMLzTGZMyhawM2zYlyI+gnpBupQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.7.3': - resolution: {integrity: sha512-pNnXMnm9dlqd3V8C7nHaXgZnMLwP/CyM8B4mSAnkqVlhj2t3b8CMjesEV85SCbFlagzJk8FapfSghfxcXKuBow==} + '@yuku-codegen/binding-linux-arm64-musl@0.7.4': + resolution: {integrity: sha512-NyuabTumcxPtZv/Q+pMVvrcKxLn1SPcpdBGtekVJz7JwI36SuExTq4IG4EZsk7YDmFKP8wDEvmKYOpV/a8Lwhw==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.7.3': - resolution: {integrity: sha512-o5GRppYvYvPl4UJbgRSZpXXDh6rOWQzX0yLB+tDzv31T7mOuLitU6zgQZxzw5rwhPJDffCt/AVKMbtizEo+UtA==} + '@yuku-codegen/binding-linux-x64-gnu@0.7.4': + resolution: {integrity: sha512-w3EnCLPD2vpJw0F+0qVV/1KSOAw4SgzjbGUbbwXUh9w5Kxo1Hdc3mN+/Nvk50oA6cCbSOCEaILnPHXPCauArvg==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.7.3': - resolution: {integrity: sha512-GdiyLD1bM3lhTUeGkA9ZZLjqU+xt5uVihgPm0e6TdBjCi6DMZORWe02oDlRbn9OZaV7AZosngXAq2jLk3TrBEw==} + '@yuku-codegen/binding-linux-x64-musl@0.7.4': + resolution: {integrity: sha512-FXKQlFjDM8FxqJ/TncAni7e7JyaXIB3Hv6boApxSs5ZUlvqP4TbrjYVk3mYDQt6xQAE/kEHplxM6m5SKx5sfRg==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.7.3': - resolution: {integrity: sha512-uQrxQDBQFIAUgHJIxcB/FAAUk0Wkyj3xGpHrp/C258nXnCTAH0KDaqyK5RN/TCkVYrpH+uCl+rGnwDLSGnPBkQ==} + '@yuku-codegen/binding-win32-arm64@0.7.4': + resolution: {integrity: sha512-rWr899KEvZIWMx9yUXQl4i90OGIs4gaw4X1UsS2rxsI3qnp8acLIVKI3N5WDqaertkW10crDRZvIxxyw7kTMTQ==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.7.3': - resolution: {integrity: sha512-UoF7tqMnVMUmIXPgDjFLhRezea6HaypwLDWNenCFxfJCzQNEa41lcHpxq1AN2Xl6GLzIDfAb+lQDi/O+zPCfyg==} + '@yuku-codegen/binding-win32-x64@0.7.4': + resolution: {integrity: sha512-3Olgmkd5rDrIN9g7wJFMRRC9jub5zAwiQOtwOVhvNe/nZE/rSufFRa7vrFupqSCQml+Zxbcy9npzo3Qne3rA2A==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.7.3': - resolution: {integrity: sha512-JcFQSNEnjtyHQRjXO0G5rQt5xHq5MR+9nHqh+wt5MP30XIiccUYlcMIa3s7CBmj8E8WPGZC08k7LNGrA1OISTA==} + '@yuku-parser/binding-darwin-arm64@0.7.4': + resolution: {integrity: sha512-rUetRGukIlPOkDCy+Fo0YTee66n9A04XRQMONptbemWSU27CCV6RDj56+4ne4eSE4gJ0139RWUfbqMtt744pWQ==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.7.3': - resolution: {integrity: sha512-U0kNeI3VygP/+8sTOLTeLTjUyTYZfl9Wuq6xVKWPXHb2K7oI0sm8JXJ5xlhAbkUDQRi0N6yK5TKxS0sQT5M6UQ==} + '@yuku-parser/binding-darwin-x64@0.7.4': + resolution: {integrity: sha512-9bHrGQUot2vWTg0YTdyIBHGd38fy2BSQH1WaqUDVuNqSn7HffrTUzWOgbaWRNd5GTOvDdrt9SDZIG7nfqzeBtg==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.7.3': - resolution: {integrity: sha512-Xc08FQTvxovy6pX+Co9fgv3N1H0OpLpph/ClbwnJkcdphY3kzN2GgIfir35kpPp8mOzlQgtAVQdeDyAYyjwx4w==} + '@yuku-parser/binding-freebsd-x64@0.7.4': + resolution: {integrity: sha512-159565OJ/LR6cCP781nH8DxB5GqlqqWk3uyOLZIznQhsj9zeht4X7+jz9IQH1l/TUio+ojEXf8yt2+DJrN+QVw==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.7.3': - resolution: {integrity: sha512-6bHeDiUd+0bmoJJ5wZVG+PcJkXQUdjy/AzHsOVcOSUtHtRTX2H3Z5RIHyPAh5OveLtT4RHyqaO3fEQGnHGX5/Q==} + '@yuku-parser/binding-linux-arm-gnu@0.7.4': + resolution: {integrity: sha512-j3s3LJxEbOyP58hmzuXpJlKzgxaswuOTu8nAbDpE119b5W3gP1SW+HndLscGUOACpyMdH5WJ6gBHRxq0HCRVBw==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.7.3': - resolution: {integrity: sha512-trGERNLJGvXkkyvdWBKspTOBATd4lcmpPFPcy5HI5kC3rC3ZMHQDJ356OMSVUsR2NQnl++F0ZTZQucao9hrbeQ==} + '@yuku-parser/binding-linux-arm-musl@0.7.4': + resolution: {integrity: sha512-PUpHPmvWIDxhYTS5oah08RQ28t6dlFBxRPJcftS6HgquiPsm/e0gL5vKwPJpyjaPBHzRH61IAUDDfrRe8iFs8g==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.7.3': - resolution: {integrity: sha512-p1ELmzhqAX7SFUvH/tR5zSb8NsWZQ8ulw7PqatfXNmJMy34bkivtL0z2jPpVoMJXf5o2+TwBN29Z31CLL0wvkA==} + '@yuku-parser/binding-linux-arm64-gnu@0.7.4': + resolution: {integrity: sha512-Hi3w0et5mu3i7S+qE0UgOev4RqJ/U9DW8xn79t4nttiICYCx3NveC0Goo78uqzxttsDI3hfRY43lrrF26svqcw==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.7.3': - resolution: {integrity: sha512-dZ79SrJ002ZXfBDmpQ47SF+06Q5bGOjFeoSK8xO97ter/bjLBgxSO4d7i9hhf+uj4xkrTPc/OFNVaeBAF3L4dA==} + '@yuku-parser/binding-linux-arm64-musl@0.7.4': + resolution: {integrity: sha512-i073u4ENL9DJeWBRKioRCz8i5hg6EmUcH89eH1lts9f9AFPA1GphODwK6OXOXUbpi2AwZ1deFIUWgLGP2mdmmA==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.7.3': - resolution: {integrity: sha512-LjT64hVGWWbOmOYS+Fd8qScgyRxgUbudhFJbw8aeUVnhiNR1np36KnE0VFWrJeu7rTdNxuzVyV5o9JLqhBDTVQ==} + '@yuku-parser/binding-linux-x64-gnu@0.7.4': + resolution: {integrity: sha512-j5pt0LyDGxCzfoqRTR3cmMG21+bgSxIufAzJXFOWXkbg/22Ih51eGEsbInnSD5Q9FvJ3ooIn/jfok0dBdhudnA==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.7.3': - resolution: {integrity: sha512-5Nw3v5BqYbOi0ZdV+SImgVYa0KDBVAY/ZNPilTsJJU4phqa2cqEW4+aMgyieE/4bqpojt/B931kM+7pipazP9g==} + '@yuku-parser/binding-linux-x64-musl@0.7.4': + resolution: {integrity: sha512-RR1hNhrSpv3FanLM6u5uD+9CYA9IM8U3uGscgvKKV77gtj7ttO4UubpwN7vcx1KknoEIQHKJFPVKoeVy+avf6w==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.7.3': - resolution: {integrity: sha512-lFYXgHiq8tJKa6/e1/q8Su+IJVV4/CjoXbIJ7/GdPXPp9Hx0gh/8HqmGSsrwlY2w8/ohBcar9wxjm1rQ8D16bg==} + '@yuku-parser/binding-win32-arm64@0.7.4': + resolution: {integrity: sha512-1xhYwOLo9TjppHHwNYi3X/dGgOvnj9xh62jpgP4U8nEtTGA70NtJDCkN45pRQdU3B6/U7oQj1T4esP3aFJg6BA==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.7.3': - resolution: {integrity: sha512-wy/fs4OrHBhKW8LDyZJRMyMQ3QOfbSQDp0WtMbtzWCVui/vTQEUJMGnwvRCppjchS/qIQfbWzIw/HSjogJqZFA==} + '@yuku-parser/binding-win32-x64@0.7.4': + resolution: {integrity: sha512-HonZAapmSKusxLZPnU9WrMzAUdPSBJliGw3CSkA9Er/aq15STEOEy9SOsVGvj4maBv95EmWxt2LThHXnFnGfNg==} cpu: [x64] os: [win32] - '@yuku-toolchain/types@0.7.3': - resolution: {integrity: sha512-ezarjq3dcl8Nx3iJ9VeAc7vhzvHyRoSvr7bLX76T+ljRq73IbZ11X2RFCblfK6YxW2DsjkzU6MPnr3JWDYqYYQ==} + '@yuku-toolchain/types@0.7.4': + resolution: {integrity: sha512-iUFXr+UnUJjzVLNI6GIv07poi9NwcG5hTBJSheJh3SdpkYpIjCl9kAGe7dbJMHn5sXeceCL4H12pKa0b6pkouQ==} '@zone-eu/mailsplit@5.4.14': resolution: {integrity: sha512-rz0FQOhN3Vq1XrSeSSa9+dPcaFbBxmQPjiZm6zS9oxdVHV7rOWIAYX3yP2YAUf0qBncY8CI+NogzPCmMVrMXcw==} @@ -5548,8 +5548,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.12: - resolution: {integrity: sha512-DJg5ELVEdLAVhirvtak7GS4nbNvBzLNAtYL1M8hLghDURBFKU2MwEH6ncHibYs6khq1NaRQ97iFMiHvzw+WoSw==} + rolldown-plugin-dts@0.27.13: + resolution: {integrity: sha512-DeVZJbbB0ajp5q6vABqC8ZCJzxftlxbiV60Bk96GFdQaysGVpgTTVjQu0lUt4Lb+aRCtejfOixtQKDRol7IuVQ==} engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: '@typescript/native-preview': '*' @@ -5928,14 +5928,14 @@ packages: typescript: optional: true - tsdown@0.22.13: - resolution: {integrity: sha512-XaYFhtiKRUvTpXv/YAehsHdbEb3LN/iMlzjSINbjlaATtXN2zVPKox2STKhcyFPlh++8Zg7suNN27E679IfAUA==} + tsdown@0.22.14: + resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.13 - '@tsdown/exe': 0.22.13 + '@tsdown/css': 0.22.14 + '@tsdown/exe': 0.22.14 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' @@ -6163,8 +6163,8 @@ packages: resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} engines: {node: '>=0.10.0'} - verkit@0.1.2: - resolution: {integrity: sha512-WqkT8n3hqizuCu71W3bUzf5fjBmkbXcudsehe/NbxA8PgqoKnSOY5K0Ba2ckg1qaRaSpSz7as/n9K1R9JXjQKg==} + verkit@0.3.0: + resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==} engines: {node: '>=18.12.0'} verror@1.10.0: @@ -6453,14 +6453,14 @@ packages: youtubei.js@17.2.0: resolution: {integrity: sha512-XLNsgRKO1h7t4i9tIMWSQSeWdD7Ujkk5v1m5YCaumaHMhu/xuLqtO3M0Hq7CXNup9HlJ1NGrT1Y+HLIHnL6Ujg==} - yuku-ast@0.7.3: - resolution: {integrity: sha512-occyAXtcU4hcl5UPEclWwLqz4J1ZAV8Us4LllmTqAe1YjL5aMbh3GCpzHt6O0sOF+dwBI3BZAcFQqbyX1RiaWg==} + yuku-ast@0.7.4: + resolution: {integrity: sha512-Pn6e7uZOBczeJ+JIiPGtD4aw6eRflzKrZJmAdeg092RxM9tQtvAkZAbEoknNgYmsB6dGYESvXPQcUE7Nu8ai7Q==} - yuku-codegen@0.7.3: - resolution: {integrity: sha512-hhyJW0TIEwm4kex8XVCS4CZIXHS/3TWWNUum+95Ji5/4W+iaLfUnvwSxJwyNfTzS8cxmd1np+EZ4b8ObLguKEA==} + yuku-codegen@0.7.4: + resolution: {integrity: sha512-bLdC5yzvn507PtU7+kB4CMBLfnvKW1N2m26Vpl1q4Os+FLROnkKTThM7g+clVb+9tHaOlcc6gqQ+rNBY8L/Dxw==} - yuku-parser@0.7.3: - resolution: {integrity: sha512-5kZ7HR+W+OsNpVtZ9LHruQsgmcoJl4TETrffPq2GbliThsFiy+zHzbMLmSxQJrokzyJqfJ/TPfFkdUDOodJCgA==} + yuku-parser@0.7.4: + resolution: {integrity: sha512-HveMyhZPQQfR4z3xskXv5hHJW9g0KIESZW6JvUZv7Jn52FfMFUhCYL77KHBtnWGFti0KrKeTHMZVTY5AUVgFAA==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -8484,73 +8484,73 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@yuku-codegen/binding-darwin-arm64@0.7.3': + '@yuku-codegen/binding-darwin-arm64@0.7.4': optional: true - '@yuku-codegen/binding-darwin-x64@0.7.3': + '@yuku-codegen/binding-darwin-x64@0.7.4': optional: true - '@yuku-codegen/binding-freebsd-x64@0.7.3': + '@yuku-codegen/binding-freebsd-x64@0.7.4': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.7.3': + '@yuku-codegen/binding-linux-arm-gnu@0.7.4': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.7.3': + '@yuku-codegen/binding-linux-arm-musl@0.7.4': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.7.3': + '@yuku-codegen/binding-linux-arm64-gnu@0.7.4': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.7.3': + '@yuku-codegen/binding-linux-arm64-musl@0.7.4': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.7.3': + '@yuku-codegen/binding-linux-x64-gnu@0.7.4': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.7.3': + '@yuku-codegen/binding-linux-x64-musl@0.7.4': optional: true - '@yuku-codegen/binding-win32-arm64@0.7.3': + '@yuku-codegen/binding-win32-arm64@0.7.4': optional: true - '@yuku-codegen/binding-win32-x64@0.7.3': + '@yuku-codegen/binding-win32-x64@0.7.4': optional: true - '@yuku-parser/binding-darwin-arm64@0.7.3': + '@yuku-parser/binding-darwin-arm64@0.7.4': optional: true - '@yuku-parser/binding-darwin-x64@0.7.3': + '@yuku-parser/binding-darwin-x64@0.7.4': optional: true - '@yuku-parser/binding-freebsd-x64@0.7.3': + '@yuku-parser/binding-freebsd-x64@0.7.4': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.7.3': + '@yuku-parser/binding-linux-arm-gnu@0.7.4': optional: true - '@yuku-parser/binding-linux-arm-musl@0.7.3': + '@yuku-parser/binding-linux-arm-musl@0.7.4': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.7.3': + '@yuku-parser/binding-linux-arm64-gnu@0.7.4': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.7.3': + '@yuku-parser/binding-linux-arm64-musl@0.7.4': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.7.3': + '@yuku-parser/binding-linux-x64-gnu@0.7.4': optional: true - '@yuku-parser/binding-linux-x64-musl@0.7.3': + '@yuku-parser/binding-linux-x64-musl@0.7.4': optional: true - '@yuku-parser/binding-win32-arm64@0.7.3': + '@yuku-parser/binding-win32-arm64@0.7.4': optional: true - '@yuku-parser/binding-win32-x64@0.7.3': + '@yuku-parser/binding-win32-x64@0.7.4': optional: true - '@yuku-toolchain/types@0.7.3': {} + '@yuku-toolchain/types@0.7.4': {} '@zone-eu/mailsplit@5.4.14': dependencies: @@ -11301,15 +11301,15 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): + rolldown-plugin-dts@0.27.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.5 obug: 2.1.4 rolldown: 1.2.0 - yuku-ast: 0.7.3 - yuku-codegen: 0.7.3 - yuku-parser: 0.7.3 + yuku-ast: 0.7.4 + yuku-codegen: 0.7.4 + yuku-parser: 0.7.4 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -11733,7 +11733,7 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - tsdown@0.22.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): + tsdown@0.22.14(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.1)(unrun@0.3.1): dependencies: ansis: 4.3.1 cac: 7.0.0 @@ -11744,12 +11744,12 @@ snapshots: obug: 2.1.4 picomatch: 4.0.5 rolldown: 1.2.0 - rolldown-plugin-dts: 0.27.12(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) + rolldown-plugin-dts: 0.27.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) tinyexec: 1.2.4 tinyglobby: 0.2.17 tree-kill: 1.2.2 unconfig-core: 7.5.0 - verkit: 0.1.2 + verkit: 0.3.0 optionalDependencies: tsx: 4.23.1 typescript: '@typescript/typescript6@6.0.2' @@ -11952,7 +11952,7 @@ snapshots: vali-date@1.0.0: {} - verkit@0.1.2: {} + verkit@0.3.0: {} verror@1.10.0: dependencies: @@ -12236,42 +12236,42 @@ snapshots: fflate: 0.8.3 meriyah: 6.1.4 - yuku-ast@0.7.3: + yuku-ast@0.7.4: dependencies: - '@yuku-toolchain/types': 0.7.3 + '@yuku-toolchain/types': 0.7.4 - yuku-codegen@0.7.3: + yuku-codegen@0.7.4: dependencies: - '@yuku-toolchain/types': 0.7.3 + '@yuku-toolchain/types': 0.7.4 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.7.3 - '@yuku-codegen/binding-darwin-x64': 0.7.3 - '@yuku-codegen/binding-freebsd-x64': 0.7.3 - '@yuku-codegen/binding-linux-arm-gnu': 0.7.3 - '@yuku-codegen/binding-linux-arm-musl': 0.7.3 - '@yuku-codegen/binding-linux-arm64-gnu': 0.7.3 - '@yuku-codegen/binding-linux-arm64-musl': 0.7.3 - '@yuku-codegen/binding-linux-x64-gnu': 0.7.3 - '@yuku-codegen/binding-linux-x64-musl': 0.7.3 - '@yuku-codegen/binding-win32-arm64': 0.7.3 - '@yuku-codegen/binding-win32-x64': 0.7.3 - - yuku-parser@0.7.3: - dependencies: - '@yuku-toolchain/types': 0.7.3 - yuku-ast: 0.7.3 + '@yuku-codegen/binding-darwin-arm64': 0.7.4 + '@yuku-codegen/binding-darwin-x64': 0.7.4 + '@yuku-codegen/binding-freebsd-x64': 0.7.4 + '@yuku-codegen/binding-linux-arm-gnu': 0.7.4 + '@yuku-codegen/binding-linux-arm-musl': 0.7.4 + '@yuku-codegen/binding-linux-arm64-gnu': 0.7.4 + '@yuku-codegen/binding-linux-arm64-musl': 0.7.4 + '@yuku-codegen/binding-linux-x64-gnu': 0.7.4 + '@yuku-codegen/binding-linux-x64-musl': 0.7.4 + '@yuku-codegen/binding-win32-arm64': 0.7.4 + '@yuku-codegen/binding-win32-x64': 0.7.4 + + yuku-parser@0.7.4: + dependencies: + '@yuku-toolchain/types': 0.7.4 + yuku-ast: 0.7.4 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.7.3 - '@yuku-parser/binding-darwin-x64': 0.7.3 - '@yuku-parser/binding-freebsd-x64': 0.7.3 - '@yuku-parser/binding-linux-arm-gnu': 0.7.3 - '@yuku-parser/binding-linux-arm-musl': 0.7.3 - '@yuku-parser/binding-linux-arm64-gnu': 0.7.3 - '@yuku-parser/binding-linux-arm64-musl': 0.7.3 - '@yuku-parser/binding-linux-x64-gnu': 0.7.3 - '@yuku-parser/binding-linux-x64-musl': 0.7.3 - '@yuku-parser/binding-win32-arm64': 0.7.3 - '@yuku-parser/binding-win32-x64': 0.7.3 + '@yuku-parser/binding-darwin-arm64': 0.7.4 + '@yuku-parser/binding-darwin-x64': 0.7.4 + '@yuku-parser/binding-freebsd-x64': 0.7.4 + '@yuku-parser/binding-linux-arm-gnu': 0.7.4 + '@yuku-parser/binding-linux-arm-musl': 0.7.4 + '@yuku-parser/binding-linux-arm64-gnu': 0.7.4 + '@yuku-parser/binding-linux-arm64-musl': 0.7.4 + '@yuku-parser/binding-linux-x64-gnu': 0.7.4 + '@yuku-parser/binding-linux-x64-musl': 0.7.4 + '@yuku-parser/binding-win32-arm64': 0.7.4 + '@yuku-parser/binding-win32-x64': 0.7.4 zod@3.25.76: {} From 767968d1b5329d4d090c0b01576aac294eb02843 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:16:01 +0800 Subject: [PATCH 0406/1190] chore(deps): bump docker/login-action from 4.4.0 to 4.5.0 (#22810) Bumps [docker/login-action](https://github.com/docker/login-action) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/af1e73f918a031802d376d3c8bbc3fe56130a9b0...06fb636fac595d6fb4b28a5dfcb21a6f5091859c) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index f6c774e17585..aaf87ab448d5 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -74,13 +74,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -207,13 +207,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: registry: ghcr.io username: ${{ github.actor }} From 5f9a9ecb1945ae0c40a3e7477fe7e9d7a04681ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:21:04 +0800 Subject: [PATCH 0407/1190] chore(deps-dev): bump lint-staged from 17.1.1 to 17.2.0 (#22813) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 17.1.1 to 17.2.0. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.1.1...v17.2.0) --- updated-dependencies: - dependency-name: lint-staged dependency-version: 17.2.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 493f199ce558..e3ee8474f126 100644 --- a/package.json +++ b/package.json @@ -187,7 +187,7 @@ "globals": "17.7.0", "husky": "9.1.7", "js-beautify": "2.0.3", - "lint-staged": "17.1.1", + "lint-staged": "17.2.0", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17bfc3345c02..d888c5ea6d34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -411,8 +411,8 @@ importers: specifier: 2.0.3 version: 2.0.3 lint-staged: - specifier: 17.1.1 - version: 17.1.1 + specifier: 17.2.0 + version: 17.2.0 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -4707,8 +4707,8 @@ packages: linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} - lint-staged@17.1.1: - resolution: {integrity: sha512-FnHWpSe5cPRtrDG+soOuNdBxb4XQb2gN5EqpEWKdweyqyOfpl4QSjbrz3ilcIf0WXmkiNQGZZRQ23R5YtB3TEw==} + lint-staged@17.2.0: + resolution: {integrity: sha512-FchGnFe4i4B1C/a35SPU9bNGPEHSC1+1iV0plLjzBmKVe9klZrlRfSgK6Cw4VeHyqOXbJUXP0vON61uRftNQ0A==} engines: {node: '>=22.22.1'} hasBin: true @@ -10176,7 +10176,7 @@ snapshots: dependencies: uc.micro: 2.1.0 - lint-staged@17.1.1: + lint-staged@17.2.0: dependencies: picomatch: 4.0.5 string-argv: 0.3.2 From 9e6e574537d097ec6a444247f9a17119dd44ca57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:27:05 +0800 Subject: [PATCH 0408/1190] chore(deps-dev): bump fs-extra from 11.3.6 to 11.4.0 (#22812) Bumps [fs-extra](https://github.com/jprichardson/node-fs-extra) from 11.3.6 to 11.4.0. - [Changelog](https://github.com/jprichardson/node-fs-extra/blob/master/CHANGELOG.md) - [Commits](https://github.com/jprichardson/node-fs-extra/compare/11.3.6...11.4.0) --- updated-dependencies: - dependency-name: fs-extra dependency-version: 11.4.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e3ee8474f126..393a675fdc04 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "eslint-plugin-unicorn": "72.0.0", "eslint-plugin-yml": "3.6.0", "fast-string-width": "3.0.2", - "fs-extra": "11.3.6", + "fs-extra": "11.4.0", "globals": "17.7.0", "husky": "9.1.7", "js-beautify": "2.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d888c5ea6d34..f9e86c6b3e13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,8 +399,8 @@ importers: specifier: 3.0.2 version: 3.0.2 fs-extra: - specifier: 11.3.6 - version: 11.3.6 + specifier: 11.4.0 + version: 11.4.0 globals: specifier: 17.7.0 version: 17.7.0 @@ -4096,8 +4096,8 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} - fs-extra@11.3.6: - resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} + fs-extra@11.4.0: + resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} engines: {node: '>=14.14'} fsevents@2.3.2: @@ -9516,7 +9516,7 @@ snapshots: dependencies: fetch-blob: 3.2.0 - fs-extra@11.3.6: + fs-extra@11.4.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.1 From edb2987d51157758829f72d2e2b57874bd9af8bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:39:08 +0800 Subject: [PATCH 0409/1190] chore(deps): bump nixpkgs from `241313f` to `e2587ca` (#22816) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `241313f` to `e2587ca`. - [Commits](https://github.com/NixOS/nixpkgs/compare/241313f4e8e508cb9b13278c2b0fa25b9ca27163...e2587caef70cea85dd97d7daab492899902dbf5d) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: e2587caef70cea85dd97d7daab492899902dbf5d dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index c71b955dca92..9458658bdbb1 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784497964, - "narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", + "lastModified": 1784796856, + "narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", + "rev": "e2587caef70cea85dd97d7daab492899902dbf5d", "type": "github" }, "original": { From 71f87aca39432981ed431746d9a56f74dde72bb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:59:29 +0800 Subject: [PATCH 0410/1190] chore(deps-dev): bump the cloudflare group with 3 updates (#22811) Bumps the cloudflare group with 3 updates: [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-pool-workers` from 0.18.7 to 0.18.8 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.18.8/packages/vitest-pool-workers) Updates `@cloudflare/workers-types` from 5.20260723.1 to 5.20260724.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.113.0 to 4.114.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.114.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-pool-workers" dependency-version: 0.18.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260724.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.114.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +- pnpm-lock.yaml | 374 ++++++++++++++++++++++++++----------------------- 2 files changed, 200 insertions(+), 180 deletions(-) diff --git a/package.json b/package.json index 393a675fdc04..ae3e86db42da 100644 --- a/package.json +++ b/package.json @@ -148,8 +148,8 @@ "@bbob/types": "4.3.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", - "@cloudflare/vitest-pool-workers": "0.18.7", - "@cloudflare/workers-types": "5.20260723.1", + "@cloudflare/vitest-pool-workers": "0.18.8", + "@cloudflare/workers-types": "5.20260724.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.75.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", - "wrangler": "4.113.0", + "wrangler": "4.114.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9e86c6b3e13..70a52543ddff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -294,11 +294,11 @@ importers: specifier: 1.3.0 version: 1.3.0 '@cloudflare/vitest-pool-workers': - specifier: 0.18.7 - version: 0.18.7(@cloudflare/workers-types@5.20260723.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + specifier: 0.18.8 + version: 0.18.8(@cloudflare/workers-types@5.20260724.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260723.1 - version: 5.20260723.1 + specifier: 5.20260724.1 + version: 5.20260724.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -471,8 +471,8 @@ importers: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: - specifier: 4.113.0 - version: 4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.114.0 + version: 4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -604,45 +604,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.18.7': - resolution: {integrity: sha512-PGYToiFoGpuRV2Uh33S4fI7JcmSkDxk6LQeneYtgfsITEkVi5iGxc3Vms6yW9Jr6uSzVK4Be9XZfdyZDr5GWYw==} + '@cloudflare/vitest-pool-workers@0.18.8': + resolution: {integrity: sha512-O1kOMZqapidlezNFiBZ7Lbd+8mMEpkGmWwPj+nPLvOngxSL11lmWq7xl7vxyjDxbeD/7l22KqgvRGM7XFaYd9w==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260721.1': - resolution: {integrity: sha512-VivNMhiEdZIB4JBWxf1RMJGROErv53qmQ+dvhjA1evrCouvqRYW718VqDideU3PSV7Ythl5Df48NqZYWoaEHpQ==} + '@cloudflare/workerd-darwin-64@1.20260722.1': + resolution: {integrity: sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260721.1': - resolution: {integrity: sha512-k7oye1ZiuwnnBBA2eTMduconr/ud5ZxFtRNTsYwMdmJeeeislw2+M72otrHxxvybCP7JWPPlJ38uhfajpcyhOA==} + '@cloudflare/workerd-darwin-arm64@1.20260722.1': + resolution: {integrity: sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260721.1': - resolution: {integrity: sha512-hon0lW4ZQ4boAVgaw+0ZFTNS8v5MWPWvK0HZnt4tDpKYnDUviLZawtUW3KqvFmCQTipVHl1S34j3J8Eqb93hGQ==} + '@cloudflare/workerd-linux-64@1.20260722.1': + resolution: {integrity: sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260721.1': - resolution: {integrity: sha512-nAl+HRQqpX5b7xVwWcvLPZmCk8NQ2yjI0yvJTWcHiRswbMEg1ZZckVmjJUAn0PHzZARbCSyIV7v3UjM+SPRmIQ==} + '@cloudflare/workerd-linux-arm64@1.20260722.1': + resolution: {integrity: sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260721.1': - resolution: {integrity: sha512-9paFG5cMTKz/CRixnEEnZbe5uvFPBFSDthxJHANfCWhUtBj49GSL1FPIokIg+Q+H8DGJEExU0lL92LtxD0lTxQ==} + '@cloudflare/workerd-windows-64@1.20260722.1': + resolution: {integrity: sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260723.1': - resolution: {integrity: sha512-+P5tDo+ZjW5nmWq/zagLVIyV8U/mSk7n8n0mLUHjn3pOdY7kdEDmURUxvhwwYqLJ/VPnyMJPAYtZjHbqAnIpDg==} + '@cloudflare/workers-types@5.20260724.1': + resolution: {integrity: sha512-gl0brZ60JhkZU3INgr1jsxaFEiWf3AB9RsCjLTMwT5RKspWRUpsFd0wxwbys7gb8Ywkfq9tORhw0y9G+7bDUYw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -1136,152 +1136,161 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -3676,7 +3685,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.50: @@ -4947,8 +4956,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260721.0: - resolution: {integrity: sha512-fBLaCxZ2i/nPH8iyLzvza0C8/sSF4sjD1ma1Skf+pkZVK0TlaW5ujHJlUHwcwR66v2JZt+Q28d4DCX/oaLG0cA==} + miniflare@4.20260722.0: + resolution: {integrity: sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw==} engines: {node: '>=22.0.0'} hasBin: true @@ -5623,9 +5632,9 @@ packages: set-cookie-parser@3.1.2: resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + engines: {node: '>=20.9.0'} shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -6328,17 +6337,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260721.1: - resolution: {integrity: sha512-b/DWhpV0jTudzQpLhDovcOgBz233386q+3Hbari7CLCNT9UXxjQziSTZ9yCoKdT2K3TSx5jrwlOisq8hlLWXYg==} + workerd@1.20260722.1: + resolution: {integrity: sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ==} engines: {node: '>=16'} hasBin: true - wrangler@4.113.0: - resolution: {integrity: sha512-ROGzSloJv0y21It6Oc9LaruNcu1tdiQ/XzL3Jc3YkFjzXEMXzTqVhA8vQaGMTdZHTjFP0PVcwAHNgaw3gXu4wA==} + wrangler@4.114.0: + resolution: {integrity: sha512-M65P25t5UHA1TIJfgZXDcj+YzVobgKdRguM2QPz0xnxLFuOcuE3ErgllDht0iaho7MS4o0g/Bb4YK2+GT+bibg==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260721.1 + '@cloudflare/workers-types': ^5.20260722.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6624,43 +6633,43 @@ snapshots: '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260721.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260721.1 + workerd: 1.20260722.1 - '@cloudflare/vitest-pool-workers@0.18.7(@cloudflare/workers-types@5.20260723.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260724.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260721.1': + '@cloudflare/workerd-darwin-64@1.20260722.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260721.1': + '@cloudflare/workerd-darwin-arm64@1.20260722.1': optional: true - '@cloudflare/workerd-linux-64@1.20260721.1': + '@cloudflare/workerd-linux-64@1.20260722.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260721.1': + '@cloudflare/workerd-linux-arm64@1.20260722.1': optional: true - '@cloudflare/workerd-windows-64@1.20260721.1': + '@cloudflare/workerd-windows-64@1.20260722.1': optional: true - '@cloudflare/workers-types@5.20260723.1': {} + '@cloudflare/workers-types@5.20260724.1': {} '@colors/colors@1.6.0': {} @@ -6994,98 +7003,108 @@ snapshots: '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.34.5': + '@img/sharp-darwin-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.3.1 optional: true - '@img/sharp-darwin-x64@0.34.5': + '@img/sharp-darwin-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.3.1 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + '@img/sharp-freebsd-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + '@img/sharp-libvips-darwin-arm64@1.3.1': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + '@img/sharp-libvips-darwin-x64@1.3.1': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@img/sharp-libvips-linux-arm64@1.3.1': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + '@img/sharp-libvips-linux-arm@1.3.1': optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + '@img/sharp-libvips-linux-ppc64@1.3.1': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + '@img/sharp-libvips-linux-riscv64@1.3.1': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + '@img/sharp-libvips-linux-s390x@1.3.1': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + '@img/sharp-libvips-linux-x64@1.3.1': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': optional: true - '@img/sharp-linux-arm64@0.34.5': + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + optional: true + + '@img/sharp-linux-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.3.1 optional: true - '@img/sharp-linux-arm@0.34.5': + '@img/sharp-linux-arm@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.3.1 optional: true - '@img/sharp-linux-ppc64@0.34.5': + '@img/sharp-linux-ppc64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.3.1 optional: true - '@img/sharp-linux-riscv64@0.34.5': + '@img/sharp-linux-riscv64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.3.1 optional: true - '@img/sharp-linux-s390x@0.34.5': + '@img/sharp-linux-s390x@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.3.1 optional: true - '@img/sharp-linux-x64@0.34.5': + '@img/sharp-linux-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.3.1 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + '@img/sharp-linuxmusl-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + '@img/sharp-linuxmusl-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 optional: true - '@img/sharp-wasm32@0.34.5': + '@img/sharp-wasm32@0.35.2': dependencies: '@emnapi/runtime': 1.11.2 optional: true - '@img/sharp-win32-arm64@0.34.5': + '@img/sharp-webcontainers-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-win32-arm64@0.35.2': optional: true - '@img/sharp-win32-ia32@0.34.5': + '@img/sharp-win32-ia32@0.35.2': optional: true - '@img/sharp-win32-x64@0.34.5': + '@img/sharp-win32-x64@0.35.2': optional: true '@inquirer/ansi@1.0.2': {} @@ -10597,12 +10616,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.34.5 + sharp: 0.35.2 undici: 7.28.0 - workerd: 1.20260721.1 + workerd: 1.20260722.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -11431,36 +11450,37 @@ snapshots: set-cookie-parser@3.1.2: {} - sharp@0.34.5: + sharp@0.35.2: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 shebang-command@2.0.0: dependencies: @@ -12100,26 +12120,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260721.1: + workerd@1.20260722.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260721.1 - '@cloudflare/workerd-darwin-arm64': 1.20260721.1 - '@cloudflare/workerd-linux-64': 1.20260721.1 - '@cloudflare/workerd-linux-arm64': 1.20260721.1 - '@cloudflare/workerd-windows-64': 1.20260721.1 + '@cloudflare/workerd-darwin-64': 1.20260722.1 + '@cloudflare/workerd-darwin-arm64': 1.20260722.1 + '@cloudflare/workerd-linux-64': 1.20260722.1 + '@cloudflare/workerd-linux-arm64': 1.20260722.1 + '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.113.0(@cloudflare/workers-types@5.20260723.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260721.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260721.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260721.1 + workerd: 1.20260722.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260723.1 + '@cloudflare/workers-types': 5.20260724.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 4e41ec66705d9ff22a235835c932ac9bef7e168a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 21:10:04 +0800 Subject: [PATCH 0411/1190] chore(deps): bump devenv from `9767a1f` to `fc06618` (#22815) Bumps [devenv](https://github.com/cachix/devenv) from `9767a1f` to `fc06618`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/9767a1f458fbd99b487dbb600a130917d4cd2b13...fc06618ba2099eaec355764f3ec5c395d45cec7c) --- updated-dependencies: - dependency-name: devenv dependency-version: fc06618ba2099eaec355764f3ec5c395d45cec7c dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9458658bdbb1..63e4df494a8d 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784758116, - "narHash": "sha256-vcjRSkJHM5/2rLmtCZBhms/WDJpyPNXW9Gi6xeSfc/Y=", + "lastModified": 1784849577, + "narHash": "sha256-vVJl1B6vuVkpmkGYxSaHHpjwCZCP59OsXlSo4bQontk=", "owner": "cachix", "repo": "devenv", - "rev": "9767a1f458fbd99b487dbb600a130917d4cd2b13", + "rev": "fc06618ba2099eaec355764f3ec5c395d45cec7c", "type": "github" }, "original": { From 0294ce99dba4b26f0ce9d9dc862892c2924a11e1 Mon Sep 17 00:00:00 2001 From: whatever <99734497+wha7ev9r@users.noreply.github.com> Date: Sat, 25 Jul 2026 05:06:28 +0800 Subject: [PATCH 0412/1190] fix(dongqiudi): adapt routes to new __NUXT__ data structure (#22720) * route: fix dongqiudi routes for new __NUXT__ data structure * fix(dongqiudi): remove unused mobile fallback * fix(dongqiudi): tolerate unavailable special articles * fix(dongqiudi): remove unnecessary bool return from ProcessFeedType2 --- lib/routes/dongqiudi/daily.ts | 2 +- lib/routes/dongqiudi/player-news.ts | 2 +- lib/routes/dongqiudi/result.ts | 25 ++++++++----- lib/routes/dongqiudi/special.ts | 18 ++++++--- lib/routes/dongqiudi/top-news.ts | 2 +- lib/routes/dongqiudi/utils.ts | 58 +++++++++-------------------- 6 files changed, 47 insertions(+), 60 deletions(-) diff --git a/lib/routes/dongqiudi/daily.ts b/lib/routes/dongqiudi/daily.ts index 5c0611eaf04c..f0ec8fa57eb6 100644 --- a/lib/routes/dongqiudi/daily.ts +++ b/lib/routes/dongqiudi/daily.ts @@ -19,5 +19,5 @@ export const route: Route = { }; function handler(ctx) { - ctx.set('redirect', '/dongqiudi/special/48'); + return ctx.set('redirect', '/dongqiudi/special/48'); } diff --git a/lib/routes/dongqiudi/player-news.ts b/lib/routes/dongqiudi/player-news.ts index 0074ff5e0f0f..6487f3cfbd15 100644 --- a/lib/routes/dongqiudi/player-news.ts +++ b/lib/routes/dongqiudi/player-news.ts @@ -21,5 +21,5 @@ export const route: Route = { async function handler(ctx) { const playerId = ctx.req.param('id'); - await utils.ProcessFeed(ctx, 'player', playerId); + return await utils.ProcessFeed(ctx, 'player', playerId); } diff --git a/lib/routes/dongqiudi/result.ts b/lib/routes/dongqiudi/result.ts index 1b424d07a66b..49a813691ea8 100644 --- a/lib/routes/dongqiudi/result.ts +++ b/lib/routes/dongqiudi/result.ts @@ -1,5 +1,3 @@ -import { JSDOM } from 'jsdom'; - import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -24,20 +22,27 @@ async function handler(ctx) { const team = ctx.req.param('team'); const link = `https://www.dongqiudi.com/team/${team}.html`; - const response = await got(link); - const dom = new JSDOM(response.data, { - runScripts: 'dangerously', - }); - const data = dom.window.__NUXT__.data[0]; - const resultData = data.teamScheduleData.filter((data) => data.fs_A && data.fs_B); + const { data: scheduleData } = await got(`https://api.dongqiudi.com/data/v1/team/schedule/${team}`); + const lastSeason = scheduleData.season_list.find((s) => !s.current); + + if (!lastSeason) { + return { + title: `${team} 比赛结果`, + link, + item: [], + }; + } + + const { data: seasonResp } = await got(lastSeason.url); + const resultData = seasonResp.data.filter((match) => match.fs_A && match.fs_B); - const teamName = data.teamDetail.base_info.team_name; + const teamName = resultData.length ? (resultData[0].team_A_id === team ? resultData[0].team_A_name : resultData[0].team_B_name) : team; const out = resultData.map((result) => ({ title: `${result.match_title} ${result.team_A_name} ${result.fs_A}-${result.fs_B} ${result.team_B_name}`, guid: result.match_id, link: result.scheme.replace('dongqiudi:///game/', 'https://www.dongqiudi.com/liveDetail/'), - pubDate: parseDate(result.start_time), + pubDate: parseDate(result.start_play), })); return { diff --git a/lib/routes/dongqiudi/special.ts b/lib/routes/dongqiudi/special.ts index 206051771fa9..c1bcaeafea62 100644 --- a/lib/routes/dongqiudi/special.ts +++ b/lib/routes/dongqiudi/special.ts @@ -9,7 +9,9 @@ export const route: Route = { path: '/special/:id', categories: ['sport'], example: '/dongqiudi/special/41', - parameters: { id: '专题 id, 可自行通过 https://www.dongqiudi.com/special/+数字匹配' }, + parameters: { + id: '专题 id, 可自行通过 https://www.dongqiudi.com/special/+数字匹配', + }, radar: [ { source: ['www.dongqiudi.com/special/:id'], @@ -30,17 +32,21 @@ async function handler(ctx) { const list = response.data.map((item) => ({ title: item.title, link: `https://www.dongqiudi.com/articles/${item.aid}.html`, - mobileLink: `https://m.dongqiudi.com/article/${item.aid}.html`, pubDate: parseDate(item.show_time, 'X'), })); const out = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const { data: response } = await got(item.mobileLink); - - utils.ProcessFeedType3(item, response); - + try { + const { data: response } = await got(item.link); + utils.ProcessFeedType2(item, response); + } catch (error) { + if (!(error instanceof Error) || !['HTTPError', 'RequestError', 'FetchError'].includes(error.name)) { + throw error; + } + // Keep the list item when the article page is gone or temporarily unavailable. + } return item; }) ) diff --git a/lib/routes/dongqiudi/top-news.ts b/lib/routes/dongqiudi/top-news.ts index 98b9a7cf8e95..6b45e94ed540 100644 --- a/lib/routes/dongqiudi/top-news.ts +++ b/lib/routes/dongqiudi/top-news.ts @@ -42,7 +42,7 @@ async function handler(ctx) { title: item.title, link: `https://www.dongqiudi.com/articles/${item.id}.html`, category: [item.category, ...(item.secondary_category ?? [])], - pubDate: parseDate(item.show_time), + pubDate: parseDate(item.show_time, 'X'), })); const out = await Promise.all( diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index 2b27887d450d..cdb4d51d5f63 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -68,17 +68,19 @@ const ProcessFeed = async (ctx, type, id) => { const apiUrl = 'https://api.dongqiudi.com/v3/archive/app/channel/feeds'; const { data: response } = await got(link); - let name; - const { window } = new JSDOM(response, { runScripts: 'dangerously', }); - const typeInfo = window.__NUXT__.data[0][`${type}Detail`].base_info; + const nuxtData = window.__NUXT__.data[0]; + let name; + let image; if (type === 'team') { - name = typeInfo.team_name; - } else if (type === 'player') { - name = typeInfo.person_name; + name = nuxtData.teamInfo.name; + image = nuxtData.teamInfo.logo; + } else { + name = nuxtData.detail.base_info.person_name; + image = nuxtData.detail.base_info.person_logo; } const { data } = await got(apiUrl, { @@ -95,7 +97,7 @@ const ProcessFeed = async (ctx, type, id) => { title: article.title, link: `https://www.dongqiudi.com/articles/${article.id}.html`, category: [article.category, ...(article.secondary_category ?? [])], - pubDate: parseDate(article.show_time), + pubDate: parseDate(article.show_time, 'X'), })); const out = await Promise.all( @@ -113,7 +115,7 @@ const ProcessFeed = async (ctx, type, id) => { return { title: `${name} - 相关新闻`, link, - image: type === 'team' ? typeInfo.team_logo : typeInfo.person_logo, + image, item: out, }; }; @@ -123,44 +125,18 @@ const ProcessFeedType2 = (item, response) => { runScripts: 'dangerously', }); - const data = dom.window.__NUXT__.data[0].newData; + const data = dom.window.__NUXT__?.data?.[0]?.article; // filter out undefined item if (!data) { return; } - if (Object.keys(data).length > 0) { - const body = ProcessVideo(load(data.body, null, false)); - ProcessHref(body('a')); - ProcessImg(body('img')); - item.description = body.html(); - item.author = data.writer; - item.pubDate = parseDate(data.show_time, 'X'); - } -}; - -const ProcessFeedType3 = (item, response) => { - const $ = load(response); - const initialState = JSON.parse( - $('script:contains("window.__INITIAL_STATE__")') - .text() - .match(/window\.__INITIAL_STATE__\s*=\s*((?:\S.*?)??);\(/)[1] - ); - - // filter out undefined item - if (!initialState) { - return; - } - - if (Object.keys(initialState.articleContent).length) { - const data = Object.values(initialState.articleContent)[0]; - const body = ProcessVideo(load(data.body, null, false)); - ProcessHref(body('a')); - ProcessImg(body('img')); - item.description = body.html(); - item.author = data.writer; - } + const body = ProcessVideo(load(data.rawBody, null, false)); + ProcessHref(body('a')); + ProcessImg(body('img')); + item.description = body.html(); + item.author = data.author; }; -export default { ProcessVideo, ProcessFeed, ProcessFeedType2, ProcessFeedType3, ProcessHref, ProcessImg }; +export default { ProcessVideo, ProcessFeed, ProcessFeedType2, ProcessHref, ProcessImg }; From 91d71f14e7acc17a9831da31ae7cc2df2d941865 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:13:33 +0800 Subject: [PATCH 0413/1190] fix(route): remove dyanmic date stamp for link (#22818) * fix(route): remove dynamic date stamp from the link * fix(route): remove dynamic date stamp from the link * fix: update radar source * fix: update radar source to restore www --- lib/routes/szmuseum/temporary.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/routes/szmuseum/temporary.tsx b/lib/routes/szmuseum/temporary.tsx index 88ec25bccc92..ec33cf64d885 100644 --- a/lib/routes/szmuseum/temporary.tsx +++ b/lib/routes/szmuseum/temporary.tsx @@ -63,7 +63,9 @@ export const route: Route = { const $item = $(item); const a = $item.find('h1 a'); const title = a.text(); - const link = new URL(a.attr('href') || '', baseUrl).href; + const linkStr = new URL(a.attr('href') || '', baseUrl); + linkStr.search = ''; // remove dynamic date stamp from the link + const link = linkStr.href; const imgUrl = new URL($item.find('.aleftimg img').attr('src') || '', baseUrl).href; const fullDuration = $item.find('.activity_r_detail p:nth-child(1) span:nth-child(2)').text().trim(); From 01039207441030614eb13864901b7637daf95074 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:23:33 +0000 Subject: [PATCH 0414/1190] Update VOUCHED list https://github.com/DIYgod/RSSHub/issues/22801#issuecomment-5077973323 --- .github/VOUCHED.td | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index e19d0249c864..c21361bfb3e5 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -16,4 +16,5 @@ hyoban neverbehave pseudoyu tonyrl +-vizmoe impersonate as pseudoyu in #22801. ae0a44ebb738ace62100f66d15d84272356a72c9 zhenlonghe From 8a2128988adc33aaa9b8e01c1120951968120d94 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:24:59 +0800 Subject: [PATCH 0415/1190] feat(route): add china silk museum exhibit route (#22809) * feat(route): add china silk museum exhibit route * fix: update radar source * fix: update rawSrc to remove redundancy * fix: update according to part of bot review --- lib/routes/chinasilkmuseum/namespace.ts | 10 ++ lib/routes/chinasilkmuseum/zz.tsx | 119 ++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 lib/routes/chinasilkmuseum/namespace.ts create mode 100644 lib/routes/chinasilkmuseum/zz.tsx diff --git a/lib/routes/chinasilkmuseum/namespace.ts b/lib/routes/chinasilkmuseum/namespace.ts new file mode 100644 index 000000000000..b2b037d1be57 --- /dev/null +++ b/lib/routes/chinasilkmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'China National Silk Museum', + url: 'www.chinasilkmuseum.com', + + zh: { + name: '中国丝绸博物馆', + }, +}; diff --git a/lib/routes/chinasilkmuseum/zz.tsx b/lib/routes/chinasilkmuseum/zz.tsx new file mode 100644 index 000000000000..d6bd51b494bf --- /dev/null +++ b/lib/routes/chinasilkmuseum/zz.tsx @@ -0,0 +1,119 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +// Convert YYYY年M月D日 to YYYY-MM-DD +const fmtExhibitionDate = (raw: string | undefined) => { + if (!raw) { + return; + } + const d = dayjs(raw.trim(), 'YYYY年M月D日'); + return d.isValid() ? d.format('YYYY-MM-DD') : undefined; +}; + +// used for 2026年6月24日 - 2026年9月1日 +const parseExhibitionDuration = (fullDuration: string): { startDate: string | undefined; endDate: string | undefined } => { + const [startRaw, endRaw] = fullDuration.split(' - ', 2); + return { startDate: fmtExhibitionDate(startRaw), endDate: fmtExhibitionDate(endRaw) }; +}; + +export const route: Route = { + path: '/zz', + categories: ['travel'], + example: '/chinasilkmuseum/zz', + name: 'Exhibition', + maintainers: ['magazian'], + radar: [ + { + source: ['www.chinasilkmuseum.com/zz/list_17.aspx'], + target: '/zz', + }, + ], + handler: async () => { + const baseUrl = 'https://www.chinasilkmuseum.com'; + const listUrl = `${baseUrl}/zz/list_17.aspx`; + const museumName = namespace.zh?.name ?? namespace.name; + + const response = await ofetch(listUrl); + const $ = load(response); + + const items = $('div.about_body div.show_info ul.ul li') + .toArray() + .map((el) => { + const $el = $(el); + const $titleLink = $el.find('div.show_text h3.h3 a'); + const title = $titleLink.text(); + const rawHref = $titleLink.attr('href') ?? ''; + const link = new URL(rawHref, baseUrl).href; + + const rawSrc = $el.find('a > img').attr('src') ?? ''; + const imgUrl = new URL(rawSrc, baseUrl).href; + + return { title, link, imgUrl }; + }); + + const list = await Promise.all( + items.map((item) => + cache.tryGet(item.link, async () => { + const detailRes = await ofetch(item.link); + const $d = load(detailRes); + const location = $d('div.detail_text p').first().text().replace('展览地点:', '').trim(); + const fullDuration = $d('div.detail_text p').eq(1).text().replace('展览时间:', '').trim(); + const { startDate, endDate } = parseExhibitionDuration(fullDuration); + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( + <div> + <img src={item.imgUrl} /> + <br /> + <p> + <b>地点:</b> + {location || '参考详情'} + </p> + <p> + <b>开展:</b> + {startDate || '未定/常设'} + </p> + <p> + <b>闭展:</b> + {endDate || '未定/常设'} + </p> + {fullDuration && ( + <p> + <small>原始展期:{fullDuration}</small> + </p> + )} + </div> + ); + + return { + title: item.title, + link: item.link, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + } as DataItem; + }) + ) + ); + + return { + title: `${museumName} - 在展`, + link: listUrl, + language: 'zh-CN', + item: list, + } as Data; + }, +}; From f3109724aacd3c0196eb07258d4c29a408a8db41 Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Sat, 25 Jul 2026 18:10:59 +0800 Subject: [PATCH 0416/1190] chore: set max diff to 1000 --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 94cb10f25ade..3fbe334b8b0e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -144,3 +144,4 @@ jobs: Thanks for the contribution. This PR was automatically closed because it matched multiple low-quality/spam signals. lock-pr: false + max-changed-lines: 1000 From a9f52c9adb0ca85040c94c2cb16ca43785bcb7f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:33:05 +0000 Subject: [PATCH 0417/1190] chore(deps-dev): bump discord-api-types from 0.38.50 to 0.38.51 (#22821) Bumps [discord-api-types](https://github.com/discordjs/discord-api-types) from 0.38.50 to 0.38.51. - [Release notes](https://github.com/discordjs/discord-api-types/releases) - [Changelog](https://github.com/discordjs/discord-api-types/blob/main/CHANGELOG.md) - [Commits](https://github.com/discordjs/discord-api-types/compare/0.38.50...0.38.51) --- updated-dependencies: - dependency-name: discord-api-types dependency-version: 0.38.51 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index ae3e86db42da..4865ce175f2d 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,7 @@ "@typescript-eslint/parser": "8.65.0", "@vercel/nft": "1.10.2", "@vitest/coverage-v8": "4.1.10", - "discord-api-types": "0.38.50", + "discord-api-types": "0.38.51", "domhandler": "6.0.1", "eslint": "10.7.0", "eslint-nibble": "9.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70a52543ddff..d6eee7d8124e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -369,8 +369,8 @@ importers: specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) discord-api-types: - specifier: 0.38.50 - version: 0.38.50 + specifier: 0.38.51 + version: 0.38.51 domhandler: specifier: 6.0.1 version: 6.0.1 @@ -3685,11 +3685,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 - discord-api-types@0.38.50: - resolution: {integrity: sha512-J2n/bpIETX3DQ6AJ7/0xbsTLmYiJQtO/LKcXKC1YDbB56OUwtDbdXOFE8Q4g8jVGHBR2VAy1+D4ngaIgkMNV9w==} + discord-api-types@0.38.51: + resolution: {integrity: sha512-SnaFHd+b8Z3HgjEIoSA1wkstXCt6J0Zrxvny0BJZZz7AhmFSetRY3DMrIdO3LFDMeeTxF7WKornnAGxaws5Adw==} dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -5343,8 +5343,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.22: - resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==} + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -9009,7 +9009,7 @@ snapshots: dependencies: heap: 0.2.7 - discord-api-types@0.38.50: {} + discord-api-types@0.38.51: {} dom-serializer@1.4.1: dependencies: @@ -11075,7 +11075,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.22: + postcss@8.5.23: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -12005,7 +12005,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.22 + postcss: 8.5.23 rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: From fde0b12b367a0eff4526a027e50452b2a74e9f63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:34:50 +0000 Subject: [PATCH 0418/1190] chore(deps): bump hono from 4.12.31 to 4.12.32 (#22824) Bumps [hono](https://github.com/honojs/hono) from 4.12.31 to 4.12.32. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.31...v4.12.32) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.32 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 4865ce175f2d..860baada77ee 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", "header-generator": "2.1.82", - "hono": "4.12.31", + "hono": "4.12.32", "html-to-text": "10.0.0", "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6eee7d8124e..f37843f69df8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,10 +48,10 @@ importers: version: 6.15.3 '@hono/node-server': specifier: 2.0.11 - version: 2.0.11(hono@4.12.31) + version: 2.0.11(hono@4.12.32) '@hono/zod-openapi': specifier: 1.5.1 - version: 1.5.1(hono@4.12.31)(zod@4.4.3) + version: 1.5.1(hono@4.12.32)(zod@4.4.3) '@jocmp/mercury-parser': specifier: 3.0.9 version: 3.0.9 @@ -84,7 +84,7 @@ importers: version: 0.0.25 '@scalar/hono-api-reference': specifier: 0.11.11 - version: 0.11.11(hono@4.12.31) + version: 0.11.11(hono@4.12.32) '@sentry/node': specifier: 10.67.0 version: 10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)) @@ -128,8 +128,8 @@ importers: specifier: 2.1.82 version: 2.1.82 hono: - specifier: 4.12.31 - version: 4.12.31 + specifier: 4.12.32 + version: 4.12.32 html-to-text: specifier: 10.0.0 version: 10.0.0 @@ -4248,8 +4248,8 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.12.31: - resolution: {integrity: sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==} + hono@4.12.32: + resolution: {integrity: sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==} engines: {node: '>=16.9.0'} hookable@6.1.1: @@ -6968,21 +6968,21 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.11(hono@4.12.31)': + '@hono/node-server@2.0.11(hono@4.12.32)': dependencies: - hono: 4.12.31 + hono: 4.12.32 - '@hono/zod-openapi@1.5.1(hono@4.12.31)(zod@4.4.3)': + '@hono/zod-openapi@1.5.1(hono@4.12.32)(zod@4.4.3)': dependencies: '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) - '@hono/zod-validator': 0.9.0(hono@4.12.31)(zod@4.4.3) - hono: 4.12.31 + '@hono/zod-validator': 0.9.0(hono@4.12.32)(zod@4.4.3) + hono: 4.12.32 openapi3-ts: 4.6.0 zod: 4.4.3 - '@hono/zod-validator@0.9.0(hono@4.12.31)(zod@4.4.3)': + '@hono/zod-validator@0.9.0(hono@4.12.32)(zod@4.4.3)': dependencies: - hono: 4.12.31 + hono: 4.12.32 zod: 4.4.3 '@humanfs/core@0.19.2': @@ -8005,10 +8005,10 @@ snapshots: '@scalar/helpers@0.9.2': {} - '@scalar/hono-api-reference@0.11.11(hono@4.12.31)': + '@scalar/hono-api-reference@0.11.11(hono@4.12.32)': dependencies: '@scalar/client-side-rendering': 0.3.4 - hono: 4.12.31 + hono: 4.12.32 '@scalar/schemas@0.7.4': dependencies: @@ -9716,7 +9716,7 @@ snapshots: hmacsha1@1.0.0: {} - hono@4.12.31: {} + hono@4.12.32: {} hookable@6.1.1: {} From 9cca12ae4374a43c1b24edaca5005a36ff8ee3de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:41:47 +0000 Subject: [PATCH 0419/1190] chore(deps): bump header-generator from 2.1.82 to 2.1.86 (#22823) Bumps [header-generator](https://github.com/apify/fingerprint-suite) from 2.1.82 to 2.1.86. - [Release notes](https://github.com/apify/fingerprint-suite/releases) - [Commits](https://github.com/apify/fingerprint-suite/compare/v2.1.82...v2.1.86) --- updated-dependencies: - dependency-name: header-generator dependency-version: 2.1.86 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 68 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 860baada77ee..02f6a406dc93 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "etag": "1.8.1", "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", - "header-generator": "2.1.82", + "header-generator": "2.1.86", "hono": "4.12.32", "html-to-text": "10.0.0", "http-cookie-agent": "8.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f37843f69df8..2085a0be3873 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,8 +125,8 @@ importers: specifier: 10.1.3 version: 10.1.3 header-generator: - specifier: 2.1.82 - version: 2.1.82 + specifier: 2.1.86 + version: 2.1.86 hono: specifier: 4.12.32 version: 4.12.32 @@ -3194,9 +3194,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adm-zip@0.5.18: - resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==} - engines: {node: '>=12.0'} + adm-zip@0.6.0: + resolution: {integrity: sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==} + engines: {node: '>=14.0'} agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} @@ -3297,6 +3297,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + baseline-browser-mapping@2.11.1: + resolution: {integrity: sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==} + engines: {node: '>=6.0.0'} + hasBin: true + basic-ftp@5.3.1: resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} engines: {node: '>=10.0.0'} @@ -3350,6 +3355,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -3398,6 +3408,9 @@ packages: caniuse-lite@1.0.30001805: resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -3764,6 +3777,9 @@ packages: electron-to-chromium@1.5.392: resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==} + electron-to-chromium@1.5.396: + resolution: {integrity: sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -4131,8 +4147,8 @@ packages: resolution: {integrity: sha512-ziTrzUhhpL9Zk5k0HHzgP/KIpWDJT0VMBC/ynt/QIBvTW+UUcSivQRl6VlwTf/EilDxtSWklHoRsKy1c4k+59w==} engines: {node: '>=18'} - generative-bayesian-network@2.1.83: - resolution: {integrity: sha512-LssI9es+oUoezoHloFGw0Hts0YEfujjBOE8KNl70oBt4HPjD/4rpUqcgZ/M7RCmgKvkmCyPB2KWowyJHuKyhfw==} + generative-bayesian-network@2.1.86: + resolution: {integrity: sha512-4+cZAUH6khje/EUKRlTlO+HpjA7aqvDlVBnBSJKZqC6qiJ3EitNZ97//xYrBbawZCHe1S1PGLza6knQTFpWCgw==} get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -4235,8 +4251,8 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - header-generator@2.1.82: - resolution: {integrity: sha512-4NjPB0+bAKjPoponSmTOkK58IEF2W22sOJA5O48k/MxbCZgOm+jrU4WVR53Z2I6xFgIPkVrQmKtt1LAbWtfqXw==} + header-generator@2.1.86: + resolution: {integrity: sha512-Nj+glwXBEU3PvvAT1WiC3FBhX1ReZZY+ADXIo2iTRqMzamLW0olJk7q/EJ6N3qJBqQWZJDJonAJRtAlzEFynLQ==} engines: {node: '>=16.0.0'} headers-polyfill@5.0.1: @@ -8591,7 +8607,7 @@ snapshots: acorn@8.17.0: {} - adm-zip@0.5.18: {} + adm-zip@0.6.0: {} agent-base@7.1.4: {} @@ -8666,6 +8682,8 @@ snapshots: baseline-browser-mapping@2.10.43: {} + baseline-browser-mapping@2.11.1: {} + basic-ftp@5.3.1: {} bcrypt-pbkdf@1.0.2: @@ -8718,6 +8736,14 @@ snapshots: node-releases: 2.0.51 update-browserslist-db: 1.2.3(browserslist@4.28.6) + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.1 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.396 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.7) + buffer-equal-constant-time@1.0.1: {} buffer@6.0.3: @@ -8766,6 +8792,8 @@ snapshots: caniuse-lite@1.0.30001805: {} + caniuse-lite@1.0.30001806: {} + caseless@0.12.0: {} ccount@2.0.1: {} @@ -8879,7 +8907,7 @@ snapshots: core-js-compat@3.49.0: dependencies: - browserslist: 4.28.6 + browserslist: 4.28.7 core-js@2.6.12: {} @@ -9093,6 +9121,8 @@ snapshots: electron-to-chromium@1.5.392: {} + electron-to-chromium@1.5.396: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -9566,9 +9596,9 @@ snapshots: transitivePeerDependencies: - supports-color - generative-bayesian-network@2.1.83: + generative-bayesian-network@2.1.86: dependencies: - adm-zip: 0.5.18 + adm-zip: 0.6.0 tslib: 2.8.1 get-caller-file@2.0.5: {} @@ -9700,10 +9730,10 @@ snapshots: he@1.2.0: {} - header-generator@2.1.82: + header-generator@2.1.86: dependencies: - browserslist: 4.28.6 - generative-bayesian-network: 2.1.83 + browserslist: 4.28.7 + generative-bayesian-network: 2.1.86 ow: 0.28.2 tslib: 2.8.1 @@ -11934,6 +11964,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 From 6ba3f37fd4620d6bbf84233b8b03ec8ea2a761fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:59:08 +0000 Subject: [PATCH 0420/1190] chore(deps): bump undici from 8.8.0 to 8.9.0 (#22825) Bumps [undici](https://github.com/nodejs/undici) from 8.8.0 to 8.9.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.8.0...v8.9.0) --- updated-dependencies: - dependency-name: undici dependency-version: 8.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 44 +++++++++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 02f6a406dc93..fba72e8d2e90 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "tsx": "4.23.1", "twitter-api-v2": "1.29.0", "ufo": "1.6.4", - "undici": "8.8.0", + "undici": "8.9.0", "uuid": "14.0.1", "winston": "3.19.0", "xxhash-wasm": "1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2085a0be3873..25ed2d19ff81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,7 +135,7 @@ importers: version: 10.0.0 http-cookie-agent: specifier: 8.0.0 - version: 8.0.0(tough-cookie@6.0.2)(undici@8.8.0) + version: 8.0.0(tough-cookie@6.0.2)(undici@8.9.0) https-proxy-agent: specifier: 9.1.0 version: 9.1.0 @@ -257,8 +257,8 @@ importers: specifier: 1.6.4 version: 1.6.4 undici: - specifier: 8.8.0 - version: 8.8.0 + specifier: 8.9.0 + version: 8.9.0 uuid: specifier: 14.0.1 version: 14.0.1 @@ -421,7 +421,7 @@ importers: version: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 - version: 1.0.30(undici@8.8.0)(utf-8-validate@5.0.10) + version: 1.0.30(undici@8.9.0)(utf-8-validate@5.0.10) oxc-parser: specifier: 0.141.0 version: 0.141.0 @@ -6069,16 +6069,20 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@6.27.0: - resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} engines: {node: '>=18.17'} undici@7.28.0: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} - undici@8.8.0: - resolution: {integrity: sha512-ubshXMXwF3MQIMF1y/WxZdNBnjEKeSg2wF5mcGUtU55YTw34tnVVpKRlLf7ruDXZ5344KokPVX4RBx1wJm64Bw==} + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + engines: {node: '>=20.18.1'} + + undici@8.9.0: + resolution: {integrity: sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==} engines: {node: '>=22.19.0'} unenv@2.0.0-rc.24: @@ -6515,17 +6519,17 @@ snapshots: '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 - undici: 6.27.0 + undici: 6.28.0 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.27.0 + undici: 6.28.0 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.27.0 + undici: 6.28.0 '@actions/io@3.0.2': {} @@ -8828,7 +8832,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.28.0 + undici: 7.29.0 whatwg-mimetype: 4.0.0 chownr@3.0.0: {} @@ -9789,12 +9793,12 @@ snapshots: http-cache-semantics@4.2.0: {} - http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.8.0): + http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.9.0): dependencies: agent-base: 9.0.0 tough-cookie: 6.0.2 optionalDependencies: - undici: 8.8.0 + undici: 8.9.0 http-proxy-agent@9.1.0: dependencies: @@ -10067,7 +10071,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.2 - undici: 7.28.0 + undici: 7.29.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10752,13 +10756,13 @@ snapshots: dependencies: write-file-atomic: 1.3.4 - node-network-devtools@1.0.30(undici@8.8.0)(utf-8-validate@5.0.10): + node-network-devtools@1.0.30(undici@8.9.0)(utf-8-validate@5.0.10): dependencies: bufferutil: 4.1.0 iconv-lite: 0.7.3 inspector: 0.5.0 open: 8.4.2 - undici: 8.8.0 + undici: 8.9.0 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -11894,11 +11898,13 @@ snapshots: undici-types@8.3.0: {} - undici@6.27.0: {} + undici@6.28.0: {} undici@7.28.0: {} - undici@8.8.0: {} + undici@7.29.0: {} + + undici@8.9.0: {} unenv@2.0.0-rc.24: dependencies: From 2221f62bdb3e36f46458f31affec7a4ee696b153 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:40:46 +0800 Subject: [PATCH 0421/1190] chore(deps-dev): bump eslint in the eslint group across 1 directory (#22820) Bumps the eslint group with 1 update in the / directory: [eslint](https://github.com/eslint/eslint). Updates `eslint` from 10.7.0 to 10.8.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.7.0...v10.8.0) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.8.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 161 ++++++++++++++++++++++++++----------------------- 2 files changed, 87 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index fba72e8d2e90..fbab5587412a 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "@vitest/coverage-v8": "4.1.10", "discord-api-types": "0.38.51", "domhandler": "6.0.1", - "eslint": "10.7.0", + "eslint": "10.8.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.2.2", "eslint-plugin-regexp": "3.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25ed2d19ff81..950c1e708b83 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,13 +304,13 @@ importers: version: 3.3.6 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.7.0) + version: 10.0.1(eslint@10.8.0) '@oxlint/plugins': specifier: 1.75.0 version: 1.75.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.7.0) + version: 5.10.0(eslint@10.8.0) '@types/babel__preset-env': specifier: 7.10.0 version: 7.10.0 @@ -358,10 +358,10 @@ importers: version: 2.16.1 '@typescript-eslint/eslint-plugin': specifier: 8.65.0 - version: 8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0) + version: 8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0))(@typescript/typescript6@6.0.2)(eslint@10.8.0) '@typescript-eslint/parser': specifier: 8.65.0 - version: 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + version: 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) '@vercel/nft': specifier: 1.10.2 version: 1.10.2(rollup@4.62.2) @@ -375,26 +375,26 @@ importers: specifier: 6.0.1 version: 6.0.1 eslint: - specifier: 10.7.0 - version: 10.7.0 + specifier: 10.8.0 + version: 10.8.0 eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.1.1)(eslint@10.7.0) + version: 9.1.1(@types/node@26.1.1)(eslint@10.8.0) eslint-plugin-n: specifier: 18.2.2 - version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.7.0) + version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.8.0) eslint-plugin-regexp: specifier: 3.1.1 - version: 3.1.1(eslint@10.7.0) + version: 3.1.1(eslint@10.8.0) eslint-plugin-simple-import-sort: specifier: 14.0.0 - version: 14.0.0(eslint@10.7.0) + version: 14.0.0(eslint@10.8.0) eslint-plugin-unicorn: specifier: 72.0.0 - version: 72.0.0(eslint@10.7.0) + version: 72.0.0(eslint@10.8.0) eslint-plugin-yml: specifier: 3.6.0 - version: 3.6.0(eslint@10.7.0) + version: 3.6.0(eslint@10.8.0) fast-string-width: specifier: 3.0.2 version: 3.0.2 @@ -1024,6 +1024,12 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1038,8 +1044,8 @@ packages: resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/config-helpers@0.6.0': - resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@1.2.1': @@ -3346,9 +3352,9 @@ packages: brace-expansion@2.1.2: resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} - brace-expansion@5.0.7: - resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.8: + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + engines: {node: 20 || >=22} browserslist@4.28.6: resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} @@ -3698,7 +3704,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.51: @@ -3954,8 +3960,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.7.0: - resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} + eslint@10.8.0: + resolution: {integrity: sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -4092,8 +4098,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flatted@3.4.3: + resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==} fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} @@ -6914,9 +6920,14 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0)': + '@eslint-community/eslint-utils@4.10.1(eslint@10.8.0)': + dependencies: + eslint: 10.8.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@10.8.0)': dependencies: - eslint: 10.7.0 + eslint: 10.8.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -6929,7 +6940,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.6.0': + '@eslint/config-helpers@0.7.0': dependencies: '@eslint/core': 1.2.1 @@ -6956,9 +6967,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.7.0)': + '@eslint/js@10.0.1(eslint@10.8.0)': optionalDependencies: - eslint: 10.7.0 + eslint: 10.8.0 '@eslint/object-schema@3.0.5': {} @@ -8119,11 +8130,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.7.0)': + '@stylistic/eslint-plugin@5.10.0(eslint@10.8.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0) '@typescript-eslint/types': 8.63.0 - eslint: 10.7.0 + eslint: 10.8.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -8291,15 +8302,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0))(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0))(@typescript/typescript6@6.0.2)(eslint@10.8.0)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/parser': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) '@typescript-eslint/scope-manager': 8.65.0 - '@typescript-eslint/type-utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) - '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/type-utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) + '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) '@typescript-eslint/visitor-keys': 8.65.0 - eslint: 10.7.0 + eslint: 10.8.0 ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8307,14 +8318,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/parser@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0)': dependencies: '@typescript-eslint/scope-manager': 8.65.0 '@typescript-eslint/types': 8.65.0 '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) '@typescript-eslint/visitor-keys': 8.65.0 debug: 4.4.3 - eslint: 10.7.0 + eslint: 10.8.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8337,13 +8348,13 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/type-utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0)': dependencies: '@typescript-eslint/types': 8.65.0 '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0) + '@typescript-eslint/utils': 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) debug: 4.4.3 - eslint: 10.7.0 + eslint: 10.8.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -8368,13 +8379,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.7.0)': + '@typescript-eslint/utils@8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0) '@typescript-eslint/scope-manager': 8.65.0 '@typescript-eslint/types': 8.65.0 '@typescript-eslint/typescript-estree': 8.65.0(@typescript/typescript6@6.0.2) - eslint: 10.7.0 + eslint: 10.8.0 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8728,7 +8739,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.7: + brace-expansion@5.0.8: dependencies: balanced-match: 4.0.4 @@ -9264,43 +9275,43 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.7.0): + eslint-compat-utils@0.5.1(eslint@10.8.0): dependencies: - eslint: 10.7.0 + eslint: 10.8.0 semver: 7.8.5 - eslint-filtered-fix@0.3.0(eslint@10.7.0): + eslint-filtered-fix@0.3.0(eslint@10.8.0): dependencies: - eslint: 10.7.0 + eslint: 10.8.0 optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.7.0): + eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.8.0): dependencies: '@babel/code-frame': 7.29.7 '@inquirer/checkbox': 4.3.2(@types/node@26.1.1) '@inquirer/confirm': 5.1.21(@types/node@26.1.1) '@inquirer/select': 4.4.2(@types/node@26.1.1) - eslint: 10.7.0 - eslint-filtered-fix: 0.3.0(eslint@10.7.0) + eslint: 10.8.0 + eslint-filtered-fix: 0.3.0(eslint@10.8.0) optionator: 0.9.4 text-table: 0.2.0 yoctocolors: 2.1.2 transitivePeerDependencies: - '@types/node' - eslint-plugin-es-x@7.8.0(eslint@10.7.0): + eslint-plugin-es-x@7.8.0(eslint@10.8.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0) '@eslint-community/regexpp': 4.12.2 - eslint: 10.7.0 - eslint-compat-utils: 0.5.1(eslint@10.7.0) + eslint: 10.8.0 + eslint-compat-utils: 0.5.1(eslint@10.8.0) - eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.7.0): + eslint-plugin-n@18.2.2(@typescript/typescript6@6.0.2)(eslint@10.8.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0) enhanced-resolve: 5.24.2 - eslint: 10.7.0 - eslint-plugin-es-x: 7.8.0(eslint@10.7.0) + eslint: 10.8.0 + eslint-plugin-es-x: 7.8.0(eslint@10.8.0) get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 @@ -9309,24 +9320,24 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.1.1(eslint@10.7.0): + eslint-plugin-regexp@3.1.1(eslint@10.8.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.7 - eslint: 10.7.0 + eslint: 10.8.0 jsdoc-type-pratt-parser: 7.2.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@14.0.0(eslint@10.7.0): + eslint-plugin-simple-import-sort@14.0.0(eslint@10.8.0): dependencies: - eslint: 10.7.0 + eslint: 10.8.0 - eslint-plugin-unicorn@72.0.0(eslint@10.7.0): + eslint-plugin-unicorn@72.0.0(eslint@10.8.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0) '@eslint/css-tree': 4.0.4 browserslist: 4.28.6 change-case: 5.4.4 @@ -9334,7 +9345,7 @@ snapshots: core-js-compat: 3.49.0 detect-indent: 7.0.2 entities: 4.5.0 - eslint: 10.7.0 + eslint: 10.8.0 find-up-simple: 1.0.1 globals: 17.7.0 indent-string: 5.0.0 @@ -9348,14 +9359,14 @@ snapshots: strip-indent: 4.1.1 yaml: 2.9.0 - eslint-plugin-yml@3.6.0(eslint@10.7.0): + eslint-plugin-yml@3.6.0(eslint@10.8.0): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.7.0 + eslint: 10.8.0 natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -9372,12 +9383,12 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.7.0: + eslint@10.8.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 - '@eslint/config-helpers': 0.6.0 + '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@humanfs/node': 0.16.8 @@ -9532,10 +9543,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.2 + flatted: 3.4.3 keyv: 4.5.4 - flatted@3.4.2: {} + flatted@3.4.3: {} fn.name@1.1.0: {} @@ -10664,7 +10675,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.7 + brace-expansion: 5.0.8 minimatch@3.1.5: dependencies: From c0825f01dd4b373322fb187953a205b585795545 Mon Sep 17 00:00:00 2001 From: Sryvkver <20717854+Sryvkver@users.noreply.github.com> Date: Sat, 25 Jul 2026 15:47:57 +0200 Subject: [PATCH 0422/1190] fix(route/fanbox) Update PostListResponse model to conform to new API response (#22828) Co-authored-by: Sryvkver <git@sryvkver.com> --- lib/routes/fanbox/index.ts | 2 +- lib/routes/fanbox/types.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/routes/fanbox/index.ts b/lib/routes/fanbox/index.ts index 4e8a0d4855d2..206ec22afd48 100644 --- a/lib/routes/fanbox/index.ts +++ b/lib/routes/fanbox/index.ts @@ -81,7 +81,7 @@ async function handler(ctx: Context): Promise<Data> { let items: DataItem[]; try { - items = await Promise.all(postListResponse.body.map((i) => parseItem(page, i))); + items = await Promise.all(postListResponse.body.posts.map((i) => parseItem(page, i))); } finally { await page.close(); await context.close(); diff --git a/lib/routes/fanbox/types.ts b/lib/routes/fanbox/types.ts index 774843c3963d..bdcc5263617f 100644 --- a/lib/routes/fanbox/types.ts +++ b/lib/routes/fanbox/types.ts @@ -25,7 +25,9 @@ export interface UserInfoResponse { } export interface PostListResponse { - body: PostItem[]; + body: { + posts: PostItem[]; + }; } export interface PostDetailResponse { From a1e16cce7dd81cff0c1b01f8872d872f2dc84e64 Mon Sep 17 00:00:00 2001 From: TonyRL <TonyRL@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:02:53 +0800 Subject: [PATCH 0423/1190] chore: add effort support --- .github/workflows/lint.yml | 3 +-- .github/workflows/pr-review.yml | 1 + .github/workflows/similar-issues.yml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3fbe334b8b0e..ce07aa9cbce0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -141,7 +141,6 @@ jobs: exempt-label: exempt failure-add-pr-labels: spam failure-pr-message: | - Thanks for the contribution. This PR was automatically closed because - it matched multiple low-quality/spam signals. + Thanks for the contribution. This PR was automatically closed because it matched multiple low-quality/spam signals. lock-pr: false max-changed-lines: 1000 diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index 93e7f6df66a8..dc072fed2ed8 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -90,6 +90,7 @@ jobs: display_report: 'true' claude_args: | --model ${{ vars.OPENCODE_MODEL }} + ${{ vars.CLAUDE_EFFORT && format('--effort {0}', vars.CLAUDE_EFFORT) || '' }} --allowedTools "Bash(base64:*),Bash(cat:*),Bash(echo:*),Bash(gh api:*),Bash(gh auth:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh repo view:*),Bash(gh search:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git status:*),Bash(grep:*),Bash(head:*),Bash(ls:*),Bash(rg:*),Bash(sed:*),Bash(tail:*),Bash(wc:*),${{ github.event_name == 'workflow_dispatch' && 'WebFetch,WebSearch' || 'WebFetch(domain:docs.rsshub.app)' }}" ${{ github.event_name != 'workflow_dispatch' && '--disallowedTools "WebSearch"' || '' }} prompt: | diff --git a/.github/workflows/similar-issues.yml b/.github/workflows/similar-issues.yml index 14e95701d0c8..3cab40d9d67a 100644 --- a/.github/workflows/similar-issues.yml +++ b/.github/workflows/similar-issues.yml @@ -32,6 +32,7 @@ jobs: allowed_non_write_users: '*' claude_args: | --model ${{ vars.OPENCODE_MODEL }} + ${{ vars.CLAUDE_EFFORT && format('--effort {0}', vars.CLAUDE_EFFORT) || '' }} --allowedTools "Bash(gh issue:*)" --disallowedTools "WebFetch,WebSearch" prompt: | From 5d6338ee344ccc1d986d059f9e41c8b9e6ec0c33 Mon Sep 17 00:00:00 2001 From: Tim <ovo-tim@qq.com> Date: Sun, 26 Jul 2026 21:38:01 +0800 Subject: [PATCH 0424/1190] feat(route): add Forward Future routes (daily newsletter + originals) (#22827) * feat: add Forward Future routes (daily newsletter + originals) * feat(route): add lang * feat: add cache.tryGet to Forward Future routes * fix: address review feedback - remove cache.tryGet from list requests, use JSON.parse for unescaping, fix description/pubDate/extraction --- lib/routes/forwardfuture/daily.ts | 58 ++++++++++++ lib/routes/forwardfuture/namespace.ts | 7 ++ lib/routes/forwardfuture/originals.ts | 122 ++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 lib/routes/forwardfuture/daily.ts create mode 100644 lib/routes/forwardfuture/namespace.ts create mode 100644 lib/routes/forwardfuture/originals.ts diff --git a/lib/routes/forwardfuture/daily.ts b/lib/routes/forwardfuture/daily.ts new file mode 100644 index 000000000000..a4449367d2c0 --- /dev/null +++ b/lib/routes/forwardfuture/daily.ts @@ -0,0 +1,58 @@ +import type { Context } from 'hono'; + +import type { Data, DataItem, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +interface NewsletterPost { + title: string; + summary: string; + url: string; + date: string; + thumbnail: string; + bodyPreview: string; + author: string; +} + +interface NewsletterResponse { + posts: NewsletterPost[]; +} + +export const route: Route = { + name: 'Daily Newsletter', + categories: ['other'], + path: '/daily', + example: '/forwardfuture/daily', + radar: [ + { + source: ['forwardfuture.com/newsletter/daily', 'forwardfuture.com/'], + }, + ], + handler, + maintainers: ['ovo-Tim'], + description: 'Daily AI newsletter from Forward Future.', +}; + +async function handler(ctx: Context): Promise<Data> { + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; + + const response = await ofetch<NewsletterResponse>('https://forwardfuture.com/api/newsletter'); + const posts = response.posts.slice(0, limit); + + const items: DataItem[] = posts.map((post) => ({ + title: post.title, + description: post.summary, + link: `https://forwardfuture.com${post.url}`, + pubDate: parseDate(post.date, 'MMM D, YYYY'), + author: post.author, + image: post.thumbnail, + })); + + return { + title: 'Forward Future - Daily Newsletter', + link: 'https://forwardfuture.com/newsletter/daily', + description: "The Future Today - Forward Future's free daily AI newsletter.", + item: items, + image: 'https://forwardfuture.com/images/logos/ff-icon.svg', + }; +} diff --git a/lib/routes/forwardfuture/namespace.ts b/lib/routes/forwardfuture/namespace.ts new file mode 100644 index 000000000000..42f3739337f8 --- /dev/null +++ b/lib/routes/forwardfuture/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Forward Future', + url: 'forwardfuture.com', + lang: 'en', +}; diff --git a/lib/routes/forwardfuture/originals.ts b/lib/routes/forwardfuture/originals.ts new file mode 100644 index 000000000000..0cb11ebc8e5b --- /dev/null +++ b/lib/routes/forwardfuture/originals.ts @@ -0,0 +1,122 @@ +import type { Context } from 'hono'; + +import type { Data, DataItem, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +interface OriginalPost { + id: string; + title: string; + summary: string; + url: string; + date: string; + dateUnix: number; + thumbnail: string; + authors: string[]; + category: string; + categories: string[]; +} + +function extractPostsArray(rscPayload: string): OriginalPost[] | null { + let searchStart = 0; + while (searchStart < rscPayload.length) { + const postsIdx = rscPayload.indexOf('"posts":', searchStart); + if (postsIdx === -1) { + return null; + } + + const arrayStart = postsIdx + '"posts":'.length; + if (rscPayload[arrayStart] !== '[') { + searchStart = arrayStart; + continue; + } + + let depth = 0; + let inString = false; + let escape = false; + let i = arrayStart; + for (; i < rscPayload.length; i++) { + const c = rscPayload[i]; + if (escape) { + escape = false; + } else if (c === '\\' && inString) { + escape = true; + } else if (c === '"' && !escape) { + inString = !inString; + } else if (!inString) { + if (c === '[') { + depth++; + } else if (c === ']') { + depth--; + if (depth === 0) { + return JSON.parse(rscPayload.slice(arrayStart, i + 1)) as OriginalPost[]; + } + } + } + } + + searchStart = arrayStart + 1; + } + return null; +} + +export const route: Route = { + name: 'Originals', + categories: ['other'], + path: '/originals', + example: '/forwardfuture/originals', + radar: [ + { + source: ['forwardfuture.com/originals', 'forwardfuture.com/'], + }, + ], + handler, + maintainers: ['ovo-Tim'], + description: 'Original essays, columns, and analysis on AI from Forward Future contributors.', +}; + +async function handler(ctx: Context): Promise<Data> { + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; + + const html = await ofetch<string>('https://forwardfuture.com/originals'); + + const pushRegex = /__next_f\.push\(\[1,"((?:[^"\\]|\\.)*)"\]\)/g; + let match: RegExpExecArray | null; + let posts: OriginalPost[] = []; + + while ((match = pushRegex.exec(html)) !== null) { + const payload = match[1]; + if (!payload.includes('posts')) { + continue; + } + + const decoded = JSON.parse(`"${payload}"`); + const extracted = extractPostsArray(decoded); + if (extracted) { + posts = extracted; + break; + } + } + + if (posts.length === 0) { + throw new Error('Failed to extract posts from the Next.js RSC payload'); + } + + const items: DataItem[] = posts.slice(0, limit).map((post) => ({ + title: post.title, + description: post.summary || undefined, + link: post.url, + pubDate: parseDate(post.dateUnix, 'X'), + author: post.authors.join(', '), + image: post.thumbnail, + category: post.categories.length > 0 ? post.categories : post.category === 'General' ? undefined : [post.category], + })); + + return { + title: 'Forward Future - Originals', + link: 'https://forwardfuture.com/originals', + description: 'Original essays, columns, and analysis on AI from Forward Future contributors.', + item: items, + image: 'https://forwardfuture.com/images/logos/ff-icon.svg', + }; +} From 1399af82966dacd64052335f15425b804d68e78f Mon Sep 17 00:00:00 2001 From: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Date: Sun, 26 Jul 2026 22:17:00 +0800 Subject: [PATCH 0425/1190] =?UTF-8?q?fix(route):=20=E8=81=94=E5=90=88?= =?UTF-8?q?=E8=B5=84=E4=BF=A1=E8=AF=84=E4=BC=B0=E8=82=A1=E4=BB=BD=E6=9C=89?= =?UTF-8?q?=E9=99=90=E5=85=AC=E5=8F=B8=E7=A0=94=E7=A9=B6=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=20(#22835)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/routes/lhratings/research.ts | 63 +++++++++++++++----------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/lib/routes/lhratings/research.ts b/lib/routes/lhratings/research.ts index ed0ad5a78882..df23675ebe72 100644 --- a/lib/routes/lhratings/research.ts +++ b/lib/routes/lhratings/research.ts @@ -9,29 +9,29 @@ import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; export const handler = async (ctx: Context): Promise<Data> => { - const { type = '1' } = ctx.req.param(); + const { type = '92' } = ctx.req.param(); const limit = Number(ctx.req.query('limit') ?? '20'); const baseUrl = 'https://www.lhratings.com'; - const targetUrl: string = new URL(`research.html?type=${type}`, baseUrl).href; + const targetUrl: string = new URL(`lists/${type}.html`, baseUrl).href; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); const language = $('html').attr('lang') ?? 'zh-CN'; - const items: DataItem[] = $('table.list-table tbody tr') + const items: DataItem[] = $('div.xlistNr ul li a') .slice(0, limit) .toArray() .map((el): Element => { const $el: Cheerio<Element> = $(el); const $aEl: Cheerio<Element> = $el.find('a').first(); - const title: string = $aEl.text(); - const pubDateStr: string | undefined = $aEl.parent().next().next().text(); - const linkUrl: string | undefined = $aEl.attr('href'); - const categoryEls: Element[] = [$aEl.parent().next()].filter(Boolean); + const title: string = $el.find('h2').text(); + const pubDateStr: string | undefined = $el.find('p').text().split(':', 2)[1]?.trim(); + const linkUrl: string | undefined = $aEl.attr('href') ? new URL($aEl.attr('href') ?? '', baseUrl).href : undefined; + const categoryEls: Array<Cheerio<Element>> = [$el.find('h3').contents()].filter(Boolean); const categories: string[] = [...new Set(categoryEls.map((el) => $(el).text()).filter(Boolean))]; - const image: string | undefined = $el.find('img').attr('src'); + const image: string | undefined = $el.find('div.xylist_img img').attr('src') ? new URL($el.find('div.xylist_img img').attr('src') ?? '', baseUrl).href : undefined; const upDatedStr: string | undefined = pubDateStr; let processedItem: DataItem = { @@ -59,15 +59,15 @@ export const handler = async (ctx: Context): Promise<Data> => { return processedItem; }); - const author: string = $('title').text(); + const author = '联合资信评估股份有限公司'; return { - title: `${author} - ${$('li.active').text()}`, + title: `${author} - ${$('title').text()}`, description: $('li.active').text(), link: targetUrl, item: items, allowEmpty: true, - image: $('a#logo img').attr('src'), + image: $('h1.logo a img').attr('src') ? new URL($('h1.logo a img').attr('src') ?? '', baseUrl).href : undefined, author, language, id: targetUrl, @@ -80,17 +80,17 @@ export const route: Route = { url: 'www.lhratings.com', maintainers: ['nczitzk'], handler, - example: '/lhratings/research/1', + example: '/lhratings/research/92', parameters: { - type: '分类,默认为 `1`,即宏观经济,可在对应分类页 URL 中找到', + type: '分类,默认为 `92`,即宏观经济,可在对应分类页 URL 中找到', }, description: `::: tip -若订阅 [宏观经济](https://www.lhratings.com/research.html?type=1),网址为 \`https://www.lhratings.com/research.html?type=1\`,请截取 \`https://www.lhratings.com/research.html?type=\` 到末尾的部分 \`1\` 作为 \`type\` 参数填入,此时目标路由为 [\`/lhratings/research/1\`](https://rsshub.app/lhratings/research/1)。 +若订阅 [宏观经济](https://www.lhratings.com/research.html?type=92),网址为 \`https://www.lhratings.com/research.html?type=92\`,请截取 \`https://www.lhratings.com/research.html?type=\` 到末尾的部分 \`92\` 作为 \`type\` 参数填入,此时目标路由为 [\`/lhratings/research/92\`](https://rsshub.app/lhratings/research/92)。 ::: -| 宏观经济 | 债券市场 | 行业研究 | 评级理论与方法 | 国际债券市场与评级 | 评级表现 | -| -------- | -------- | -------- | -------------- | ------------------ | -------- | -| 1 | 2 | 3 | 4 | 5 | 6 |`, +| 宏观经济 | 债券市场 | 行业研究 | 每日资讯 | 其他 | +| -------- | -------- | -------- | -------- | ---- | +| 92 | 93 | 94 | 95 | 96 |`, categories: ['finance'], features: { requireConfig: false, @@ -113,33 +113,28 @@ export const route: Route = { }, { title: '宏观经济', - source: ['www.lhratings.com/research.html?type=1'], - target: '/research/1', + source: ['www.lhratings.com/research.html?type=92'], + target: '/research/92', }, { title: '债券市场', - source: ['www.lhratings.com/research.html?type=2'], - target: '/research/2', + source: ['www.lhratings.com/research.html?type=93'], + target: '/research/93', }, { title: '行业研究', - source: ['www.lhratings.com/research.html?type=3'], - target: '/research/3', + source: ['www.lhratings.com/research.html?type=94'], + target: '/research/94', }, { - title: '评级理论与方法', - source: ['www.lhratings.com/research.html?type=4'], - target: '/research/4', + title: '每日资讯', + source: ['www.lhratings.com/research.html?type=95'], + target: '/research/95', }, { - title: '国际债券市场与评级', - source: ['www.lhratings.com/research.html?type=5'], - target: '/research/5', - }, - { - title: '评级表现', - source: ['www.lhratings.com/research.html?type=6'], - target: '/research/6', + title: '其他', + source: ['www.lhratings.com/research.html?type=96'], + target: '/research/96', }, ], view: ViewType.Articles, From 29147419b5d76932f72e8ec47c323e52bc507155 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:08:06 +0000 Subject: [PATCH 0426/1190] chore(deps): bump docker/login-action from 4.5.0 to 4.5.1 (#22839) Bumps [docker/login-action](https://github.com/docker/login-action) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/06fb636fac595d6fb4b28a5dfcb21a6f5091859c...abd2ef45e78c5afb21d64d4ca52ee8550d9572c7) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.5.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index aaf87ab448d5..acfec3b54d01 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -74,13 +74,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.actor }} @@ -207,13 +207,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.actor }} From e3501b6321d6516ae889756a02e8af9125aa36b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:19:04 +0000 Subject: [PATCH 0427/1190] chore(deps): bump @hono/node-server from 2.0.11 to 2.0.12 (#22842) Bumps [@hono/node-server](https://github.com/honojs/node-server) from 2.0.11 to 2.0.12. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](https://github.com/honojs/node-server/compare/v2.0.11...v2.0.12) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 2.0.12 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 230 ++++++++++++++++++++++++------------------------- 2 files changed, 116 insertions(+), 116 deletions(-) diff --git a/package.json b/package.json index fbab5587412a..be6c65edbccb 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.15.3", - "@hono/node-server": "2.0.11", + "@hono/node-server": "2.0.12", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", "@notionhq/client": "5.23.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 950c1e708b83..93ef36738247 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 6.15.3 version: 6.15.3 '@hono/node-server': - specifier: 2.0.11 - version: 2.0.11(hono@4.12.32) + specifier: 2.0.12 + version: 2.0.12(hono@4.12.32) '@hono/zod-openapi': specifier: 1.5.1 version: 1.5.1(hono@4.12.32)(zod@4.4.3) @@ -364,7 +364,7 @@ importers: version: 8.65.0(@typescript/typescript6@6.0.2)(eslint@10.8.0) '@vercel/nft': specifier: 1.10.2 - version: 1.10.2(rollup@4.62.2) + version: 1.10.2(rollup@4.62.3) '@vitest/coverage-v8': specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) @@ -1099,8 +1099,8 @@ packages: engines: {node: '>=14'} hasBin: true - '@hono/node-server@2.0.11': - resolution: {integrity: sha512-bjD221KPLoJTWUwso1J6fGKiTXEUFedG/s0visavY4zakFPkeGURMRNly+FhBHs7T8Dz4qHaZIMX9ZoJHSJtKA==} + '@hono/node-server@2.0.12': + resolution: {integrity: sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==} engines: {node: '>=20'} peerDependencies: hono: ^4 @@ -2420,141 +2420,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.62.2': - resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + '@rollup/rollup-android-arm-eabi@4.62.3': + resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.62.2': - resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + '@rollup/rollup-android-arm64@4.62.3': + resolution: {integrity: sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.62.2': - resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + '@rollup/rollup-darwin-arm64@4.62.3': + resolution: {integrity: sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.2': - resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + '@rollup/rollup-darwin-x64@4.62.3': + resolution: {integrity: sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.62.2': - resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + '@rollup/rollup-freebsd-arm64@4.62.3': + resolution: {integrity: sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.2': - resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + '@rollup/rollup-freebsd-x64@4.62.3': + resolution: {integrity: sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': + resolution: {integrity: sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.3': + resolution: {integrity: sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.62.2': - resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + '@rollup/rollup-linux-arm64-gnu@4.62.3': + resolution: {integrity: sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.62.2': - resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + '@rollup/rollup-linux-arm64-musl@4.62.3': + resolution: {integrity: sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.62.2': - resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + '@rollup/rollup-linux-loong64-gnu@4.62.3': + resolution: {integrity: sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.62.2': - resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + '@rollup/rollup-linux-loong64-musl@4.62.3': + resolution: {integrity: sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + '@rollup/rollup-linux-ppc64-gnu@4.62.3': + resolution: {integrity: sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.62.2': - resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + '@rollup/rollup-linux-ppc64-musl@4.62.3': + resolution: {integrity: sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.3': + resolution: {integrity: sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.62.2': - resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + '@rollup/rollup-linux-riscv64-musl@4.62.3': + resolution: {integrity: sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.62.2': - resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + '@rollup/rollup-linux-s390x-gnu@4.62.3': + resolution: {integrity: sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.2': - resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + '@rollup/rollup-linux-x64-gnu@4.62.3': + resolution: {integrity: sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.62.2': - resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + '@rollup/rollup-linux-x64-musl@4.62.3': + resolution: {integrity: sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.62.2': - resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + '@rollup/rollup-openbsd-x64@4.62.3': + resolution: {integrity: sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.62.2': - resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + '@rollup/rollup-openharmony-arm64@4.62.3': + resolution: {integrity: sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.62.2': - resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + '@rollup/rollup-win32-arm64-msvc@4.62.3': + resolution: {integrity: sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.2': - resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + '@rollup/rollup-win32-ia32-msvc@4.62.3': + resolution: {integrity: sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.2': - resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + '@rollup/rollup-win32-x64-gnu@4.62.3': + resolution: {integrity: sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.2': - resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + '@rollup/rollup-win32-x64-msvc@4.62.3': + resolution: {integrity: sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==} cpu: [x64] os: [win32] @@ -3704,7 +3704,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.51: @@ -5608,8 +5608,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.62.2: - resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + rollup@4.62.3: + resolution: {integrity: sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6999,7 +6999,7 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.0.11(hono@4.12.32)': + '@hono/node-server@2.0.12(hono@4.12.32)': dependencies: hono: 4.12.32 @@ -7930,87 +7930,87 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + '@rollup/pluginutils@5.4.0(rollup@4.62.3)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.5 optionalDependencies: - rollup: 4.62.2 + rollup: 4.62.3 - '@rollup/rollup-android-arm-eabi@4.62.2': + '@rollup/rollup-android-arm-eabi@4.62.3': optional: true - '@rollup/rollup-android-arm64@4.62.2': + '@rollup/rollup-android-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-arm64@4.62.2': + '@rollup/rollup-darwin-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-x64@4.62.2': + '@rollup/rollup-darwin-x64@4.62.3': optional: true - '@rollup/rollup-freebsd-arm64@4.62.2': + '@rollup/rollup-freebsd-arm64@4.62.3': optional: true - '@rollup/rollup-freebsd-x64@4.62.2': + '@rollup/rollup-freebsd-x64@4.62.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.2': + '@rollup/rollup-linux-arm-musleabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.2': + '@rollup/rollup-linux-arm64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.2': + '@rollup/rollup-linux-arm64-musl@4.62.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.2': + '@rollup/rollup-linux-loong64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.2': + '@rollup/rollup-linux-loong64-musl@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.2': + '@rollup/rollup-linux-ppc64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.2': + '@rollup/rollup-linux-ppc64-musl@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.2': + '@rollup/rollup-linux-riscv64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.2': + '@rollup/rollup-linux-riscv64-musl@4.62.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.2': + '@rollup/rollup-linux-s390x-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.2': + '@rollup/rollup-linux-x64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-musl@4.62.2': + '@rollup/rollup-linux-x64-musl@4.62.3': optional: true - '@rollup/rollup-openbsd-x64@4.62.2': + '@rollup/rollup-openbsd-x64@4.62.3': optional: true - '@rollup/rollup-openharmony-arm64@4.62.2': + '@rollup/rollup-openharmony-arm64@4.62.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.2': + '@rollup/rollup-win32-arm64-msvc@4.62.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.2': + '@rollup/rollup-win32-ia32-msvc@4.62.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.2': + '@rollup/rollup-win32-x64-gnu@4.62.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.2': + '@rollup/rollup-win32-x64-msvc@4.62.3': optional: true '@rss3/api-core@0.0.25': @@ -8459,10 +8459,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.10.2(rollup@4.62.2)': + '@vercel/nft@1.10.2(rollup@4.62.3)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + '@rollup/pluginutils': 5.4.0(rollup@4.62.3) acorn: 8.17.0 acorn-import-attributes: 1.9.5(acorn@8.17.0) async-sema: 3.1.1 @@ -11421,35 +11421,35 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.0 '@rolldown/binding-win32-x64-msvc': 1.2.0 - rollup@4.62.2: + rollup@4.62.3: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.2 - '@rollup/rollup-android-arm64': 4.62.2 - '@rollup/rollup-darwin-arm64': 4.62.2 - '@rollup/rollup-darwin-x64': 4.62.2 - '@rollup/rollup-freebsd-arm64': 4.62.2 - '@rollup/rollup-freebsd-x64': 4.62.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 - '@rollup/rollup-linux-arm-musleabihf': 4.62.2 - '@rollup/rollup-linux-arm64-gnu': 4.62.2 - '@rollup/rollup-linux-arm64-musl': 4.62.2 - '@rollup/rollup-linux-loong64-gnu': 4.62.2 - '@rollup/rollup-linux-loong64-musl': 4.62.2 - '@rollup/rollup-linux-ppc64-gnu': 4.62.2 - '@rollup/rollup-linux-ppc64-musl': 4.62.2 - '@rollup/rollup-linux-riscv64-gnu': 4.62.2 - '@rollup/rollup-linux-riscv64-musl': 4.62.2 - '@rollup/rollup-linux-s390x-gnu': 4.62.2 - '@rollup/rollup-linux-x64-gnu': 4.62.2 - '@rollup/rollup-linux-x64-musl': 4.62.2 - '@rollup/rollup-openbsd-x64': 4.62.2 - '@rollup/rollup-openharmony-arm64': 4.62.2 - '@rollup/rollup-win32-arm64-msvc': 4.62.2 - '@rollup/rollup-win32-ia32-msvc': 4.62.2 - '@rollup/rollup-win32-x64-gnu': 4.62.2 - '@rollup/rollup-win32-x64-msvc': 4.62.2 + '@rollup/rollup-android-arm-eabi': 4.62.3 + '@rollup/rollup-android-arm64': 4.62.3 + '@rollup/rollup-darwin-arm64': 4.62.3 + '@rollup/rollup-darwin-x64': 4.62.3 + '@rollup/rollup-freebsd-arm64': 4.62.3 + '@rollup/rollup-freebsd-x64': 4.62.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.3 + '@rollup/rollup-linux-arm-musleabihf': 4.62.3 + '@rollup/rollup-linux-arm64-gnu': 4.62.3 + '@rollup/rollup-linux-arm64-musl': 4.62.3 + '@rollup/rollup-linux-loong64-gnu': 4.62.3 + '@rollup/rollup-linux-loong64-musl': 4.62.3 + '@rollup/rollup-linux-ppc64-gnu': 4.62.3 + '@rollup/rollup-linux-ppc64-musl': 4.62.3 + '@rollup/rollup-linux-riscv64-gnu': 4.62.3 + '@rollup/rollup-linux-riscv64-musl': 4.62.3 + '@rollup/rollup-linux-s390x-gnu': 4.62.3 + '@rollup/rollup-linux-x64-gnu': 4.62.3 + '@rollup/rollup-linux-x64-musl': 4.62.3 + '@rollup/rollup-openbsd-x64': 4.62.3 + '@rollup/rollup-openharmony-arm64': 4.62.3 + '@rollup/rollup-win32-arm64-msvc': 4.62.3 + '@rollup/rollup-win32-ia32-msvc': 4.62.3 + '@rollup/rollup-win32-x64-gnu': 4.62.3 + '@rollup/rollup-win32-x64-msvc': 4.62.3 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -12059,7 +12059,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 postcss: 8.5.23 - rollup: 4.62.2 + rollup: 4.62.3 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.1.1 From 4814bf513b3d0f30d49ea6ab119b0b7b91d01630 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:56:55 +0800 Subject: [PATCH 0428/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22840) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260724.1 to 5.20260727.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260727.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index be6c65edbccb..58a285d609da 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.8", - "@cloudflare/workers-types": "5.20260724.1", + "@cloudflare/workers-types": "5.20260727.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.75.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93ef36738247..9702728663dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.0 '@cloudflare/vitest-pool-workers': specifier: 0.18.8 - version: 0.18.8(@cloudflare/workers-types@5.20260724.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.8(@cloudflare/workers-types@5.20260727.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260724.1 - version: 5.20260724.1 + specifier: 5.20260727.1 + version: 5.20260727.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.114.0 - version: 4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -641,8 +641,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260724.1': - resolution: {integrity: sha512-gl0brZ60JhkZU3INgr1jsxaFEiWf3AB9RsCjLTMwT5RKspWRUpsFd0wxwbys7gb8Ywkfq9tORhw0y9G+7bDUYw==} + '@cloudflare/workers-types@5.20260727.1': + resolution: {integrity: sha512-b/wT+LMZz0oELzxibww0ujFz5BD8NRz9WJ+xd+JNZJUMXgh8IHjpibKdGDvtkbotmihWUknP5tBPUU8KluLxxA==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -6665,7 +6665,7 @@ snapshots: optionalDependencies: workerd: 1.20260722.1 - '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260724.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260727.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6673,7 +6673,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6695,7 +6695,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260722.1': optional: true - '@cloudflare/workers-types@5.20260724.1': {} + '@cloudflare/workers-types@5.20260727.1': {} '@colors/colors@1.6.0': {} @@ -12181,7 +12181,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260722.1 '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.114.0(@cloudflare/workers-types@5.20260724.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) @@ -12192,7 +12192,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260722.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260724.1 + '@cloudflare/workers-types': 5.20260727.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 9892409cbf30b587a4e4f9106b54f080a0331165 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:59:28 +0800 Subject: [PATCH 0429/1190] chore(deps-dev): bump @bbob/types from 4.3.1 to 4.4.0 (#22841) Bumps [@bbob/types](https://github.com/JiLiZART/bbob) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/types@4.3.1...@bbob/types@4.4.0) --- updated-dependencies: - dependency-name: "@bbob/types" dependency-version: 4.4.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 58a285d609da..99bd670fd390 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "devDependencies": { "@actions/core": "3.0.1", "@actions/github": "9.1.1", - "@bbob/types": "4.3.1", + "@bbob/types": "4.4.0", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9702728663dc..263d132e36f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -285,8 +285,8 @@ importers: specifier: 9.1.1 version: 9.1.1 '@bbob/types': - specifier: 4.3.1 - version: 4.3.1 + specifier: 4.4.0 + version: 4.4.0 '@cloudflare/containers': specifier: 0.3.7 version: 0.3.7 @@ -571,8 +571,8 @@ packages: '@bbob/preset@4.3.1': resolution: {integrity: sha512-we6ITgrwciNIdpZRRJl4y3OOEbXEEVA21895JUInvYXevZBrIgIAvO502qlEYQtqhJqQ1HxY5xyhDjZ9rEYQHg==} - '@bbob/types@4.3.1': - resolution: {integrity: sha512-TXxFPfKvZVH/sR0mfn5I99bEFmbnAzIoqFBxG1htRiKyJUx7Yb/ZNS/OsQD1mIsR6+srDqXFDaWbDgVudTTjvQ==} + '@bbob/types@4.4.0': + resolution: {integrity: sha512-d79ov/IQFW5gEAllrK48xqI/IDs6y31F4gXRUX3d7KYN+r6EaRsChjMgv0wPppZovUL2/eECFOe82sqAa0HF7g==} '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} @@ -6615,35 +6615,35 @@ snapshots: dependencies: '@bbob/parser': 4.3.1 '@bbob/plugin-helper': 4.3.1 - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 '@bbob/html@4.3.1': dependencies: '@bbob/core': 4.3.1 '@bbob/plugin-helper': 4.3.1 - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 '@bbob/parser@4.3.1': dependencies: '@bbob/plugin-helper': 4.3.1 - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 '@bbob/plugin-helper@4.3.1': dependencies: - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 '@bbob/preset-html5@4.3.1': dependencies: '@bbob/plugin-helper': 4.3.1 '@bbob/preset': 4.3.1 - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 '@bbob/preset@4.3.1': dependencies: '@bbob/plugin-helper': 4.3.1 - '@bbob/types': 4.3.1 + '@bbob/types': 4.4.0 - '@bbob/types@4.3.1': {} + '@bbob/types@4.4.0': {} '@bcoe/v8-coverage@1.0.2': {} From e586a4bb5b52d0c2c5ed2b879684769c933a6150 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:19:49 +0800 Subject: [PATCH 0430/1190] chore(deps-dev): bump globals from 17.7.0 to 17.8.0 (#22845) Bumps [globals](https://github.com/sindresorhus/globals) from 17.7.0 to 17.8.0. - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.7.0...v17.8.0) --- updated-dependencies: - dependency-name: globals dependency-version: 17.8.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 99bd670fd390..1a0b53466410 100644 --- a/package.json +++ b/package.json @@ -184,7 +184,7 @@ "eslint-plugin-yml": "3.6.0", "fast-string-width": "3.0.2", "fs-extra": "11.4.0", - "globals": "17.7.0", + "globals": "17.8.0", "husky": "9.1.7", "js-beautify": "2.0.3", "lint-staged": "17.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 263d132e36f6..038972b887ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -402,8 +402,8 @@ importers: specifier: 11.4.0 version: 11.4.0 globals: - specifier: 17.7.0 - version: 17.7.0 + specifier: 17.8.0 + version: 17.8.0 husky: specifier: 9.1.7 version: 9.1.7 @@ -4203,8 +4203,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@17.7.0: - resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + globals@17.8.0: + resolution: {integrity: sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==} engines: {node: '>=18'} globrex@0.1.2: @@ -9347,7 +9347,7 @@ snapshots: entities: 4.5.0 eslint: 10.8.0 find-up-simple: 1.0.1 - globals: 17.7.0 + globals: 17.8.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 is-identifier: 1.1.0 @@ -9668,7 +9668,7 @@ snapshots: globals@15.15.0: {} - globals@17.7.0: {} + globals@17.8.0: {} globrex@0.1.2: {} From bcd9db638326b239fc9edc5de6cc09e562f8581b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:20:45 +0800 Subject: [PATCH 0431/1190] chore(deps): bump @bbob/plugin-helper from 4.3.1 to 4.4.0 (#22846) Bumps [@bbob/plugin-helper](https://github.com/JiLiZART/bbob) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/plugin-helper@4.3.1...@bbob/plugin-helper@4.4.0) --- updated-dependencies: - dependency-name: "@bbob/plugin-helper" dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 1a0b53466410..07a62b90ec7d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ }, "dependencies": { "@bbob/html": "4.3.1", - "@bbob/plugin-helper": "4.3.1", + "@bbob/plugin-helper": "4.4.0", "@bbob/preset-html5": "4.3.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.15.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 038972b887ae..ecfe3d97b44a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,8 +35,8 @@ importers: specifier: 4.3.1 version: 4.3.1 '@bbob/plugin-helper': - specifier: 4.3.1 - version: 4.3.1 + specifier: 4.4.0 + version: 4.4.0 '@bbob/preset-html5': specifier: 4.3.1 version: 4.3.1 @@ -562,8 +562,8 @@ packages: '@bbob/parser@4.3.1': resolution: {integrity: sha512-BqDo8PDLcVLhFvOMdLmeaWk5qqD8E1bEdvzSJQuJC/U1XFs1tMC4lwZ4oPBFmZCeCLNsYFawqjP/S5MDdr7/Tg==} - '@bbob/plugin-helper@4.3.1': - resolution: {integrity: sha512-0gyhW5sU270iCBvaRO1jYDTs1oGHu913yB9tqxPO9zT5OIDhu47PwensdJ7EuZwgYKEyQ4zZZ+1jvDzkubjdyQ==} + '@bbob/plugin-helper@4.4.0': + resolution: {integrity: sha512-1X9El64z/g+4vMKx+CnLc2O1No/KqRRU4Ku1PLYN1CD6OmJk/T1sQ3dlLAzSjV6gzZ2fSa8/endhZpf2ULKBLA==} '@bbob/preset-html5@4.3.1': resolution: {integrity: sha512-6Yn8kOrBWt/2u4ZUt7nSTzWP+xvTaH4TlAds6p+BcBRbs4Ov2f1zkRgCh1JlckMXwhH9q9CGF0wn6w8bkUYcFA==} @@ -6614,33 +6614,33 @@ snapshots: '@bbob/core@4.3.1': dependencies: '@bbob/parser': 4.3.1 - '@bbob/plugin-helper': 4.3.1 + '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 '@bbob/html@4.3.1': dependencies: '@bbob/core': 4.3.1 - '@bbob/plugin-helper': 4.3.1 + '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 '@bbob/parser@4.3.1': dependencies: - '@bbob/plugin-helper': 4.3.1 + '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 - '@bbob/plugin-helper@4.3.1': + '@bbob/plugin-helper@4.4.0': dependencies: '@bbob/types': 4.4.0 '@bbob/preset-html5@4.3.1': dependencies: - '@bbob/plugin-helper': 4.3.1 + '@bbob/plugin-helper': 4.4.0 '@bbob/preset': 4.3.1 '@bbob/types': 4.4.0 '@bbob/preset@4.3.1': dependencies: - '@bbob/plugin-helper': 4.3.1 + '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 '@bbob/types@4.4.0': {} From 303ee883cfbc80c30a04765217094471c38f97e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:23:28 +0800 Subject: [PATCH 0432/1190] chore(deps): bump nixpkgs from `e2587ca` to `624af66` (#22848) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `e2587ca` to `624af66`. - [Commits](https://github.com/NixOS/nixpkgs/compare/e2587caef70cea85dd97d7daab492899902dbf5d...624af665418d3c65d544145b4d34ad696439570e) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: 624af665418d3c65d544145b4d34ad696439570e dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 63e4df494a8d..55f39994af33 100644 --- a/flake.lock +++ b/flake.lock @@ -277,11 +277,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1784796856, - "narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=", + "lastModified": 1785090369, + "narHash": "sha256-m0pDuRJG7EDo9ri+4Ksu83VsI+PlxNC9lNBfydejce4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e2587caef70cea85dd97d7daab492899902dbf5d", + "rev": "624af665418d3c65d544145b4d34ad696439570e", "type": "github" }, "original": { From 70e97b7406f86340ca8de7568bfbcbecda64cf75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:27:44 +0800 Subject: [PATCH 0433/1190] chore(deps): bump @bbob/preset-html5 from 4.3.1 to 4.4.0 (#22847) Bumps [@bbob/preset-html5](https://github.com/JiLiZART/bbob) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/preset-html5@4.3.1...@bbob/preset-html5@4.4.0) --- updated-dependencies: - dependency-name: "@bbob/preset-html5" dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 07a62b90ec7d..4af13aa4acb5 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "dependencies": { "@bbob/html": "4.3.1", "@bbob/plugin-helper": "4.4.0", - "@bbob/preset-html5": "4.3.1", + "@bbob/preset-html5": "4.4.0", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.15.3", "@hono/node-server": "2.0.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecfe3d97b44a..bcc17cfa9d9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,8 +38,8 @@ importers: specifier: 4.4.0 version: 4.4.0 '@bbob/preset-html5': - specifier: 4.3.1 - version: 4.3.1 + specifier: 4.4.0 + version: 4.4.0 '@googleapis/youtube': specifier: 33.0.0 version: 33.0.0 @@ -565,11 +565,11 @@ packages: '@bbob/plugin-helper@4.4.0': resolution: {integrity: sha512-1X9El64z/g+4vMKx+CnLc2O1No/KqRRU4Ku1PLYN1CD6OmJk/T1sQ3dlLAzSjV6gzZ2fSa8/endhZpf2ULKBLA==} - '@bbob/preset-html5@4.3.1': - resolution: {integrity: sha512-6Yn8kOrBWt/2u4ZUt7nSTzWP+xvTaH4TlAds6p+BcBRbs4Ov2f1zkRgCh1JlckMXwhH9q9CGF0wn6w8bkUYcFA==} + '@bbob/preset-html5@4.4.0': + resolution: {integrity: sha512-hvYpHCRxo2lhfGXgI8TT4BWpVCJM5Bxtgc2XL1Pr77lmTb6yTVG+nNNuNxBJ3Mz3K0XPzaOhlS1YkTLpf1vYfA==} - '@bbob/preset@4.3.1': - resolution: {integrity: sha512-we6ITgrwciNIdpZRRJl4y3OOEbXEEVA21895JUInvYXevZBrIgIAvO502qlEYQtqhJqQ1HxY5xyhDjZ9rEYQHg==} + '@bbob/preset@4.4.0': + resolution: {integrity: sha512-N0k1pS9ywv6qbOiLAoM6RH1yOJtIHDSM7rHWmtfe+FyWjHnP7Icyn4McLIra2ZHlutJIVY+05UGAf375ByRzGw==} '@bbob/types@4.4.0': resolution: {integrity: sha512-d79ov/IQFW5gEAllrK48xqI/IDs6y31F4gXRUX3d7KYN+r6EaRsChjMgv0wPppZovUL2/eECFOe82sqAa0HF7g==} @@ -6632,13 +6632,13 @@ snapshots: dependencies: '@bbob/types': 4.4.0 - '@bbob/preset-html5@4.3.1': + '@bbob/preset-html5@4.4.0': dependencies: '@bbob/plugin-helper': 4.4.0 - '@bbob/preset': 4.3.1 + '@bbob/preset': 4.4.0 '@bbob/types': 4.4.0 - '@bbob/preset@4.3.1': + '@bbob/preset@4.4.0': dependencies: '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 From eb219a526541ca7a1a48724cb9ca2eda128a36db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:35:35 +0800 Subject: [PATCH 0434/1190] chore(deps): bump @bbob/html from 4.3.1 to 4.4.0 (#22843) Bumps [@bbob/html](https://github.com/JiLiZART/bbob) from 4.3.1 to 4.4.0. - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/html@4.3.1...@bbob/html@4.4.0) --- updated-dependencies: - dependency-name: "@bbob/html" dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 4af13aa4acb5..bab0e1e6a6f1 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "worker-test": "npm run worker-build && vitest run lib/worker.test.ts" }, "dependencies": { - "@bbob/html": "4.3.1", + "@bbob/html": "4.4.0", "@bbob/plugin-helper": "4.4.0", "@bbob/preset-html5": "4.4.0", "@googleapis/youtube": "33.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bcc17cfa9d9a..042c106aadfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,8 +32,8 @@ importers: .: dependencies: '@bbob/html': - specifier: 4.3.1 - version: 4.3.1 + specifier: 4.4.0 + version: 4.4.0 '@bbob/plugin-helper': specifier: 4.4.0 version: 4.4.0 @@ -553,14 +553,14 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@bbob/core@4.3.1': - resolution: {integrity: sha512-qNS5wNY2oJROV5CQ2e29e9dBz4ZuSO7JcciBXM2yDHvKFUYl+J4o8DxOsugce6z2cFpGz6F4J7vJN7X+6ujWiQ==} + '@bbob/core@4.4.0': + resolution: {integrity: sha512-4rLEjFsbMECLsUsz8SCwP/W2LomPAT1Pj5KBfIF3RDBdQAaU0nSLOauIDhnU17zxUZBlvXsncZmZTWTFNqXCNQ==} - '@bbob/html@4.3.1': - resolution: {integrity: sha512-W1qFvBZwhpGkZBNfeRpUFsoF492BlfNmgdh5iczg7B/PDULdOPH5lPLtT/6ZNpyx/xo6/DBssl9YtEO/bm15hw==} + '@bbob/html@4.4.0': + resolution: {integrity: sha512-cPNPMOq5akCexag3XerJLQ7A9TGdUdPPACzlZniMj2JGGgBsgGq9kIHm+6huQswvveQDd39dIkKUlcFpK1hQZg==} - '@bbob/parser@4.3.1': - resolution: {integrity: sha512-BqDo8PDLcVLhFvOMdLmeaWk5qqD8E1bEdvzSJQuJC/U1XFs1tMC4lwZ4oPBFmZCeCLNsYFawqjP/S5MDdr7/Tg==} + '@bbob/parser@4.4.0': + resolution: {integrity: sha512-5w0VnGCSY3JYGs1m4AfR5a3soBoc8HB3EUCYneXQqP1KPUvFuBKDc5XWGQ5EeXl6QuM03oS39HO19fhY9GgzXA==} '@bbob/plugin-helper@4.4.0': resolution: {integrity: sha512-1X9El64z/g+4vMKx+CnLc2O1No/KqRRU4Ku1PLYN1CD6OmJk/T1sQ3dlLAzSjV6gzZ2fSa8/endhZpf2ULKBLA==} @@ -6611,19 +6611,19 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@bbob/core@4.3.1': + '@bbob/core@4.4.0': dependencies: - '@bbob/parser': 4.3.1 + '@bbob/parser': 4.4.0 '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 - '@bbob/html@4.3.1': + '@bbob/html@4.4.0': dependencies: - '@bbob/core': 4.3.1 + '@bbob/core': 4.4.0 '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 - '@bbob/parser@4.3.1': + '@bbob/parser@4.4.0': dependencies: '@bbob/plugin-helper': 4.4.0 '@bbob/types': 4.4.0 From f79f710712dc50179055d6d7c31fd01f808547a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:38:03 +0800 Subject: [PATCH 0435/1190] chore(deps): bump devenv from `fc06618` to `6319d63` (#22849) Bumps [devenv](https://github.com/cachix/devenv) from `fc06618` to `6319d63`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/fc06618ba2099eaec355764f3ec5c395d45cec7c...6319d63344675f65c2c213be95084544866d8951) --- updated-dependencies: - dependency-name: devenv dependency-version: 6319d63344675f65c2c213be95084544866d8951 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 55f39994af33..556fea0436dd 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1784849577, - "narHash": "sha256-vVJl1B6vuVkpmkGYxSaHHpjwCZCP59OsXlSo4bQontk=", + "lastModified": 1785127153, + "narHash": "sha256-vX6OVIUWiEiD5v1InHQk1OTjJsK2WgEqWmMPeIS5l78=", "owner": "cachix", "repo": "devenv", - "rev": "fc06618ba2099eaec355764f3ec5c395d45cec7c", + "rev": "6319d63344675f65c2c213be95084544866d8951", "type": "github" }, "original": { From 14e552cab6b7e8851158adce41e68cf9dc6b6ce0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:12:24 +0800 Subject: [PATCH 0436/1190] chore(deps): bump jsdom from 29.1.1 to 30.0.0 (#22844) * chore(deps): bump jsdom from 29.1.1 to 30.0.0 Bumps [jsdom](https://github.com/jsdom/jsdom) from 29.1.1 to 30.0.0. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](https://github.com/jsdom/jsdom/compare/v29.1.1...v30.0.0) --- updated-dependencies: - dependency-name: jsdom dependency-version: 30.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: bump engine range --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 102 ++++++++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index bab0e1e6a6f1..7a6a11266dd5 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", - "jsdom": "29.1.1", + "jsdom": "30.0.0", "json-bigint": "1.0.0", "jsonpath-plus": "10.4.0", "jsrsasign": "11.1.3", @@ -226,7 +226,7 @@ ] }, "engines": { - "node": "^22.20.0 || ^24" + "node": "^22.22.2 || ^24.15.0" }, "packageManager": "pnpm@10.34.5+sha512.a4ee05f2f73658255bd6a89859c065a45c28a57daefae2c893a168ee2b73168c37b91e83e57ea67654ad03f03031746430e8bce38e362e042605fb8abc80192e", "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 042c106aadfc..b5329c40c576 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,8 +155,8 @@ importers: specifier: 5.0.0 version: 5.0.0 jsdom: - specifier: 29.1.1 - version: 29.1.1(@noble/hashes@2.2.0) + specifier: 30.0.0 + version: 30.0.0(@noble/hashes@2.2.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -469,7 +469,7 @@ importers: version: 7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.114.0 version: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -508,20 +508,13 @@ packages: '@apm-js-collab/tracing-hooks@0.13.0': resolution: {integrity: sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==} - '@asamuzakjp/css-color@5.1.11': - resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@asamuzakjp/dom-selector@7.1.1': - resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@asamuzakjp/generational-cache@1.0.1': - resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@asamuzakjp/css-color@6.0.5': + resolution: {integrity: sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==} + engines: {node: ^22.13.0 || >=24.0.0} - '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@asamuzakjp/dom-selector@8.3.0': + resolution: {integrity: sha512-UJLfKXBhrc8i1vH2eJXuYQMwlsLKWFw3O+CPqXSuVEiikeAim3UgrfWX0k4tA/X8cRFM8iZ7OaqBokFGbYusdg==} + engines: {node: ^22.13.0 || >=24.0.0} '@asteasolutions/zod-to-openapi@8.5.0': resolution: {integrity: sha512-SABbKiObg5dLRiTFnqiW1WWwGcg1BJfmHtT2asIBnBHg6Smy/Ms2KHc650+JI4Hw7lSkdiNebEGXpwoxfben8Q==} @@ -659,15 +652,15 @@ packages: resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.2.1': - resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + '@csstools/css-calc@3.3.0': + resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.9': - resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==} + '@csstools/css-color-parser@4.1.10': + resolution: {integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -679,8 +672,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.6': - resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==} + '@csstools/css-syntax-patches-for-csstree@1.1.7': + resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -4562,11 +4555,11 @@ packages: resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==} engines: {node: '>=20.0.0'} - jsdom@29.1.1: - resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + jsdom@30.0.0: + resolution: {integrity: sha512-JQHfRGmmKmaZoUAvIgff5jjG/0SzTQlGz8c7t72KzBzo8ZULEjAjnYE0sNwBOUA4QtWwYE2xoYitg8NFsmiYxA==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: - canvas: ^3.0.0 + canvas: ^3.2.3 peerDependenciesMeta: canvas: optional: true @@ -6338,6 +6331,10 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + whatwg-url@17.1.0: + resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==} + engines: {node: ^22.14.0 || >=24.0.0} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -6563,25 +6560,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@asamuzakjp/css-color@5.1.11': + '@asamuzakjp/css-color@6.0.5': dependencies: - '@asamuzakjp/generational-cache': 1.0.1 - '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + lru-cache: 11.5.2 - '@asamuzakjp/dom-selector@7.1.1': + '@asamuzakjp/dom-selector@8.3.0': dependencies: - '@asamuzakjp/generational-cache': 1.0.1 - '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - - '@asamuzakjp/generational-cache@1.0.1': {} - - '@asamuzakjp/nwsapi@2.3.9': {} + lru-cache: 11.5.2 '@asteasolutions/zod-to-openapi@8.5.0(zod@4.4.3)': dependencies: @@ -6672,7 +6664,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -6707,15 +6699,15 @@ snapshots: '@csstools/color-helpers@6.1.0': {} - '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.1.0 - '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -6723,7 +6715,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -8490,7 +8482,7 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -10065,12 +10057,12 @@ snapshots: jsdoc-type-pratt-parser@7.2.0: {} - jsdom@29.1.1(@noble/hashes@2.2.0): + jsdom@30.0.0(@noble/hashes@2.2.0): dependencies: - '@asamuzakjp/css-color': 5.1.11 - '@asamuzakjp/dom-selector': 7.1.1 + '@asamuzakjp/css-color': 6.0.5 + '@asamuzakjp/dom-selector': 8.3.0 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.7(css-tree@3.2.1) '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.2.0) @@ -10082,11 +10074,11 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.2 - undici: 7.29.0 + undici: 8.9.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.1(@noble/hashes@2.2.0) + whatwg-url: 17.1.0(@noble/hashes@2.2.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -12068,7 +12060,7 @@ snapshots: tsx: 4.23.1 yaml: 2.9.0 - vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) @@ -12094,7 +12086,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@types/node': 26.1.1 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) - jsdom: 29.1.1(@noble/hashes@2.2.0) + jsdom: 30.0.0(@noble/hashes@2.2.0) transitivePeerDependencies: - msw @@ -12137,6 +12129,14 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + whatwg-url@17.1.0(@noble/hashes@2.2.0): + dependencies: + '@exodus/bytes': 1.15.1(@noble/hashes@2.2.0) + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 From 5527d18de9605e5df9112f40904596e6ae5b971e Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:23:57 +0800 Subject: [PATCH 0437/1190] refactor: remove unnecessary trim/first/last (#22837) * refactor: remove unnecessary trim/first/last * refactor(types): allow null in DataItem.description * refactor: remove unnecessary fallback * refactor(types): allow null in Data.description * refactor: remove unnecessary fallback * refactor: remove .clone() * refactor: refine json content text * fIx: cognition * fix: more fixes * fix(route/jiemian): use api * fix(route/jandan): housekeeping add back maintainers from #8904 * fix(route/kcna/news): update source and target mappings * fix(route/jandan): sanitize HTML content in titles * refactor: remove unnecessary referer * fix(route/jinse): regex --- lib/routes/0x80/index.ts | 2 +- lib/routes/163/music/artist-songs.ts | 3 - lib/routes/163/music/artist.ts | 6 +- lib/routes/163/music/djradio.tsx | 3 - lib/routes/163/music/playlist.ts | 4 - lib/routes/163/music/userevents.tsx | 6 +- lib/routes/19lou/index.ts | 2 +- lib/routes/2023game/index.ts | 4 +- lib/routes/30secondsofcode/utils.ts | 10 +- lib/routes/3dmgame/news-center.ts | 4 +- lib/routes/4khd/article.ts | 2 +- lib/routes/4kup/article.ts | 2 +- lib/routes/51read/article.ts | 2 +- lib/routes/6v123/index.ts | 2 +- lib/routes/a9vg/index.ts | 3 +- lib/routes/aflcio/blog.ts | 4 +- lib/routes/agirls/topic-list.ts | 6 +- lib/routes/agirls/topic.ts | 4 +- lib/routes/agirls/utils.ts | 2 +- lib/routes/agirls/z-index.ts | 4 +- lib/routes/ai-bot/daily-ai-news.ts | 4 +- lib/routes/aiaa/journal.ts | 6 +- lib/routes/aiblog-2xv/archives.ts | 14 +- lib/routes/aisixiang/thinktank.ts | 2 +- lib/routes/aisixiang/toplist.ts | 2 +- lib/routes/aisixiang/zhuanti.ts | 2 +- lib/routes/alicesoft/infomation.ts | 7 +- lib/routes/aliyun/database-month.ts | 2 +- lib/routes/alwayscontrol/news.ts | 79 +++--- lib/routes/anime1/anime.ts | 4 +- lib/routes/anime1/search.ts | 2 +- lib/routes/anthropic/engineering.ts | 6 +- lib/routes/anthropic/news.ts | 2 +- lib/routes/anytxt/release-notes.ts | 2 +- lib/routes/apple/design.ts | 2 +- lib/routes/aqara/news.ts | 2 +- lib/routes/asiantolick/index.ts | 2 +- lib/routes/bandisoft/history.ts | 2 +- lib/routes/bangumi.tv/group/reply.ts | 4 +- lib/routes/bangumi.tv/other/followrank.ts | 2 +- lib/routes/baoyu/index.ts | 2 +- lib/routes/bbc/learningenglish.ts | 2 +- lib/routes/bilibili/dynamic.ts | 8 +- lib/routes/bilibili/followings-dynamic.ts | 2 +- lib/routes/bilibili/hot-search.ts | 3 - lib/routes/bitget/announcement.ts | 5 +- lib/routes/bjwxdxh/index.ts | 2 +- lib/routes/bntnews/index.ts | 1 - lib/routes/buaa/news/index.ts | 2 +- lib/routes/bullionvault/gold-news.ts | 2 +- lib/routes/bwsg/index.ts | 2 +- lib/routes/capitalmind/utils.ts | 7 +- lib/routes/capitalmuseum/exhibition.tsx | 4 +- lib/routes/cas/genetics/index.ts | 4 +- lib/routes/caus/index.ts | 3 +- lib/routes/ccagm/index.ts | 2 +- lib/routes/cccmc/index.ts | 2 +- lib/routes/cdu/cdrw.ts | 2 +- lib/routes/cdu/tzggcdunews.ts | 2 +- lib/routes/cefco/news.ts | 2 +- lib/routes/ceph/blog.ts | 4 +- lib/routes/chikubi/utils.ts | 2 +- lib/routes/chinafactcheck/utils.ts | 8 +- lib/routes/chinaratings/credit-research.ts | 2 +- lib/routes/chinatimes/index.ts | 6 +- lib/routes/chnmuseum/zl.tsx | 7 +- lib/routes/chongbuluo/index.ts | 8 +- lib/routes/chuapp/chuapp.ts | 4 +- lib/routes/cjlu/yjsy/index.ts | 18 -- lib/routes/claude/blog.ts | 10 +- lib/routes/claude/code-changelog.ts | 6 +- lib/routes/cline/blog.ts | 4 +- lib/routes/cmu/andypavlo/blog.ts | 4 +- lib/routes/cnljxh/index.ts | 2 +- lib/routes/cnu/iec.ts | 2 +- lib/routes/cnu/jdxw.ts | 2 +- lib/routes/cnu/jwc.ts | 4 +- lib/routes/cnu/physics.ts | 2 +- lib/routes/cnu/smkxxy.ts | 2 +- lib/routes/cockroachlabs/blog.ts | 2 +- lib/routes/cognition/blog.ts | 95 ++----- lib/routes/cointelegraph/index.ts | 2 +- lib/routes/comic-fuz/magazine.ts | 3 +- lib/routes/comic-fuz/manga.ts | 3 +- lib/routes/comic-walker/manga.ts | 5 +- lib/routes/comicat/search.ts | 4 +- lib/routes/cool18/index.ts | 2 +- lib/routes/coolidge/news.ts | 10 +- lib/routes/cosplaytele/article.ts | 4 +- lib/routes/costar/press-releases.ts | 4 +- lib/routes/crush/index.ts | 4 +- lib/routes/csust/tggs.ts | 2 +- lib/routes/csust/utils.ts | 7 +- lib/routes/csust/xkxs.ts | 2 +- lib/routes/cugb/jwc.ts | 6 +- lib/routes/cugb/news.ts | 5 +- lib/routes/cuilingmag/index.ts | 2 +- lib/routes/cursor/blog.ts | 6 +- lib/routes/cursor/changelog.ts | 10 +- lib/routes/daoxuan/rss.ts | 4 +- lib/routes/ddosi/index.ts | 3 - lib/routes/decrypt/index.ts | 70 ++--- lib/routes/denonbu/news.ts | 4 +- lib/routes/dev.to/guides.ts | 4 +- lib/routes/dev.to/top.ts | 2 +- lib/routes/dgut/jwb.ts | 2 +- lib/routes/dhu/jiaowu/news.ts | 2 +- lib/routes/dhu/news/xsxx.ts | 2 +- lib/routes/dhu/xxgk/news.ts | 2 +- lib/routes/dhu/yjs/news.ts | 2 +- lib/routes/diariofruticola/filtro.ts | 2 +- .../digitalpolicyalert/activity-tracker.ts | 15 +- lib/routes/dnaindia/common.ts | 1 - lib/routes/douban/other/later.ts | 4 +- lib/routes/douban/people/status.ts | 8 +- lib/routes/dpm/exhibitions.tsx | 2 +- lib/routes/dw/utils.tsx | 4 +- lib/routes/dykszx/news.ts | 10 +- lib/routes/ecnu/art.ts | 10 +- lib/routes/ecnu/bksy.ts | 8 - lib/routes/ecnu/cee.ts | 18 +- lib/routes/ecnu/chem.ts | 10 +- lib/routes/ecnu/chinese.ts | 10 +- lib/routes/ecnu/comm.ts | 10 +- lib/routes/ecnu/cs.ts | 8 - lib/routes/ecnu/cxcy.ts | 2 +- lib/routes/ecnu/dase.ts | 8 - lib/routes/ecnu/dx.ts | 10 +- lib/routes/ecnu/dxb.ts | 10 +- lib/routes/ecnu/ed.ts | 10 +- lib/routes/ecnu/geoai.ts | 10 +- lib/routes/ecnu/ghcollege.ts | 10 +- lib/routes/ecnu/history.ts | 10 +- lib/routes/ecnu/mks.ts | 10 +- lib/routes/ecnu/mxcsy.ts | 10 +- lib/routes/ecnu/pharm.ts | 10 +- lib/routes/ecnu/philo.ts | 2 +- lib/routes/ecnu/phy.ts | 8 - lib/routes/ecnu/psy.ts | 8 - lib/routes/ecnu/sees.ts | 10 +- lib/routes/ecnu/sei.ts | 10 +- lib/routes/ecnu/spm.ts | 10 +- lib/routes/ecnu/stat.ts | 10 +- lib/routes/ecnu/tyxx.ts | 10 +- lib/routes/ecust/jwc/notice.ts | 2 +- lib/routes/elamigos/index.ts | 4 +- lib/routes/eprice/rss.tsx | 6 +- lib/routes/expats/czech-news.ts | 2 +- lib/routes/f95zone/post.ts | 3 +- lib/routes/f95zone/thread.ts | 12 +- lib/routes/f95zone/utils.ts | 2 +- lib/routes/fantia/user.ts | 1 - lib/routes/fashionnetwork/index.ts | 3 +- lib/routes/firefox/breaches.ts | 2 +- lib/routes/fjdaily/index.ts | 4 +- lib/routes/flyert/util.ts | 6 +- lib/routes/flyert/utils.ts | 2 +- lib/routes/freebuf/index.ts | 1 - lib/routes/fxiaoke/crm.ts | 2 +- lib/routes/gameapps/index.tsx | 6 +- lib/routes/gamer/hot.ts | 7 +- lib/routes/gdmuseum/exhibition.tsx | 5 +- lib/routes/gdmuseum/news.ts | 4 +- lib/routes/gdufs/news.ts | 2 +- lib/routes/gdufs/xwxy/index.ts | 2 +- lib/routes/gigazine/en.ts | 12 +- lib/routes/github/activity.ts | 2 +- lib/routes/github/advisor.ts | 2 +- lib/routes/github/topic.ts | 2 +- lib/routes/go/jihs/idwr.ts | 2 +- lib/routes/google/developers.ts | 6 +- lib/routes/google/research.ts | 2 +- lib/routes/gov/beijing/bjedu/gh.ts | 4 +- lib/routes/gov/chongqing/sydwgkzp.ts | 4 +- lib/routes/gov/hainan/iitb/tzgg.ts | 2 +- lib/routes/gov/huizhou/zwgk/index.ts | 2 +- lib/routes/gov/mee/nnsa.ts | 2 +- lib/routes/gov/mee/ywdt.ts | 2 +- lib/routes/gov/mem/zfxxgkpt.ts | 4 +- lib/routes/gov/mfa/wjdt.ts | 2 +- lib/routes/gov/miit/wjfb.ts | 4 +- lib/routes/gov/miit/wjgs.ts | 6 - lib/routes/gov/miit/yjzj.ts | 4 +- lib/routes/gov/miit/zcjd.ts | 6 - lib/routes/gov/moa/gjs.ts | 2 +- lib/routes/gov/mof/gss.ts | 2 +- lib/routes/gov/mot/index.ts | 2 +- lib/routes/gov/nppa/channels.ts | 2 +- lib/routes/gov/pudong/zwgk.ts | 2 +- lib/routes/gov/shenzhen/hrss/szksy/index.ts | 2 +- lib/routes/gov/shenzhen/szlh/index.ts | 8 +- lib/routes/gov/shenzhen/zjj/index.ts | 4 +- lib/routes/gov/zj/czt/zfcg.ts | 2 +- lib/routes/gov/zj/search.ts | 8 +- lib/routes/grainoil/category.ts | 2 +- lib/routes/greasyfork/scripts.ts | 2 +- lib/routes/grubstreet/utils.ts | 5 +- lib/routes/guancha/member.ts | 2 +- lib/routes/gxmzu/lib.ts | 2 +- lib/routes/gxmzu/utils/index.ts | 17 +- lib/routes/hackernews/index.ts | 20 +- lib/routes/hamel/index.ts | 6 +- lib/routes/hebeimuseum/list.tsx | 15 +- lib/routes/hexun/index.ts | 4 +- lib/routes/hit/today.ts | 22 +- lib/routes/hitsz/due-tzgg.ts | 2 +- lib/routes/hitwh/today.ts | 30 +- lib/routes/hlju/news.ts | 2 +- lib/routes/hnmuseum/exhibitions.tsx | 6 +- lib/routes/hongkong/chp.ts | 2 +- lib/routes/hotukdeals/hottest.ts | 6 +- lib/routes/hpoi/utils.ts | 9 - lib/routes/hrbeu/cec/list.ts | 11 +- lib/routes/hrbeu/gx/card.ts | 6 +- lib/routes/hrbeu/gx/list.ts | 6 +- lib/routes/hrbeu/sec/list.ts | 9 +- lib/routes/hrbeu/uae/news.ts | 6 +- lib/routes/hrbeu/ugs/news.ts | 8 +- lib/routes/hrbeu/yjsy/list.ts | 6 +- lib/routes/hrbust/cs.ts | 2 +- lib/routes/hrbust/jwzx.ts | 2 +- lib/routes/huggingface/blog-community.ts | 2 +- lib/routes/huggingface/blog-zh.ts | 2 +- lib/routes/huggingface/blog.ts | 2 +- lib/routes/huijin-inv/news.ts | 2 +- lib/routes/humanlayer/blog.ts | 6 +- lib/routes/hunau/utils/news-content.ts | 2 +- lib/routes/ieee/journal.ts | 2 +- lib/routes/iheima/index.ts | 1 - lib/routes/iiilab/index.ts | 2 +- lib/routes/in-en/index.ts | 10 +- lib/routes/inceptionlabs/blog.ts | 10 +- lib/routes/infoq/recommend.ts | 3 - lib/routes/infzm/hot.ts | 3 - lib/routes/infzm/utils.ts | 2 +- lib/routes/inoreader/index.ts | 3 +- lib/routes/investor/index.ts | 4 +- lib/routes/ipsw.dev/index.tsx | 3 - lib/routes/ipsw/index.ts | 8 +- lib/routes/itsec/news.ts | 8 +- lib/routes/j-test/news.ts | 2 +- lib/routes/jandan/index.ts | 6 +- lib/routes/jandan/section.ts | 39 +-- lib/routes/jandan/utils.ts | 263 +++++------------- lib/routes/jianshu/home.ts | 3 - lib/routes/jiemian/account.ts | 53 +++- lib/routes/jiemian/common.tsx | 129 +++++---- lib/routes/jiemian/lists.ts | 76 ++++- lib/routes/jiemian/special.ts | 4 +- lib/routes/jiemian/video.ts | 91 +++++- lib/routes/jiemian/vip.ts | 42 ++- lib/routes/jinse/lives.ts | 57 ++-- lib/routes/jinse/timeline.ts | 2 +- lib/routes/jisilu/util.ts | 4 +- lib/routes/jlu/ccst/xwzx/index.ts | 2 +- lib/routes/jlu/jwc.ts | 6 +- lib/routes/joneslanglasalle/index.ts | 4 +- lib/routes/jou/utils/index.ts | 17 +- lib/routes/jpmorganchase/research.ts | 2 +- lib/routes/jumeili/home.ts | 2 - lib/routes/kanxue/topic.ts | 3 - lib/routes/kcna/news.tsx | 105 +++---- lib/routes/kcna/utils.ts | 64 +---- lib/routes/keylol/index.ts | 2 +- lib/routes/kiro/blog.ts | 2 +- lib/routes/kiro/changelog.ts | 4 +- .../kleinanzeigen/utils/parse-listing-page.ts | 2 +- lib/routes/kovidgoyal/kitty/changelog.ts | 2 +- lib/routes/last-origin/news.ts | 6 +- lib/routes/logrocket/index.ts | 2 +- lib/routes/malaysiakini/index.ts | 4 +- lib/routes/mashiro/index.ts | 2 +- lib/routes/medium/parse-article.ts | 2 +- lib/routes/meritalk/articles.ts | 6 +- lib/routes/meteoblue/weathernews.ts | 4 +- lib/routes/mhlw/monthly-labour-survey.ts | 12 +- lib/routes/mi/utils.tsx | 3 - lib/routes/misskon/utils.ts | 1 - lib/routes/mit/hanlab.ts | 4 +- lib/routes/mit/scratch/user-comments.ts | 2 +- lib/routes/miyuki/news.ts | 8 +- lib/routes/mrinalxdev/blog.ts | 2 +- lib/routes/my-formosa/index.ts | 41 ++- lib/routes/my-formosa/namespace.ts | 2 +- lib/routes/mycard520/news.ts | 10 +- lib/routes/nankai/ai-notice.ts | 37 +-- lib/routes/nankai/graduate-notice.ts | 95 ++----- lib/routes/nankai/jwc.ts | 19 +- lib/routes/nankai/notice.ts | 27 +- lib/routes/nankai/yzb.ts | 2 +- lib/routes/natgeo/natgeo.ts | 2 +- lib/routes/ncu/jwc.ts | 18 +- lib/routes/neea/jlpt.ts | 2 +- lib/routes/netflix/research.ts | 2 +- lib/routes/nextjs/blog.ts | 4 +- lib/routes/nga/post.ts | 2 +- lib/routes/niaogebiji/cat.ts | 8 +- lib/routes/nielsberglund/index.ts | 4 +- lib/routes/nikkei/asia/index.ts | 4 +- lib/routes/njit/jwc.ts | 2 +- lib/routes/njit/tzgg.ts | 2 +- lib/routes/nju/scit.ts | 2 +- lib/routes/nju/zbb.ts | 8 +- lib/routes/njucm/utils/index.ts | 6 +- lib/routes/njupt/jwc.ts | 84 ++---- lib/routes/njxzc/home.ts | 2 +- lib/routes/njxzc/lib.ts | 4 +- lib/routes/njxzc/utils/index.ts | 19 +- lib/routes/nmc/publish.ts | 4 +- lib/routes/notion/release.ts | 8 +- lib/routes/nowcoder/schedule.ts | 2 +- lib/routes/npr/full.ts | 4 +- lib/routes/nuist/bulletin.ts | 2 +- lib/routes/nuist/cas.ts | 2 +- lib/routes/nyc/mayors-office-news.ts | 2 +- lib/routes/nycu/aa.ts | 2 +- lib/routes/nycu/announcement.ts | 2 +- lib/routes/nycu/osa.ts | 4 +- lib/routes/nymity/censorbib.ts | 6 +- lib/routes/ollama/blog.ts | 6 +- lib/routes/ollama/models.ts | 4 +- lib/routes/openrice/promos.ts | 6 +- lib/routes/oreno3d/get-sec-page-data.ts | 4 - lib/routes/osu/beatmaps/packs.ts | 2 +- lib/routes/p-articles/contributors.ts | 2 +- lib/routes/p-articles/utils.ts | 4 +- lib/routes/papers/category.ts | 3 +- lib/routes/people/index.ts | 2 +- lib/routes/perplexity/blog.ts | 10 +- lib/routes/peterwunder/achievements.ts | 24 +- lib/routes/pincong/hot.ts | 2 +- lib/routes/pingwest/status.ts | 3 - lib/routes/pingwest/tag.ts | 9 +- lib/routes/pingwest/user.ts | 9 +- lib/routes/pku/eecs.ts | 56 ++-- lib/routes/pku/utils.ts | 13 - lib/routes/pornhub/category-url.ts | 2 +- lib/routes/priconne-redive/news.ts | 4 +- lib/routes/projectjav/utils.ts | 4 +- lib/routes/qingting/podcast.ts | 6 +- lib/routes/qlu/notice.ts | 2 +- lib/routes/quicker/qa.ts | 3 +- lib/routes/quicker/share.ts | 2 +- lib/routes/quicker/user.ts | 2 +- lib/routes/qztc/sjxy/index.ts | 2 +- lib/routes/railway/index.ts | 2 +- lib/routes/raycast/changelog.ts | 4 +- lib/routes/react/blog.ts | 4 +- lib/routes/resetera/thread.ts | 12 +- lib/routes/reuters/common.tsx | 1 - lib/routes/rfi/news.ts | 26 +- lib/routes/rsshub/transform/sitemap.ts | 6 +- lib/routes/ruankao/news.ts | 2 +- lib/routes/rule34video/latest.ts | 3 - lib/routes/rustcc/jobs.ts | 3 - lib/routes/rustcc/news.ts | 3 - lib/routes/samrdprc/index.ts | 4 +- lib/routes/samrdprc/news.ts | 2 +- lib/routes/sankei/news.ts | 2 +- lib/routes/sankei/topics.ts | 2 +- lib/routes/saraba1st/thread.ts | 1 - lib/routes/sass/gs/index.ts | 2 +- lib/routes/scmp/index.ts | 2 +- lib/routes/scnu/cs/match.ts | 3 - lib/routes/scnu/jw.ts | 5 +- lib/routes/scnu/library.ts | 3 - lib/routes/scpta/news.ts | 2 +- lib/routes/scut/jwc/news.ts | 10 +- lib/routes/scut/jwc/notice.ts | 10 +- lib/routes/scut/jwc/school.ts | 10 +- lib/routes/shanghaimuseum/offline-exhibit.tsx | 2 +- lib/routes/shisu/en.ts | 2 +- lib/routes/shisu/news.ts | 2 +- lib/routes/shmeea/index.ts | 4 +- lib/routes/shopify/apps/[handle].reviews.ts | 3 +- lib/routes/shopify/apps/search.ts | 3 +- lib/routes/shu/global.ts | 4 +- lib/routes/shu/index.ts | 6 +- lib/routes/sjtu/cs/tzgg.tsx | 15 +- lib/routes/sjtu/cs/xshd.tsx | 17 -- lib/routes/sjtu/jwc.ts | 11 - lib/routes/smashingmagazine/category.ts | 9 +- lib/routes/smzdm/haowen.ts | 2 +- lib/routes/snnu/ccs.ts | 2 +- lib/routes/snnu/index.ts | 8 +- lib/routes/snnu/yjs.ts | 2 +- lib/routes/solidot/_article.ts | 6 +- lib/routes/stcn/index.ts | 6 +- lib/routes/stcn/kx.ts | 4 +- lib/routes/stcn/rank.ts | 4 +- lib/routes/steam/search.ts | 8 +- lib/routes/steam/sharefile-changelog.ts | 10 +- lib/routes/swjtu/scai.ts | 4 +- lib/routes/sxhm/announcement.ts | 2 +- lib/routes/syosetu/dev.ts | 2 +- lib/routes/syosetu/utils.ts | 2 +- lib/routes/sysu/cse.ts | 3 - lib/routes/szftedu/dongtai.ts | 2 +- lib/routes/szftedu/gonggao.ts | 2 +- lib/routes/szmuseum/temporary.tsx | 6 +- lib/routes/szse/notice.ts | 8 +- lib/routes/szu/yz/utils.ts | 34 +-- lib/routes/taobao/mysql.ts | 2 +- lib/routes/techpowerup/review.ts | 6 +- lib/routes/the/index.ts | 14 +- lib/routes/thegradient/index.ts | 4 +- lib/routes/thepaper/839studio/category.ts | 2 +- lib/routes/thepaper/839studio/studio.ts | 2 +- lib/routes/theverge/index.ts | 2 +- lib/routes/thinkingmachines/news.ts | 4 +- lib/routes/thoughtworks/index.ts | 1 - lib/routes/thzt/index.ts | 4 +- lib/routes/tidb/blog.ts | 2 +- lib/routes/tjbwg/exhibition.tsx | 3 +- lib/routes/tju/cic/index.ts | 6 +- lib/routes/tju/yzb/index.ts | 3 - lib/routes/tongji/sem/_utils.ts | 2 +- lib/routes/tongji/sse/_article.ts | 6 - lib/routes/tophub/index.ts | 1 - lib/routes/tophub/list.tsx | 1 - lib/routes/toranoana/news.ts | 2 +- lib/routes/trendforce/news-cn.ts | 10 +- lib/routes/tsinghua/lib/tzgg.ts | 8 +- lib/routes/udn/global/index.ts | 8 +- lib/routes/uestc/gr.ts | 2 +- lib/routes/uestc/scse.ts | 3 +- lib/routes/unipd/ilbolive/news.ts | 8 +- lib/routes/usenix/usenix.ts | 2 +- lib/routes/ustb/yjsy/news.ts | 6 +- lib/routes/ustc/gs.ts | 2 +- lib/routes/ustc/math.ts | 2 +- lib/routes/ustc/sist.ts | 2 +- lib/routes/verfghbw/press.ts | 76 +++-- lib/routes/visionias/daily-news-summary.ts | 2 +- lib/routes/visionias/news-today.ts | 2 +- lib/routes/visualstudio/code-blog.ts | 2 +- lib/routes/warp/blog.ts | 3 +- lib/routes/wechat/sogou.ts | 1 - lib/routes/weibo/oasis/user.ts | 4 +- lib/routes/wfu/news.ts | 3 - lib/routes/whu/cs.ts | 2 +- lib/routes/whu/rsgis.ts | 8 +- lib/routes/whu/swrh.ts | 6 +- lib/routes/wiensued/index.ts | 6 +- lib/routes/windsurf/changelog.ts | 4 +- lib/routes/wohnnet/index.ts | 2 +- lib/routes/wufazhuce/one.ts | 4 +- lib/routes/xaut/index.ts | 2 +- lib/routes/xbmu/academic.ts | 2 +- lib/routes/xbookcn/blog.ts | 4 +- lib/routes/xidian/cs.ts | 6 +- lib/routes/xidian/gr.ts | 6 +- lib/routes/xidian/jwc.ts | 6 +- lib/routes/xjtlu/news.ts | 2 +- lib/routes/xjtu/ee-jzxx.ts | 1 - lib/routes/xmanhua/index.ts | 8 +- lib/routes/xmu/kydt.ts | 8 +- lib/routes/xmut/jwc/bkjw.ts | 8 +- lib/routes/xueqiu/hots.ts | 1 - lib/routes/xueqiu/today.ts | 1 - lib/routes/xyu/library.ts | 2 +- lib/routes/xyu/notices.ts | 11 +- lib/routes/ynet/list.ts | 2 +- lib/routes/zaimanhua/comic.ts | 1 - lib/routes/zaimanhua/update.ts | 3 +- lib/routes/zcmu/yxy/index.ts | 2 +- lib/routes/zed/blog.ts | 1 - lib/routes/zhizhuan100/report.ts | 4 +- lib/routes/zju/math/index.ts | 15 +- lib/routes/zju/sis/index.ts | 4 +- lib/routes/zjut/cs/index.ts | 4 +- lib/routes/zjut/jwc/index.ts | 2 +- lib/routes/zjut/www/index.ts | 4 +- lib/routes/zongheng/detail.ts | 2 +- lib/routes/zotero/versions.ts | 8 +- lib/routes/zxcs/novel.ts | 2 +- lib/routes/zzu/dzb.ts | 4 +- lib/routes/zzu/kjc.ts | 4 +- lib/routes/zzu/math.ts | 4 +- lib/routes/zzu/news.ts | 4 +- lib/routes/zzu/rsc.ts | 6 +- lib/routes/zzu/ss.ts | 2 +- lib/routes/zzu/sxy.ts | 10 +- lib/routes/zzu/zcycwb.ts | 4 +- lib/types.ts | 8 +- lib/views/json.ts | 2 +- 486 files changed, 1485 insertions(+), 2336 deletions(-) delete mode 100644 lib/routes/pku/utils.ts diff --git a/lib/routes/0x80/index.ts b/lib/routes/0x80/index.ts index c59a1cf0785c..13463db233f9 100644 --- a/lib/routes/0x80/index.ts +++ b/lib/routes/0x80/index.ts @@ -41,7 +41,7 @@ async function handler() { item = $(item); const link = item.attr('href') || ''; - const title = item.text() || ''; + const title = item.text(); const pubDate = extractDateFromURL(link); return { diff --git a/lib/routes/163/music/artist-songs.ts b/lib/routes/163/music/artist-songs.ts index f1becb7ef578..7ceca4957f32 100644 --- a/lib/routes/163/music/artist-songs.ts +++ b/lib/routes/163/music/artist-songs.ts @@ -32,9 +32,6 @@ async function handler(ctx) { const id = ctx.req.param('id'); const { data } = await got('https://music.163.com/api/v1/artist/songs', { - headers: { - Referer: 'https://music.163.com/', - }, searchParams: { id, private_cloud: 'true', diff --git a/lib/routes/163/music/artist.ts b/lib/routes/163/music/artist.ts index 5b6e1dd3dba8..76c40dee7b5a 100644 --- a/lib/routes/163/music/artist.ts +++ b/lib/routes/163/music/artist.ts @@ -31,11 +31,7 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const response = await got(`https://music.163.com/api/artist/albums/${id}`, { - headers: { - Referer: 'https://music.163.com/', - }, - }); + const response = await got(`https://music.163.com/api/artist/albums/${id}`); const data = response.data; diff --git a/lib/routes/163/music/djradio.tsx b/lib/routes/163/music/djradio.tsx index 19cdcf1840b3..18d117a5248a 100644 --- a/lib/routes/163/music/djradio.tsx +++ b/lib/routes/163/music/djradio.tsx @@ -57,9 +57,6 @@ const ProcessFeed = (id, limit, offset) => `163:music:djradio:${id}:${limit}:${offset}`, async () => await got.post('https://music.163.com/api/dj/program/byradio', { - headers: { - Referer: 'https://music.163.com/', - }, form: { radioId: id, limit, diff --git a/lib/routes/163/music/playlist.ts b/lib/routes/163/music/playlist.ts index 0c9eca8f2570..38bdfb157d91 100644 --- a/lib/routes/163/music/playlist.ts +++ b/lib/routes/163/music/playlist.ts @@ -40,16 +40,12 @@ async function handler(ctx) { const response = await got.get(`https://music.163.com/api/v3/playlist/detail?id=${id}`, { headers: { - Referer: 'https://music.163.com/', Cookie: config.ncm.cookies, }, }); const data = response.data.playlist; const songinfo = await got('https://music.163.com/api/song/detail', { - headers: { - Referer: 'https://music.163.com', - }, searchParams: { ids: `[${data.trackIds.slice(0, 201).map((item) => item.id)}]`, }, diff --git a/lib/routes/163/music/userevents.tsx b/lib/routes/163/music/userevents.tsx index 911f0df8c403..8efb40e0822f 100644 --- a/lib/routes/163/music/userevents.tsx +++ b/lib/routes/163/music/userevents.tsx @@ -37,11 +37,7 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const response = await got(`https://music.163.com/api/event/get/${id}`, { - headers: { - Referer: 'https://music.163.com/', - }, - }); + const response = await got(`https://music.163.com/api/event/get/${id}`); const { data } = response; const { nickname, signature, avatarUrl } = data.events[0].user; diff --git a/lib/routes/19lou/index.ts b/lib/routes/19lou/index.ts index 00c01289a3e3..8cf8cba12da0 100644 --- a/lib/routes/19lou/index.ts +++ b/lib/routes/19lou/index.ts @@ -100,7 +100,7 @@ async function handler(ctx) { item.author = content('.uname, .user-name').first().text(); item.description = content('.post-cont').first().html() || content('.thread-cont').html(); - item.pubDate = timezone(parseDate(content('.cont-top-left meta').first().attr('content')), 8); + item.pubDate = timezone(parseDate(content('.cont-top-left meta').attr('content')), 8); return item; }) diff --git a/lib/routes/2023game/index.ts b/lib/routes/2023game/index.ts index 13ad92163cba..48182ff63a3d 100644 --- a/lib/routes/2023game/index.ts +++ b/lib/routes/2023game/index.ts @@ -55,8 +55,8 @@ async function handler(ctx: Context): Promise<Data> { title: $item.text().trim(), guid: `2023game:${href}`, link: href!, - pubDate: parseDate($item.find('.time_box').text().trim()), - description: $item.html() ?? '', + pubDate: parseDate($item.find('.time_box').text()), + description: $item.html(), }; }); diff --git a/lib/routes/30secondsofcode/utils.ts b/lib/routes/30secondsofcode/utils.ts index 502ed2ba34f5..230debc1563f 100644 --- a/lib/routes/30secondsofcode/utils.ts +++ b/lib/routes/30secondsofcode/utils.ts @@ -30,15 +30,9 @@ async function processItem({ link: articleLink, date }) { .map((tag) => $(tag).find('a').text()); const article = $('main > article'); const title = article.find('h1').text(); - article.find('img').each((_, element) => { - const img = $(element); - const src = img.attr('src'); - if (src?.startsWith('/')) { - img.attr('src', `${rootUrl}${src}`); - } - }); const image = article.find('img').attr('src'); - const description = article.clone().find('h1, script').remove().end().html(); + article.find('h1').remove(); + const description = article.html(); return { title, diff --git a/lib/routes/3dmgame/news-center.ts b/lib/routes/3dmgame/news-center.ts index 8006f074c5df..2ca342d3e3fb 100644 --- a/lib/routes/3dmgame/news-center.ts +++ b/lib/routes/3dmgame/news-center.ts @@ -48,7 +48,7 @@ async function handler(ctx) { title: item.find('.bt').text(), link: item.attr('href'), description: item.find('p').text(), - pubDate: timezone(parseDate(item.find('.time').text().trim()), 8), + pubDate: timezone(parseDate(item.find('.time').text()), 8), }; } const a = item.find('.text a'); @@ -56,7 +56,7 @@ async function handler(ctx) { title: a.first().text(), link: a.attr('href'), description: item.find('.miaoshu').text(), - pubDate: timezone(parseDate(item.find('.time').text().trim()), 8), + pubDate: timezone(parseDate(item.find('.time').text()), 8), }; }); diff --git a/lib/routes/4khd/article.ts b/lib/routes/4khd/article.ts index 75a2a585b03b..5f8ddf8ea0cb 100644 --- a/lib/routes/4khd/article.ts +++ b/lib/routes/4khd/article.ts @@ -21,7 +21,7 @@ function loadArticle(item: WPPost) { return { title: item.title.rendered, - description: article.html() ?? '', + description: article.html(), pubDate: parseDate(item.date_gmt), link: item.link, }; diff --git a/lib/routes/4kup/article.ts b/lib/routes/4kup/article.ts index d0ff478743c8..f1e158495c27 100644 --- a/lib/routes/4kup/article.ts +++ b/lib/routes/4kup/article.ts @@ -22,7 +22,7 @@ function loadArticle(item: WPPost) { return { title: item.title.rendered, - description: article.html() ?? '', + description: article.html(), pubDate: parseDate(item.date_gmt), link: item.link, }; diff --git a/lib/routes/51read/article.ts b/lib/routes/51read/article.ts index 35e7ffa4eab7..b8556f6db9ad 100644 --- a/lib/routes/51read/article.ts +++ b/lib/routes/51read/article.ts @@ -81,7 +81,7 @@ const buildItem = (url: string) => return { title: $('h1').text(), - description: $('.kb-cot').html() || '', + description: $('.kb-cot').html(), link: url, }; }) as Promise<DataItem>; diff --git a/lib/routes/6v123/index.ts b/lib/routes/6v123/index.ts index 8da5bd463402..54e55a5e65a5 100644 --- a/lib/routes/6v123/index.ts +++ b/lib/routes/6v123/index.ts @@ -73,7 +73,7 @@ export const handler = async (ctx: Context): Promise<Data> => { $$('div#endText div.downtps').remove(); const title: string = $$('h1').text(); - const description: string | undefined = $$('div#endText').html() ?? undefined; + const description = $$('div#endText').html(); const pubDateStr: string | undefined = item.link?.match(/\/(\d{4}-\d{2}-\d{2})\/\d+\.html/)?.[1]; const categoryEls: Element[] = $$('div#endText p a').toArray(); const categories: string[] = [...new Set(categoryEls.map((el) => $$(el).text()?.trim()).filter(Boolean))]; diff --git a/lib/routes/a9vg/index.ts b/lib/routes/a9vg/index.ts index 5259262cbc18..dc911e6cd1c9 100644 --- a/lib/routes/a9vg/index.ts +++ b/lib/routes/a9vg/index.ts @@ -72,7 +72,7 @@ export const handler = async (ctx) => { ); }); - item.title = $$('h1.ts, div.c-article-main_content-title').first().text(); + item.title = $$('h1.ts, div.c-article-main_content-title').text(); item.description = renderDescription({ description: $$('td.t_f, div.c-article-main_contentraw').first().html(), }); @@ -91,7 +91,6 @@ export const handler = async (ctx) => { $$('div.authi em') .first() .text() - .trim() .match(/发表于 (\d+-\d+-\d+ \d+:\d+)/)?.[1] ?? $$('span.c-article-main_content-intro-item').first().text(), ['YYYY-M-D HH:mm', 'YYYY-MM-DD HH:mm'] ), diff --git a/lib/routes/aflcio/blog.ts b/lib/routes/aflcio/blog.ts index d05d11a013de..e9570b925d43 100644 --- a/lib/routes/aflcio/blog.ts +++ b/lib/routes/aflcio/blog.ts @@ -27,7 +27,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $aEl: Cheerio<Element> = $el.find('header.container h1 a').first(); const title: string = $aEl.text(); - const description: string | undefined = $el.find('div.section').html() ?? ''; + const description = $el.find('div.section').html(); const pubDateStr: string | undefined = $el.find('div.date-timeline time').attr('datetime'); const linkUrl: string | undefined = $aEl.attr('href'); const authorEls: Element[] = $el.find('div.date-timeline a.user').toArray(); @@ -74,7 +74,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('header.article-header h1').text(); - const description: string | undefined = $$('div.section-article-body').html() ?? ''; + const description = $$('div.section-article-body').html(); const pubDateStr: string | undefined = $$('time').attr('datetime'); const authorEls: Element[] = $$('div.byline a[property="schema:name"]').toArray(); const authors: DataItem['author'] = authorEls.map((authorEl) => { diff --git a/lib/routes/agirls/topic-list.ts b/lib/routes/agirls/topic-list.ts index b7f1f0fff6ff..5d4c48f5e1e3 100644 --- a/lib/routes/agirls/topic-list.ts +++ b/lib/routes/agirls/topic-list.ts @@ -42,14 +42,14 @@ async function handler() { .map((item) => { item = $(item); return { - title: item.find('.ag-topic__link').text().trim(), - description: item.find('.ag-topic__summery').text().trim(), + title: item.find('.ag-topic__link').text(), + description: item.find('.ag-topic__summery').text(), link: `${baseUrl}${item.find('.ag-topic__link').attr('href')}`, }; }); return { - title: $('head title').text().trim(), + title: $('head title').text(), link, description: $('head meta[name=description]').attr('content'), item: items, diff --git a/lib/routes/agirls/topic.ts b/lib/routes/agirls/topic.ts index 5ea7dc8a2bc6..ff67a670c170 100644 --- a/lib/routes/agirls/topic.ts +++ b/lib/routes/agirls/topic.ts @@ -41,7 +41,7 @@ async function handler(ctx) { .map((item) => { item = $(item); return { - title: item.text().trim(), + title: item.text(), link: `${baseUrl}${item.attr('href')}`, }; }); @@ -49,7 +49,7 @@ async function handler(ctx) { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseArticle(item)))); return { - title: $('head title').text().trim(), + title: $('head title').text(), link, description: ldJson['@graph'][0].description, item: items, diff --git a/lib/routes/agirls/utils.ts b/lib/routes/agirls/utils.ts index 13059a6978bd..31d2d05a90a1 100644 --- a/lib/routes/agirls/utils.ts +++ b/lib/routes/agirls/utils.ts @@ -13,7 +13,7 @@ const parseArticle = async (item) => { ...new Set( content('.ag-article__tag') .toArray() - .map((e) => content(e).text().trim().replace('#', '')) + .map((e) => content(e).text().replace('#', '')) ), ]; const ldJson = JSON.parse(content('script[type="application/ld+json"]').text()); diff --git a/lib/routes/agirls/z-index.ts b/lib/routes/agirls/z-index.ts index 689668196f27..cfab56f9d2f4 100644 --- a/lib/routes/agirls/z-index.ts +++ b/lib/routes/agirls/z-index.ts @@ -45,7 +45,7 @@ async function handler(ctx) { .map((item) => { item = $(item); return { - title: item.text().trim(), + title: item.text(), link: `${baseUrl}${item.attr('href')}`, }; }); @@ -53,7 +53,7 @@ async function handler(ctx) { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseArticle(item)))); return { - title: $('head title').text().trim(), + title: $('head title').text(), link, description: $('head meta[name=description]').attr('content'), item: items, diff --git a/lib/routes/ai-bot/daily-ai-news.ts b/lib/routes/ai-bot/daily-ai-news.ts index d40cb3a59618..cffe9d822e68 100755 --- a/lib/routes/ai-bot/daily-ai-news.ts +++ b/lib/routes/ai-bot/daily-ai-news.ts @@ -44,7 +44,7 @@ function processNewsList($: CheerioAPI, $newsList: Cheerio<Element>, ctx: DateCo const $child = $(child); if ($child.hasClass('news-date')) { - currentPubDate = parseDateString($child.text().trim(), ctx); + currentPubDate = parseDateString($child.text(), ctx); return []; } @@ -52,7 +52,7 @@ function processNewsList($: CheerioAPI, $newsList: Cheerio<Element>, ctx: DateCo const $link = $child.find('h2 a'); const title = $link.text().trim(); const link = $link.attr('href'); - const description = $child.find('p.text-muted').html() || ''; + const description = $child.find('p.text-muted').html(); if (!link) { return []; diff --git a/lib/routes/aiaa/journal.ts b/lib/routes/aiaa/journal.ts index f196199a6e65..203de941c831 100644 --- a/lib/routes/aiaa/journal.ts +++ b/lib/routes/aiaa/journal.ts @@ -38,9 +38,9 @@ async function handler(ctx) { .map((element) => { const $item = $(element); const title = $item.find(String.raw`dc\:title`).text(); - const link = $item.find('link').text() || ''; - const description = $item.find('description').text() || ''; - const pubDate = parseDate($item.find(String.raw`dc\:date`).text() || ''); + const link = $item.find('link').text(); + const description = $item.find('description').text(); + const pubDate = parseDate($item.find(String.raw`dc\:date`).text()); const authors = $item .find(String.raw`dc\:creator`) .toArray() diff --git a/lib/routes/aiblog-2xv/archives.ts b/lib/routes/aiblog-2xv/archives.ts index 38528ecf7471..47d10fe8a950 100644 --- a/lib/routes/aiblog-2xv/archives.ts +++ b/lib/routes/aiblog-2xv/archives.ts @@ -43,16 +43,16 @@ async function handler() { .toArray() .map((postItem) => { const $post = $(postItem); - const $link = $post.find('a').first(); - const $title = $post.find('h3').first(); + const $link = $post.find('a'); + const $title = $post.find('h3'); const $dateMeta = $post.find('.archive-meta span'); return { - title: $title.text().trim(), // 去除首尾空格 + title: $title.text(), link: $link.attr('href') || '', // 解析发布时间和更新时间(根据页面结构调整选择器,若存在则启用) pubDate: parseDate($dateMeta.eq(0).attr('title') || ''), - author: $post.find('.archive-meta span').last().text().trim() || '', + author: $post.find('.archive-meta span').last().text(), description: '', }; }) @@ -64,11 +64,11 @@ async function handler() { const response = await ofetch(item.link); const $ = load(response); - const $main = $('main').first(); + const $main = $('main'); item.description = `<article> <header> <div class="post-description"> - ${$main.find('header .post-description').first().html()} + ${$main.find('header .post-description').html()} </div> </header> @@ -77,7 +77,7 @@ async function handler() { </figure> <div class="post-content"> - ${$main.find('.post-content').first().html()} + ${$main.find('.post-content').html()} </div> </article>`; return item; diff --git a/lib/routes/aisixiang/thinktank.ts b/lib/routes/aisixiang/thinktank.ts index 427e27ae401f..5e27bf23501a 100644 --- a/lib/routes/aisixiang/thinktank.ts +++ b/lib/routes/aisixiang/thinktank.ts @@ -36,7 +36,7 @@ async function handler(ctx) { const $ = load(response); - const title = `${$('h2').first().text().trim()}${type}`; + const title = `${$('h2').first().text()}${type}`; let items = []; diff --git a/lib/routes/aisixiang/toplist.ts b/lib/routes/aisixiang/toplist.ts index ede852f5e190..dd225c45a4fc 100644 --- a/lib/routes/aisixiang/toplist.ts +++ b/lib/routes/aisixiang/toplist.ts @@ -26,7 +26,7 @@ async function handler(ctx) { const $ = load(response); - const title = `${$('a.hl').text() || ''}${$('title').text().split('_', 1)[0]}`; + const title = `${$('a.hl').text()}${$('title').text().split('_', 1)[0]}`; const items = $('div.tops_list') .slice(0, limit) diff --git a/lib/routes/aisixiang/zhuanti.ts b/lib/routes/aisixiang/zhuanti.ts index b08713dd4664..845c0773a78a 100644 --- a/lib/routes/aisixiang/zhuanti.ts +++ b/lib/routes/aisixiang/zhuanti.ts @@ -38,7 +38,7 @@ async function handler(ctx) { const $ = load(response); - const title = $('div.tips h2').first().text(); + const title = $('div.tips h2').text(); const items = $('div.article-title') .slice(0, limit) diff --git a/lib/routes/alicesoft/infomation.ts b/lib/routes/alicesoft/infomation.ts index 305013091b97..d91c0572a2c0 100644 --- a/lib/routes/alicesoft/infomation.ts +++ b/lib/routes/alicesoft/infomation.ts @@ -81,7 +81,12 @@ async function handler(ctx) { ); return { - title: 'ALICESOFT ' + $('article h2').clone().children().remove().end().text(), + title: + 'ALICESOFT ' + + $('article h2') + .contents() + .filter((_, node) => node.type === 'text') + .text(), link: url, item: items, language: 'ja', diff --git a/lib/routes/aliyun/database-month.ts b/lib/routes/aliyun/database-month.ts index c005de1d56d0..be75ade65851 100644 --- a/lib/routes/aliyun/database-month.ts +++ b/lib/routes/aliyun/database-month.ts @@ -38,7 +38,7 @@ async function handler() { .map((e) => { const element = $(e); const title = element.find('a').text().trim(); - const link = `http://mysql.taobao.org${element.find('a').attr('href').trim()}/`; + const link = `http://mysql.taobao.org${element.find('a').attr('href')}/`; return { title, description: '', diff --git a/lib/routes/alwayscontrol/news.ts b/lib/routes/alwayscontrol/news.ts index 17a97b10c822..3e900aa1edab 100644 --- a/lib/routes/alwayscontrol/news.ts +++ b/lib/routes/alwayscontrol/news.ts @@ -4,6 +4,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { finishArticleItem } from '@/utils/wechat-mp'; const baseUrl = 'https://www.alwayscontrol.com.cn'; @@ -25,7 +26,7 @@ export const route: Route = { handler, radar: [ { - source: ['www.alwayscontrol.com.cn/zh-CN/news/list'], + source: ['www.alwayscontrol.com.cn/zh-CN/about/news'], target: '/news', }, ], @@ -33,76 +34,58 @@ export const route: Route = { }; async function handler() { - const listUrl = `${baseUrl}/zh-CN/news/list`; + const listUrl = `${baseUrl}/zh-CN/about/news`; // 获取新闻列表页面 const response = await got(listUrl); const $ = load(response.data); // 解析新闻列表 - const list = $('article') + const list = $('div.grid > a') .toArray() .map((item) => { const $item = $(item); - const title = $item.find('h2').text().trim(); - const date = $item.find('time').text().trim(); - const link = $item.find('a').attr('href'); const image = $item.find('img').attr('src'); return { - title, - link: link ? `${baseUrl}${link}` : '', - pubDate: parseDate(date, 'YYYY-MM-DD'), - image: image ? (image.startsWith('http') ? image : `${baseUrl}${image}`) : '', + title: $item.find('h3').text(), + link: new URL($item.attr('href')!, baseUrl).href, + pubDate: parseDate($item.find('h3').next().text(), 'YYYY.MM.DD'), + image: image ? new URL(image, baseUrl).href : '', }; }); // 获取每篇新闻的详细内容 const items = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - if (!item.link) { - return item; - } + list.map((item) => { + if (new URL(item.link).host === 'mp.weixin.qq.com') { + return finishArticleItem({ ...item, guid: item.link }); + } - try { - const detailResponse = await got(item.link); - const $detail = load(detailResponse.data); + return cache.tryGet(item.link, async () => { + const detailResponse = await got(item.link); + const $detail = load(detailResponse.data); - // 处理图片URL(相对路径转绝对路径) - $detail('article img').each((_, elem) => { - const $img = $detail(elem); - const src = $img.attr('src'); - if (src && src.startsWith('/')) { - $img.attr('src', `${baseUrl}${src}`); - } - }); - - // 移除所有 class、style 等属性,但保留 src、href、alt - $detail('article *').each((_, elem) => { - const $elem = $detail(elem); - const allowedAttrs = new Set(['src', 'href', 'alt', 'title']); - const attrs = Object.keys(elem.attribs || {}); + // 移除所有 class、style 等属性,但保留 src、href、alt + $detail('.prose *').each((_, elem) => { + const $elem = $detail(elem); + const allowedAttrs = new Set(['src', 'href', 'alt', 'title']); + const attrs = Object.keys(elem.attribs || {}); - for (const attr of attrs) { - if (!allowedAttrs.has(attr)) { - $elem.removeAttr(attr); - } + for (const attr of attrs) { + if (!allowedAttrs.has(attr)) { + $elem.removeAttr(attr); } - }); + } + }); - item.description = $detail('article').html() || ''; - item.author = '旭衡电子(深圳)有限公司'; - item.category = ['公司动态', '最新资讯']; + item.description = $detail('.prose').html(); + item.author = '旭衡电子(深圳)有限公司'; + item.category = ['公司动态', '最新资讯']; - return item; - } catch { - // 如果获取详情失败,返回基本图片信息 - item.description = item.image ? `<img src="${item.image}">` : ''; - return item; - } - }) - ) + return item; + }); + }) ); return { diff --git a/lib/routes/anime1/anime.ts b/lib/routes/anime1/anime.ts index 5537c1e95658..e6c1ee6755a7 100644 --- a/lib/routes/anime1/anime.ts +++ b/lib/routes/anime1/anime.ts @@ -39,13 +39,13 @@ async function handler(ctx) { const $ = load(response); - const title = $('.page-title').text().trim(); + const title = $('.page-title').text(); const items = $('article') .toArray() .map((el) => { const $el = $(el); - const title = $el.find('.entry-title a').text().trim(); + const title = $el.find('.entry-title a').text(); return { title, link: $el.find('.entry-title a').attr('href'), diff --git a/lib/routes/anime1/search.ts b/lib/routes/anime1/search.ts index 773cd69ebd66..7fe24ec02740 100644 --- a/lib/routes/anime1/search.ts +++ b/lib/routes/anime1/search.ts @@ -38,7 +38,7 @@ async function handler(ctx) { .toArray() .map((el) => { const $el = $(el); - const title = $el.find('.entry-title a').text().trim(); + const title = $el.find('.entry-title a').text(); return { title, link: $el.find('.entry-title a').attr('href'), diff --git a/lib/routes/anthropic/engineering.ts b/lib/routes/anthropic/engineering.ts index acb0435dfd5d..4fc6cdd5630b 100644 --- a/lib/routes/anthropic/engineering.ts +++ b/lib/routes/anthropic/engineering.ts @@ -34,9 +34,9 @@ async function handler(ctx) { const $e = $(element); const href = $e.attr('href') ?? ''; const fullLink = href.startsWith('http') ? href : `${baseUrl}${href}`; - const pubDate = $e.find('div[class*="date"]').text().trim(); + const pubDate = $e.find('div[class*="date"]').text(); return { - title: $e.find('h2, h3').text().trim(), + title: $e.find('h2, h3').text(), link: fullLink, pubDate, }; @@ -64,7 +64,7 @@ async function handler(ctx) { } }); - item.description = content.html() ?? undefined; + item.description = content.html(); return item; }), diff --git a/lib/routes/anthropic/news.ts b/lib/routes/anthropic/news.ts index 52c05e8fc836..d8522d110b2b 100644 --- a/lib/routes/anthropic/news.ts +++ b/lib/routes/anthropic/news.ts @@ -65,7 +65,7 @@ async function handler(ctx) { } }); - item.description = content.html() ?? undefined; + item.description = content.html(); return item; }), diff --git a/lib/routes/anytxt/release-notes.ts b/lib/routes/anytxt/release-notes.ts index 6b4535f86359..e28d2a4a1831 100644 --- a/lib/routes/anytxt/release-notes.ts +++ b/lib/routes/anytxt/release-notes.ts @@ -27,7 +27,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $el: Cheerio<Element> = $(el); const title: string = $el.text(); - const description: string | undefined = $el.next().html() ?? ''; + const description = $el.next().html(); const pubDateStr: string | undefined = title.split(/\s/, 1)[0]; const linkUrl: string | undefined = targetUrl; const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/apple/design.ts b/lib/routes/apple/design.ts index db5d47045323..a3019840257b 100644 --- a/lib/routes/apple/design.ts +++ b/lib/routes/apple/design.ts @@ -25,7 +25,7 @@ async function handler() { .toArray() .flatMap((item) => { const table = $(item); - const date = table.find('.date').first().text(); + const date = table.find('.date').text(); return table .find('.topic-item') diff --git a/lib/routes/aqara/news.ts b/lib/routes/aqara/news.ts index 5de47f531771..6017ad4a0259 100644 --- a/lib/routes/aqara/news.ts +++ b/lib/routes/aqara/news.ts @@ -40,7 +40,7 @@ async function handler(ctx) { item.title = content('h4.fnt_56').last().text(); item.description = content('div.news_body').html(); - item.pubDate = parseDate(content('div.news_date').first().text(), 'YYYY 年 MM 月 DD 日'); + item.pubDate = parseDate(content('div.news_date').text(), 'YYYY 年 MM 月 DD 日'); return item; }) diff --git a/lib/routes/asiantolick/index.ts b/lib/routes/asiantolick/index.ts index 920eedd65eb9..04b58df8fe56 100644 --- a/lib/routes/asiantolick/index.ts +++ b/lib/routes/asiantolick/index.ts @@ -86,7 +86,7 @@ async function handler(ctx) { const content = load(detailResponse); - item.title = content('h1').first().text(); + item.title = content('h1').text(); item.description = renderDescription({ description: content('#metadata_qrcode').html(), images: content('div.miniatura') diff --git a/lib/routes/bandisoft/history.ts b/lib/routes/bandisoft/history.ts index f32f51f0eac0..e931d079c606 100644 --- a/lib/routes/bandisoft/history.ts +++ b/lib/routes/bandisoft/history.ts @@ -160,7 +160,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const pubDateStr: string | undefined = $el.find('div.cell2').text(); const title: string = version; - const description: string | undefined = $el.find('ul.cell3').html() ?? undefined; + const description = $el.find('ul.cell3').html(); const linkUrl: string = targetUrl; const guid = `bandisoft-${id}-${language}-${version}`; diff --git a/lib/routes/bangumi.tv/group/reply.ts b/lib/routes/bangumi.tv/group/reply.ts index 3c58341027b4..b235bfbb566b 100644 --- a/lib/routes/bangumi.tv/group/reply.ts +++ b/lib/routes/bangumi.tv/group/reply.ts @@ -62,8 +62,8 @@ async function handler(ctx) { const postTopic = { title, description: $('.postTopic .topic_content').html(), - author: $('.postTopic .inner strong a').first().text(), - pubDate: timezone(parseDate($('.postTopic .re_info small').text().trim().slice(5)), 8), + author: $('.postTopic .inner strong a').text(), + pubDate: timezone(parseDate($('.postTopic .re_info small').text().slice(5)), 8), link, }; diff --git a/lib/routes/bangumi.tv/other/followrank.ts b/lib/routes/bangumi.tv/other/followrank.ts index 804beecf7202..f1e74db9c60c 100644 --- a/lib/routes/bangumi.tv/other/followrank.ts +++ b/lib/routes/bangumi.tv/other/followrank.ts @@ -46,7 +46,7 @@ async function handler(ctx) { ?.match(/url\((.*?)\)/)?.[1]; const info = $item.find('small.grey').text(); return { - title: $item.find('.title').text().trim(), + title: $item.find('.title').text(), link, description: `<img src="${imageUrl}"><br>${info}`, }; diff --git a/lib/routes/baoyu/index.ts b/lib/routes/baoyu/index.ts index c7ef8d42090b..d1909380e10c 100644 --- a/lib/routes/baoyu/index.ts +++ b/lib/routes/baoyu/index.ts @@ -38,7 +38,7 @@ async function handler() { const $ = load(response); const container = $('.container'); - const content = container.find('.prose').html() || ''; + const content = container.find('.prose').html(); return { title: item.title, diff --git a/lib/routes/bbc/learningenglish.ts b/lib/routes/bbc/learningenglish.ts index e0acd8f535ee..048959b7caff 100644 --- a/lib/routes/bbc/learningenglish.ts +++ b/lib/routes/bbc/learningenglish.ts @@ -78,7 +78,7 @@ async function handler(ctx: Context) { const $content = load(detailResponse); - item.description = $content('.widget-richtext').html() ?? undefined; + item.description = $content('.widget-richtext').html(); return item; }); }) diff --git a/lib/routes/bilibili/dynamic.ts b/lib/routes/bilibili/dynamic.ts index 9d236d551d1a..bd1c8f81effc 100644 --- a/lib/routes/bilibili/dynamic.ts +++ b/lib/routes/bilibili/dynamic.ts @@ -346,8 +346,7 @@ async function handler(ctx) { const emoji = node.emoji; description = description.replaceAll( emoji.text, - () => - `<img alt="${emoji.text}" src="${emoji.icon_url}" style="margin: -1px 1px 0px; display: inline-block; width: 20px; height: 20px; vertical-align: text-bottom;" title="" referrerpolicy="no-referrer">` + () => `<img alt="${emoji.text}" src="${emoji.icon_url}" style="margin: -1px 1px 0px; display: inline-block; width: 20px; height: 20px; vertical-align: text-bottom;" title="">` ); } // 处理转发带图评论的情况 @@ -355,10 +354,7 @@ async function handler(ctx) { const { pics, text } = node; description = description.replaceAll(text, () => pics - .map( - (pic) => - `<img alt="${text}" src="${pic.src}" style="margin: 0px 0px 0px; display: inline-block; width: ${pic.width}px; height: ${pic.height}px; vertical-align: text-bottom;" title="" referrerpolicy="no-referrer">` - ) + .map((pic) => `<img alt="${text}" src="${pic.src}" style="margin: 0px 0px 0px; display: inline-block; width: ${pic.width}px; height: ${pic.height}px; vertical-align: text-bottom;" title="">`) .join('<br>') ); } diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts index d4b966e91d1c..beeed2e81d4a 100644 --- a/lib/routes/bilibili/followings-dynamic.ts +++ b/lib/routes/bilibili/followings-dynamic.ts @@ -179,7 +179,7 @@ async function handler(ctx) { for (const item of emoji) { data_content = data_content.replaceAll( new RegExp(`\\${item.text}`, 'g'), - () => `<img alt="${item.text}" src="${item.url}"style="margin: -1px 1px 0px; display: inline-block; width: 20px; height: 20px; vertical-align: text-bottom;" title="" referrerpolicy="no-referrer">` + () => `<img alt="${item.text}" src="${item.url}"style="margin: -1px 1px 0px; display: inline-block; width: 20px; height: 20px; vertical-align: text-bottom;" title="">` ); } } diff --git a/lib/routes/bilibili/hot-search.ts b/lib/routes/bilibili/hot-search.ts index ad759fff45d8..2d5e193f75e6 100644 --- a/lib/routes/bilibili/hot-search.ts +++ b/lib/routes/bilibili/hot-search.ts @@ -38,9 +38,6 @@ async function handler() { const response = await got({ method: 'get', url, - headers: { - Referer: 'https://api.bilibili.com', - }, }); const trending = response?.data?.data?.trending; const title = trending?.title; diff --git a/lib/routes/bitget/announcement.ts b/lib/routes/bitget/announcement.ts index e9d070c61d13..623ce581a39f 100644 --- a/lib/routes/bitget/announcement.ts +++ b/lib/routes/bitget/announcement.ts @@ -96,12 +96,9 @@ const handler: Route['handler'] = async (ctx) => { link: item.openUrl ?? '', pubDate: item.sendTime ? date : undefined, description: item.content ?? '', + image: item.imgUrl, }; - if (item.imgUrl) { - dataItem.image = item.imgUrl; - } - if (item.stationLetterType === '01' || item.stationLetterType === '06') { try { const itemResponse = await ofetch<string>(item.openUrl ?? '', { diff --git a/lib/routes/bjwxdxh/index.ts b/lib/routes/bjwxdxh/index.ts index baec7979c1b0..5b7a58cfe138 100644 --- a/lib/routes/bjwxdxh/index.ts +++ b/lib/routes/bjwxdxh/index.ts @@ -62,7 +62,7 @@ async function handler(ctx) { .match(/作者:(\S*)\s+发布于:(\S*\s+.*?)\s/); item.author = info[1]; item.pubDate = timezone(parseDate(info[2], 'YYYY-MM-DD HH:mm:ss'), 8); - item.description = content('div#con').html().trim().replaceAll('\n', ''); + item.description = content('div#con').html().replaceAll('\n', ''); return item; }) ) diff --git a/lib/routes/bntnews/index.ts b/lib/routes/bntnews/index.ts index eb0f25f7ceb1..7de9a8ca43d2 100644 --- a/lib/routes/bntnews/index.ts +++ b/lib/routes/bntnews/index.ts @@ -78,7 +78,6 @@ async function handler(ctx) { // Remove ads $content.find('.googleBanner').remove(); - $content.find('script').remove(); $content.find('style').remove(); if ($content.length > 0) { diff --git a/lib/routes/buaa/news/index.ts b/lib/routes/buaa/news/index.ts index c8499f371a6d..6d2dbf064db4 100644 --- a/lib/routes/buaa/news/index.ts +++ b/lib/routes/buaa/news/index.ts @@ -54,7 +54,7 @@ async function handler(ctx: Context): Promise<Data> { const response = await got(item.link); const $ = load(response.data); - item.description = $('.v_news_content').html() || ''; + item.description = $('.v_news_content').html(); item.author = $('.vsbcontent_end').text().trim(); return item; diff --git a/lib/routes/bullionvault/gold-news.ts b/lib/routes/bullionvault/gold-news.ts index ff2cc9d09f05..381425b4d66a 100644 --- a/lib/routes/bullionvault/gold-news.ts +++ b/lib/routes/bullionvault/gold-news.ts @@ -66,7 +66,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('article.article h1').text(); - const description: string | undefined = $$('div.content').html() ?? ''; + const description = $$('div.content').html(); const pubDateStr: string | undefined = $$('div.submitted').text().split(/,/).pop(); const categories: string[] = $$('meta[name="news_keywords"]').attr('content')?.split(/,/) ?? []; const authorEls: Element[] = $$('div.view-author-bio').toArray(); diff --git a/lib/routes/bwsg/index.ts b/lib/routes/bwsg/index.ts index 51435d76f854..2b9cfc04a28e 100644 --- a/lib/routes/bwsg/index.ts +++ b/lib/routes/bwsg/index.ts @@ -43,7 +43,7 @@ RSS feed might not get all items. const $el = $(el); const link = el.attribs.href; const image = $el.find('.res_immobiliensuche__immobilien__item__thumb > img').attr('src'); - const title = $el.find('.res_immobiliensuche__immobilien__item__content__title').text().trim(); + const title = $el.find('.res_immobiliensuche__immobilien__item__content__title').text(); const location = $el.find('.res_immobiliensuche__immobilien__item__content__meta__location').text().trim(); const price = $el.find('.res_immobiliensuche__immobilien__item__content__meta__preis').text().trim(); const metadata = $el.find('.res_immobiliensuche__immobilien__item__content__meta__row_1').text().trim(); diff --git a/lib/routes/capitalmind/utils.ts b/lib/routes/capitalmind/utils.ts index 08fb49362067..2d34367fad9d 100644 --- a/lib/routes/capitalmind/utils.ts +++ b/lib/routes/capitalmind/utils.ts @@ -30,7 +30,7 @@ export async function fetchArticles(path) { // Fetch full article content const articleResponse = await ofetch(link); const $articlePage = load(articleResponse); - const $article = $articlePage('article').clone(); + const $article = $articlePage('article'); // Extract tags from footer const tags: string[] = $article @@ -52,7 +52,7 @@ export async function fetchArticles(path) { pubDate = $time.attr('datetime') || $time.text().trim(); } - const $content = $article.find('section[aria-label="Post content"]').clone(); + const $content = $article.find('section[aria-label="Post content"]'); // Remove footer $content.find('footer').remove(); @@ -98,9 +98,6 @@ export async function fetchArticles(path) { if (urlMatch && urlMatch[1]) { const originalUrl = decodeURIComponent(urlMatch[1]); $img.attr('src', originalUrl); - } else if (src.startsWith('/')) { - // Handle other relative URLs - $img.attr('src', baseUrl + src); } } }); diff --git a/lib/routes/capitalmuseum/exhibition.tsx b/lib/routes/capitalmuseum/exhibition.tsx index b540ab5c9b85..f3afd390326f 100644 --- a/lib/routes/capitalmuseum/exhibition.tsx +++ b/lib/routes/capitalmuseum/exhibition.tsx @@ -67,7 +67,7 @@ export const route: Route = { }); const $ = load(response.data); - const nuxtDataStr = $('#__NUXT_DATA__').text() || ''; // use __NUXT_DATA__ to get the data structure of the page + const nuxtDataStr = $('#__NUXT_DATA__').text(); // use __NUXT_DATA__ to get the data structure of the page const nuxtData = JSON.parse(nuxtDataStr); const targetType = typeMap[typeParam]; @@ -109,7 +109,7 @@ export const route: Route = { }); const detail$ = load(detailResponse.data); - const detailNuxtDataStr = detail$('#__NUXT_DATA__').text() || ''; + const detailNuxtDataStr = detail$('#__NUXT_DATA__').text(); const detailNuxtData = JSON.parse(detailNuxtDataStr); const detailObj = detailNuxtData.find((obj: any) => typeof obj === 'object' && 'address' in obj); diff --git a/lib/routes/cas/genetics/index.ts b/lib/routes/cas/genetics/index.ts index 842288e94b7e..9b0330d9cfb6 100644 --- a/lib/routes/cas/genetics/index.ts +++ b/lib/routes/cas/genetics/index.ts @@ -29,7 +29,7 @@ async function handler(ctx) { .map((item) => { item = $(item); const a = item.find('a').first(); - const date = item.find('.box-date').first(); + const date = item.find('.box-date'); return { title: a.text(), link: new URL(a.attr('href'), currentUrl).href, @@ -55,7 +55,7 @@ async function handler(ctx) { .map((item) => { item = $(item); const a = item.find('a').first(); - const date = item.find('.col-news-date').first(); + const date = item.find('.col-news-date'); return { title: a.text(), link: new URL(a.attr('href'), currentUrl).href, diff --git a/lib/routes/caus/index.ts b/lib/routes/caus/index.ts index 46d055d4bc53..b876c865085c 100644 --- a/lib/routes/caus/index.ts +++ b/lib/routes/caus/index.ts @@ -45,8 +45,7 @@ async function handler(ctx) { const query: Record<string, string | number> = { per_page: 20, - // Reason: using _embed=1 with many posts produces responses too large for ofetch to parse as JSON - _embed: 'author,wp:term', + _embed: '', }; if (cat.id) { query.categories = cat.id; diff --git a/lib/routes/ccagm/index.ts b/lib/routes/ccagm/index.ts index 86fdb8ae1f97..2d612486bc42 100644 --- a/lib/routes/ccagm/index.ts +++ b/lib/routes/ccagm/index.ts @@ -54,7 +54,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('h2.center').text(); - const description: string | undefined = $$('div.newsview').html() ?? undefined; + const description = $$('div.newsview').html(); const pubDateStr: string | undefined = $$('p.title_s').text().trim().split(/:/).pop(); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/cccmc/index.ts b/lib/routes/cccmc/index.ts index 188447cd7205..2fa8b9c72776 100644 --- a/lib/routes/cccmc/index.ts +++ b/lib/routes/cccmc/index.ts @@ -57,7 +57,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.title').text(); - const description: string = $$('div#article-content').html() ?? ''; + const description = $$('div#article-content').html(); const pubDateStr: string | undefined = $$('span.time').text().split(/:/).pop(); const authorEls: Element[] = $$('span.form, span.from').toArray(); const authors: DataItem['author'] = authorEls.map((authorEl) => { diff --git a/lib/routes/cdu/cdrw.ts b/lib/routes/cdu/cdrw.ts index 57c9336b3830..f43df4b2490a 100644 --- a/lib/routes/cdu/cdrw.ts +++ b/lib/routes/cdu/cdrw.ts @@ -44,7 +44,7 @@ async function handler() { // 优先使用title属性内容,避免内容被截断 const title = element.attr('title') || element.find('.tit').text().trim(); const link = element.attr('href'); - const dateText = element.find('.date').text().trim(); + const dateText = element.find('.date').text(); const pubDate = timezone(parseDate(dateText), 8); return { diff --git a/lib/routes/cdu/tzggcdunews.ts b/lib/routes/cdu/tzggcdunews.ts index 319cdf884865..932dd417b46a 100644 --- a/lib/routes/cdu/tzggcdunews.ts +++ b/lib/routes/cdu/tzggcdunews.ts @@ -44,7 +44,7 @@ async function handler() { // 优先使用title属性内容,避免内容被截断 const title = element.attr('title') || element.find('.tit').text().trim(); const link = element.attr('href'); - const dateText = element.find('.date').text().trim(); + const dateText = element.find('.date').text(); const pubDate = timezone(parseDate(dateText), 8); return { diff --git a/lib/routes/cefco/news.ts b/lib/routes/cefco/news.ts index 3a0ba1355eea..a8ca7280c4ae 100644 --- a/lib/routes/cefco/news.ts +++ b/lib/routes/cefco/news.ts @@ -37,7 +37,7 @@ async function handler() { title: a.text().trim(), link: new URL(a.attr('href')!, baseUrl).href, description: $item.find('dd.clamp').text(), - pubDate: timezone(parseDate($item.find('dd.time').text().trim(), 'YYYY/MM/DD'), 8), + pubDate: timezone(parseDate($item.find('dd.time').text(), 'YYYY/MM/DD'), 8), image: $item.find('a.imgbox img').attr('src') ? new URL($item.find('a.imgbox img').attr('src')!, baseUrl).href : undefined, }; }); diff --git a/lib/routes/ceph/blog.ts b/lib/routes/ceph/blog.ts index 155518378b05..70024f92c15b 100644 --- a/lib/routes/ceph/blog.ts +++ b/lib/routes/ceph/blog.ts @@ -42,7 +42,7 @@ async function handler(ctx: Context): Promise<Data> { .toArray() .map((e) => { const element = $(e); - const title = element.find('a').text().trim(); + const title = element.find('a').text(); const pubDate = parseDate(element.find('time').attr('datetime')); return { title, @@ -58,7 +58,7 @@ async function handler(ctx: Context): Promise<Data> { const data = itemReponse.data; const item$ = load(data); - item.author = item$('#main section > div:nth-child(1) span').text().trim(); + item.author = item$('#main section > div:nth-child(1) span').text(); item.description = item$('#main section > div:nth-child(2) > div').html(); return item; }) diff --git a/lib/routes/chikubi/utils.ts b/lib/routes/chikubi/utils.ts index e2b075b239f4..b5a6295f7165 100644 --- a/lib/routes/chikubi/utils.ts +++ b/lib/routes/chikubi/utils.ts @@ -70,7 +70,7 @@ function processDescription(description: string): string { return $('body') .children() .toArray() - .map((el) => $(el).clone().wrap('<div>').parent().html()) + .map((el) => $.html(el)) .join(''); } diff --git a/lib/routes/chinafactcheck/utils.ts b/lib/routes/chinafactcheck/utils.ts index 293a1ad6e90d..c902d8106fb4 100644 --- a/lib/routes/chinafactcheck/utils.ts +++ b/lib/routes/chinafactcheck/utils.ts @@ -32,14 +32,14 @@ const getArticleDetail = async (link) => { }); const $ = cleanDom(load(response.data)); - const title = $('.content-head h2').text().trim(); - const author = $('.content-persons p span:last').text().trim(); - const pubDate = parseDate($('.content-time').text().trim(), 'YYYY-MM-DD'); + const title = $('.content-head h2').text(); + const author = $('.content-persons p span:last').text(); + const pubDate = parseDate($('.content-time').text(), 'YYYY-MM-DD'); const description = $('div[class=content-list-box]').html(); const category = $('.content-tags a[rel="tag"]') .toArray() - .map((item) => $(item).text().trim()); + .map((item) => $(item).text()); return new ArticleDetail(title, author, pubDate, description, category); }; class ArticleDetail { diff --git a/lib/routes/chinaratings/credit-research.ts b/lib/routes/chinaratings/credit-research.ts index fae25399a999..054bffaf1041 100644 --- a/lib/routes/chinaratings/credit-research.ts +++ b/lib/routes/chinaratings/credit-research.ts @@ -55,7 +55,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.newshead h2, div.title h3').text(); - const description: string = $$('div.news div.content').html() ?? ''; + const description = $$('div.news div.content').html(); const metaStr: string = $$('div.newshead p span, div.title p span').text(); const pubDateStr: string | undefined = metaStr?.match(/(\d{4}-\d{2}-\d{2})/)?.[1]; diff --git a/lib/routes/chinatimes/index.ts b/lib/routes/chinatimes/index.ts index db4305191c6a..f526b586730b 100644 --- a/lib/routes/chinatimes/index.ts +++ b/lib/routes/chinatimes/index.ts @@ -52,14 +52,14 @@ async function handler(ctx) { const $item = $(item); const a = $item.find('.title a'); return { - title: a.text().trim(), + title: a.text(), link: `${baseUrl}${a.attr('href')}?chdtv`, guid: `${baseUrl}${a.attr('href')}`, pubDate: timezone(parseDate($item.find('time').attr('datetime')), 8), category: $item .find('.category a') .toArray() - .map((i) => $(i).text().trim()), + .map((i) => $(i).text()), }; }); @@ -85,7 +85,7 @@ async function handler(ctx) { ...item.category, ...$('.article-hash-tag a') .toArray() - .map((i) => $(i).text().trim()), + .map((i) => $(i).text()), ]), ]; diff --git a/lib/routes/chnmuseum/zl.tsx b/lib/routes/chnmuseum/zl.tsx index b8a533b024cf..c253d90e8363 100644 --- a/lib/routes/chnmuseum/zl.tsx +++ b/lib/routes/chnmuseum/zl.tsx @@ -113,7 +113,7 @@ const fetchTargetElements = async (cleanType: string, subtype: string | undefine const $item = $(el).closest('li'); if ($item.length > 0) { const rawLink = $(el).attr('href') || ''; - const rawZtzl = $(el).attr('ztzlurl')?.trim() || ''; // some exhibition links have a separate detailed page, use ztzlurl to get the detailed exhibition link if available + const rawZtzl = $(el).attr('ztzlurl') || ''; // some exhibition links have a separate detailed page, use ztzlurl to get the detailed exhibition link if available // Use exhibitionLink to remove the repeat ones const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); @@ -181,7 +181,7 @@ export const route: Route = { return cache.tryGet(itemLink, async () => { // for detailed exhibition page if available, different from base exhibition page. - const rawZtzl = aTag.attr('ztzlurl')?.trim() || ''; + const rawZtzl = aTag.attr('ztzlurl') || ''; const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); // title may not have full display on the page, use the img alt information instead @@ -198,8 +198,7 @@ export const route: Route = { .find((box) => box.find('p').first().text().includes(keyword)) ?.find('p') .last() - .text() - .trim() ?? ''; + .text() ?? ''; const location = findValue('地点'); let fullDuration = findValue('展期'); diff --git a/lib/routes/chongbuluo/index.ts b/lib/routes/chongbuluo/index.ts index db542bbb7624..faed6af4e366 100644 --- a/lib/routes/chongbuluo/index.ts +++ b/lib/routes/chongbuluo/index.ts @@ -41,13 +41,13 @@ async function handler() { .map(async (element) => { const item = $(element); const titleElement = item.find('th.common a.xst'); - const title = titleElement.text().trim(); + const title = titleElement.text(); const href = titleElement.attr('href') || ''; const threadLink = href.startsWith('http') ? href : `${baseUrl}/${href}`; - const author = item.find('td.by cite a').text().trim(); + const author = item.find('td.by cite a').text(); - const pubDateText = item.find('td.by em a span').attr('title') || item.find('td.by em a').text().trim(); + const pubDateText = item.find('td.by em a span').attr('title') || item.find('td.by em a').text(); const pubDate = parseDate(pubDateText); return await cache.tryGet(threadLink, async () => { @@ -56,7 +56,7 @@ async function handler() { const $thread = load(threadResponse); // 查找第一个帖子内容 - const content = $thread('.t_f').first().html()?.trim() || ''; + const content = $thread('.t_f').first().html()?.trim(); return { title, diff --git a/lib/routes/chuapp/chuapp.ts b/lib/routes/chuapp/chuapp.ts index f533c4e7a807..e888f1073090 100644 --- a/lib/routes/chuapp/chuapp.ts +++ b/lib/routes/chuapp/chuapp.ts @@ -127,9 +127,9 @@ async function handler(ctx: Context): Promise<Data | null> { const item: DataItem = { title: article.title, link: article.link, - description: s('.content .the-content').html() || '', + description: s('.content .the-content').html(), pubDate: parseDate(toJavaScriptTimestamp(s('.friendly_time').attr('data-time'))), - author: s('.author-time .fn-left').text() || '', + author: s('.author-time .fn-left').text(), }; return item; diff --git a/lib/routes/cjlu/yjsy/index.ts b/lib/routes/cjlu/yjsy/index.ts index 1d06a3d55586..e9de3c7f7a8c 100644 --- a/lib/routes/cjlu/yjsy/index.ts +++ b/lib/routes/cjlu/yjsy/index.ts @@ -13,22 +13,6 @@ const titleMap = new Map([ ['yjstz', '中量大研究生院 —— 研究生通知'], ['jstz', '中量大研究生院 —— 教师通知'], ]); -const headers = { - Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', - 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', - 'Cache-Control': 'max-age=0', - Connection: 'keep-alive', - Referer: 'https://yjsy.cjlu.edu.cn', - 'Sec-Fetch-Dest': 'document', - 'Sec-Fetch-Mode': 'navigate', - 'Sec-Fetch-Site': 'same-origin', - 'Sec-Fetch-User': '?1', - 'Upgrade-Insecure-Requests': '1', - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0', - 'sec-ch-ua': '"Microsoft Edge";v="141", "Not?A_Brand";v="8", "Chromium";v="141"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', -}; const allowedResourceTypes = new Set(['document', 'script']); @@ -88,7 +72,6 @@ async function handler(ctx) { const { page, destroy } = await getPlaywrightPage(url, { onBeforeLoad: async (page) => { - await page.setExtraHTTPHeaders(headers); await page.route('**/*', (route) => { const request = route.request(); allowedResourceTypes.has(request.resourceType()) ? route.continue() : route.abort(); @@ -136,7 +119,6 @@ async function handler(ctx) { const res = await ofetch(item.link, { responseType: 'text', headers: { - ...headers, Cookie: cookieString, Referer: url, }, diff --git a/lib/routes/claude/blog.ts b/lib/routes/claude/blog.ts index 50b2828eed45..a54d5aeac3bc 100644 --- a/lib/routes/claude/blog.ts +++ b/lib/routes/claude/blog.ts @@ -44,13 +44,13 @@ async function handler(ctx) { .slice(0, limit) .map((el) => { const $el = $(el); - const title = $el.find('.card_blog_list_title').text().trim(); + const title = $el.find('.card_blog_list_title').text(); const href = $el.find('a.clickable_link').attr('href') ?? ''; - const pubDateText = $el.find('[fs-list-fieldtype="date"][fs-list-field="date"]').text().trim(); + const pubDateText = $el.find('[fs-list-fieldtype="date"][fs-list-field="date"]').text(); const category = $el .find('[fs-list-field="category"]') .toArray() - .map((c) => $(c).text().trim()) + .map((c) => $(c).text()) .filter(Boolean); return { @@ -70,9 +70,9 @@ async function handler(ctx) { const content = $('.blog_post_content_wrap'); - content.find('style, script').remove(); + content.find('style').remove(); - item.description = content.html() ?? undefined; + item.description = content.html(); return item; }), diff --git a/lib/routes/claude/code-changelog.ts b/lib/routes/claude/code-changelog.ts index 167f71e7212a..46d74e262f1b 100644 --- a/lib/routes/claude/code-changelog.ts +++ b/lib/routes/claude/code-changelog.ts @@ -20,13 +20,13 @@ const handler = async (ctx: Context): Promise<Data> => { .toArray() .map((el): DataItem => { const $entry = $(el); - const version = $entry.find('[data-component-part="update-label"]').text().trim(); + const version = $entry.find('[data-component-part="update-label"]').text(); if (!version) { return null as unknown as DataItem; } - const dateText = $entry.find('[data-component-part="update-description"]').text().trim(); - const description = $entry.find('[data-component-part="update-content"]').html() ?? ''; + const dateText = $entry.find('[data-component-part="update-description"]').text(); + const description = $entry.find('[data-component-part="update-content"]').html(); const anchor = $entry.attr('id') ?? version.replaceAll('.', '-'); const link = `${targetUrl}#${anchor}`; diff --git a/lib/routes/cline/blog.ts b/lib/routes/cline/blog.ts index 4ae95ce50cea..723352b21ea2 100644 --- a/lib/routes/cline/blog.ts +++ b/lib/routes/cline/blog.ts @@ -15,12 +15,12 @@ function extractArticlesFromDOM($: CheerioAPI): DataItem[] { .map((article) => { const element = $(article); - const title = element.find('h2').text().trim(); + const title = element.find('h2').text(); const link = element.find('a').first().attr('href'); const fullLink = link ? (link.startsWith('http') ? link : `${rootUrl}${link.startsWith('/') ? link : `/${link}`}`) : ''; // Extract date and author with single regex - const metaText = element.find('.text-sm.text-slate-500').text().trim(); + const metaText = element.find('.text-sm.text-slate-500').text(); const metaMatch = metaText.match(/^([^•]+)•\s*([A-Z]+\s+\d{1,2},?\s+\d{4})/i); const author = metaMatch ? metaMatch[1].trim() : 'Cline Team'; const pubDate = metaMatch ? parseDate(metaMatch[2]) : undefined; diff --git a/lib/routes/cmu/andypavlo/blog.ts b/lib/routes/cmu/andypavlo/blog.ts index bdf87b6a832a..6813a38b26ad 100644 --- a/lib/routes/cmu/andypavlo/blog.ts +++ b/lib/routes/cmu/andypavlo/blog.ts @@ -18,9 +18,9 @@ async function getArticles() { const $description = $item.find('p'); return { - title: $title.text().trim(), + title: $title.text(), link: $title.attr('href'), - description: $description.text().trim(), + description: $description.text(), pubDate: parseDate($date.attr('title')), guid: $title.attr('href'), }; diff --git a/lib/routes/cnljxh/index.ts b/lib/routes/cnljxh/index.ts index 0af711fc0cc2..8d55f53fbbe8 100644 --- a/lib/routes/cnljxh/index.ts +++ b/lib/routes/cnljxh/index.ts @@ -54,7 +54,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.content_title h2').text(); - const description: string | undefined = $$('div.content_div').html() ?? ''; + const description = $$('div.content_div').html(); const authors: DataItem['author'] = $$('div.content_title p').text().split(/\s/, 1)[0]?.split(/:/).pop(); let processedItem: DataItem = { diff --git a/lib/routes/cnu/iec.ts b/lib/routes/cnu/iec.ts index 21fc4eb760b7..d2e65b088246 100644 --- a/lib/routes/cnu/iec.ts +++ b/lib/routes/cnu/iec.ts @@ -43,7 +43,7 @@ async function handler() { const a = item.find('a'); // 提取日期 [YYYY-MM-DD] - const dateText = span.text().trim(); + const dateText = span.text(); const dateMatch = dateText.match(/\[(\d{4}-\d{2}-\d{2})\]/); const pubDate = dateMatch ? parseDate(dateMatch[1], 'YYYY-MM-DD') : undefined; diff --git a/lib/routes/cnu/jdxw.ts b/lib/routes/cnu/jdxw.ts index c2ecd618b4a4..7067c52780a3 100644 --- a/lib/routes/cnu/jdxw.ts +++ b/lib/routes/cnu/jdxw.ts @@ -46,7 +46,7 @@ async function handler() { return { title: item.find('span.listTitle').text().trim(), link: linkUrl, - pubDate: parseDate(item.find('span.listDate').text().trim(), 'YYYY-MM-DD'), + pubDate: parseDate(item.find('span.listDate').text(), 'YYYY-MM-DD'), description: '', }; }); diff --git a/lib/routes/cnu/jwc.ts b/lib/routes/cnu/jwc.ts index a91ab883b6e2..300ef2989ffb 100644 --- a/lib/routes/cnu/jwc.ts +++ b/lib/routes/cnu/jwc.ts @@ -44,8 +44,8 @@ async function handler() { const linkUrl = href?.startsWith('http') ? href : `${baseUrl}/tzgg/${href}`; const dateSpan = item.find('span.date'); - const day = dateSpan.find('span.day').text().trim(); - const year = dateSpan.find('span.year').text().trim(); + const day = dateSpan.find('span.day').text(); + const year = dateSpan.find('span.year').text(); const pubDate = year && day ? parseDate(`${year}-${day}`, 'YYYY-MM-DD') : null; const categoryName = item.find('span.name').text().trim(); diff --git a/lib/routes/cnu/physics.ts b/lib/routes/cnu/physics.ts index 1c6b575158cf..c610c7df1773 100644 --- a/lib/routes/cnu/physics.ts +++ b/lib/routes/cnu/physics.ts @@ -44,7 +44,7 @@ async function handler() { const a = item.find('a'); // 提取日期 [YYYY-MM-DD] - const dateText = span.text().trim(); + const dateText = span.text(); const dateMatch = dateText.match(/\[(\d{4}-\d{2}-\d{2})\]/); const pubDate = dateMatch ? parseDate(dateMatch[1], 'YYYY-MM-DD') : undefined; diff --git a/lib/routes/cnu/smkxxy.ts b/lib/routes/cnu/smkxxy.ts index ea14a82c1786..e7461ce10951 100644 --- a/lib/routes/cnu/smkxxy.ts +++ b/lib/routes/cnu/smkxxy.ts @@ -45,7 +45,7 @@ async function handler() { return { title: item.find('p.gpArticleTitle').text().trim(), link: linkUrl, - pubDate: parseDate(item.find('span.gpArticleDate').text().trim(), 'YYYY-MM-DD'), + pubDate: parseDate(item.find('span.gpArticleDate').text(), 'YYYY-MM-DD'), description: '', }; }); diff --git a/lib/routes/cockroachlabs/blog.ts b/lib/routes/cockroachlabs/blog.ts index f23a108eea52..d433e6ce5101 100644 --- a/lib/routes/cockroachlabs/blog.ts +++ b/lib/routes/cockroachlabs/blog.ts @@ -67,7 +67,7 @@ async function handler(ctx) { // <article class="blog-content null"> // ..multiple <div>/<a>/<img>/<p>.. // </article> - const content = $('article.blog-content').html() || ''; + const content = $('article.blog-content').html(); // <div class="mt-4 flex flex-col items-center justify-center gap-1 sm:flex-row sm:gap-4"> // <div> diff --git a/lib/routes/cognition/blog.ts b/lib/routes/cognition/blog.ts index 4e52bd107f96..0df384cfa9eb 100644 --- a/lib/routes/cognition/blog.ts +++ b/lib/routes/cognition/blog.ts @@ -1,14 +1,14 @@ import { load } from 'cheerio'; -import type { DataItem, Route } from '@/types'; +import type { Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/blog/:category?', + path: '/blog', name: 'Blog', - url: 'cognition.ai/blog', + url: 'cognition.com/blog', maintainers: ['Loongphy', 'ttttmr'], example: '/cognition/blog', categories: ['programming'], @@ -23,90 +23,31 @@ export const route: Route = { }, radar: [ { - source: ['cognition.ai/blog/1', 'cognition.ai/blog/:category/1'], - target: '/blog/:category?', + source: ['cognition.com/blog'], }, ], view: ViewType.Articles, handler, - parameters: { - category: 'Category name, e.g., Research, Tutorials', - }, -}; - -const splitAuthors = (text: string | undefined): DataItem['author'] => { - if (!text) { - return undefined; - } - - const names = text - .split(',') - .map((name) => name.trim()) - .filter(Boolean); - - if (names.length === 0) { - return undefined; - } - - return names.map((name) => ({ - name, - })); }; -export async function handler(ctx) { - const baseUrl = 'https://cognition.ai'; - const { category } = ctx.req.param(); - const listPath = category ? `/blog/${category}/1` : '/blog/1'; - const targetUrl = new URL(listPath, baseUrl).href; +async function handler() { + const baseUrl = 'https://cognition.com'; + const targetUrl = `${baseUrl}/blog`; const html = await ofetch(targetUrl); const $ = load(html); - const items = $('#blog-post-list__list li.blog-post-list__list-item') + const items = $('section li a') .toArray() - .map((el) => { - const element = $(el); - const linkElement = element.find('a.o-blog-preview').first(); - - const href = linkElement.attr('href'); - const link = href ? new URL(href, baseUrl).href : undefined; - - if (!link) { - return; - } - - const title = linkElement.find('h3.o-blog-preview__title').text().trim(); - if (!title) { - return; - } - - const summary = linkElement.find('p.o-blog-preview__intro').text().trim(); - - const dateNode = linkElement.find('.o-blog-preview__meta-date').clone(); - dateNode.find('.o-blog-preview__meta').remove(); - const dateText = dateNode.text().trim(); - const authorText = linkElement.find('.o-blog-preview__meta-author').text().trim(); - - const dataItem: DataItem = { - title, - link, - pubDate: parseDate(dateText), + .map((item) => { + const $item = $(item); + + return { + title: $item.find('h2').text(), + link: new URL($item.attr('href')!, baseUrl).href, + description: $item.find('p').text(), + pubDate: parseDate($item.find('span').text(), 'MM.DD.YY'), }; - - if (summary) { - dataItem.description = summary; - } - - const authors = splitAuthors(authorText); - if (authors) { - dataItem.author = authors; - } - - return dataItem; - }) - .filter((item): item is DataItem => item !== undefined); - - const imageAttr = $('meta[property="og:image"]').attr('content'); - const image = imageAttr ? new URL(imageAttr, baseUrl).href : undefined; + }); return { title: $('title').text(), @@ -114,6 +55,6 @@ export async function handler(ctx) { link: targetUrl, allowEmpty: true, item: items, - image, + image: $('meta[property="og:image"]').attr('content'), }; } diff --git a/lib/routes/cointelegraph/index.ts b/lib/routes/cointelegraph/index.ts index 33bb1d2d1e71..3e5294ffaa5e 100644 --- a/lib/routes/cointelegraph/index.ts +++ b/lib/routes/cointelegraph/index.ts @@ -61,7 +61,7 @@ async function handler(): Promise<Data> { pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, link, author: item.creator || 'CoinTelegraph', - category: item.categories?.map((c) => c.trim()) || [], + category: item.categories || [], image: item.enclosure?.url, } as DataItem; }) diff --git a/lib/routes/comic-fuz/magazine.ts b/lib/routes/comic-fuz/magazine.ts index 37268378db8a..05109f06f787 100644 --- a/lib/routes/comic-fuz/magazine.ts +++ b/lib/routes/comic-fuz/magazine.ts @@ -34,7 +34,6 @@ export const route: Route = { const response = await ofetch(openUrl, { headers: { - Referer: 'https://comic-fuz.com/', 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8', }, }); @@ -68,7 +67,7 @@ export const route: Route = { thumb = `${imgUrl}${thumb}`; } - const rawDate = item.updatedDate ? item.updatedDate.replace(/\s*発売/, '').trim() : ''; + const rawDate = item.updatedDate ? item.updatedDate.replace(/\s*発売/, '') : ''; return { title: `${magazineTitle} - ${item.magazineIssueName}`, diff --git a/lib/routes/comic-fuz/manga.ts b/lib/routes/comic-fuz/manga.ts index c6df1c73bb2a..cb912747d10e 100644 --- a/lib/routes/comic-fuz/manga.ts +++ b/lib/routes/comic-fuz/manga.ts @@ -34,7 +34,6 @@ export const route: Route = { const response = await ofetch(openUrl, { headers: { - Referer: 'https://comic-fuz.com/', 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8', }, }); @@ -53,7 +52,7 @@ export const route: Route = { throw new Error('无法解析页面 Props 数据'); } - const mangaTitle = $('title').text().trim(); + const mangaTitle = $('title').text(); const mangaAuthor = pageProps.authorships?.map((item: any) => item.author?.authorName).join(', ') || ''; const mangaDescription = pageProps.manga?.longDescription || ''; diff --git a/lib/routes/comic-walker/manga.ts b/lib/routes/comic-walker/manga.ts index bbcbc9f4bec1..c6582943e0f1 100644 --- a/lib/routes/comic-walker/manga.ts +++ b/lib/routes/comic-walker/manga.ts @@ -41,7 +41,6 @@ export const route: Route = { const response = await ofetch<string>(fetchUrl, { headers: { - Referer: baseUrl, 'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8', }, }); @@ -69,8 +68,8 @@ export const route: Route = { throw new Error('成功获取数据对象,但未找到作品基本信息'); } - const mangaTitle = work.title || $('title').text().trim(); - const mangaAuthor = work.authors?.map((author: any) => author.name).join(', ') || ''; + const mangaTitle = work.title || $('title').text(); + const mangaAuthor = work.authors?.map((author: any) => author.name).join(', '); const mangaDescription = work.summary || ''; const coverImage = work.bookCover || work.thumbnail; diff --git a/lib/routes/comicat/search.ts b/lib/routes/comicat/search.ts index 3c013955a6c9..a0fd35817676 100644 --- a/lib/routes/comicat/search.ts +++ b/lib/routes/comicat/search.ts @@ -40,7 +40,7 @@ async function handler(ctx) { .map((item) => ({ title: $(item).find('td:nth-child(3)').text().trim(), link: `${baseUrl}/${$(item).find('td:nth-child(3) a').attr('href')}`, - category: $(item).find('td:nth-child(2)').text().trim(), + category: $(item).find('td:nth-child(2)').text(), author: $(item).find('td:nth-child(8)').text().trim(), })); @@ -51,7 +51,7 @@ async function handler(ctx) { const $ = load(response); item.pubDate = parseDate($('div.main > div.slayout > div > div.c1 > div:nth-child(1) > div > p:nth-child(4)').text().split('发布时间: ', 2)[1]); - const marginLink = `magnet:?xt=urn:btih:${$('#text_hash_id').text().split(',特征码:', 2)[1]}`.trim(); + const marginLink = `magnet:?xt=urn:btih:${$('#text_hash_id').text().split(',特征码:', 2)[1]}`; item.enclosure_url = marginLink; item.enclosure_type = 'application/x-bittorrent'; item.description = $('#btm > div.main > div.slayout > div > div.c2 > div:nth-child(1) > div.intro').html(); diff --git a/lib/routes/cool18/index.ts b/lib/routes/cool18/index.ts index 189fe1aa4077..15e6fbfd8c54 100644 --- a/lib/routes/cool18/index.ts +++ b/lib/routes/cool18/index.ts @@ -142,7 +142,7 @@ function extractGlobalSearchList($: CheerioAPI, limit: number): DataItem[] { .trim() .replaceAll(/[【】]/g, ''); const title = $pElements.filter('.lf').eq(1).text().trim(); - const dateText = $pElements.filter('.lr').text().trim(); + const dateText = $pElements.filter('.lr').text(); return { title: title || $link.text().trim(), diff --git a/lib/routes/coolidge/news.ts b/lib/routes/coolidge/news.ts index 3209db5e887c..41539e271c71 100644 --- a/lib/routes/coolidge/news.ts +++ b/lib/routes/coolidge/news.ts @@ -10,17 +10,17 @@ const handler = async () => { const html = await ofetch(link); const $ = load(html); - const container = $('#block-coolidge-content > div > div > div.view-content').first(); + const container = $('#block-coolidge-content > div > div > div.view-content'); const elements = container.find('div.news-item').toArray(); const items = elements.map((el) => { const element = $(el); - const titleEl = element.find('h2.news-item__title > a').first(); - const title = titleEl.text().trim(); + const titleEl = element.find('h2.news-item__title > a'); + const title = titleEl.text(); const href = titleEl.attr('href'); - const descriptionText = element.find('div.news-item__content > p').first().text().trim(); - const imageSrc = element.find('div.news-item__image img').first().attr('src'); + const descriptionText = element.find('div.news-item__content > p').first().text(); + const imageSrc = element.find('div.news-item__image img').attr('src'); const absoluteLink = href ? new URL(href, link).href : undefined; const absoluteImage = imageSrc ? new URL(imageSrc, link).href : undefined; diff --git a/lib/routes/cosplaytele/article.ts b/lib/routes/cosplaytele/article.ts index a6d370843584..08285007184e 100644 --- a/lib/routes/cosplaytele/article.ts +++ b/lib/routes/cosplaytele/article.ts @@ -7,8 +7,8 @@ export const loadArticle = async (link) => { const resp = await got(link); const article = load(resp.body); - const title = article('h1.entry-title').text().trim(); - const description = article('.entry-content').html() ?? ''; + const title = article('h1.entry-title').text(); + const description = article('.entry-content').html(); const pubDate = parseDate(article('time')[0].attribs.datetime); return { diff --git a/lib/routes/costar/press-releases.ts b/lib/routes/costar/press-releases.ts index 2f192d07a287..c3440778375e 100644 --- a/lib/routes/costar/press-releases.ts +++ b/lib/routes/costar/press-releases.ts @@ -28,7 +28,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $aEl: Cheerio<Element> = $el.find('a.coh-link').first(); const title: string = $aEl.text(); - const description: string | undefined = $el.find('div.coh-container').eq(3).html() ?? undefined; + const description = $el.find('div.coh-container').eq(3).html(); const pubDateStr: string | undefined = $el.find('div.coh-container').eq(4).text(); const linkUrl: string | undefined = $aEl.attr('href'); const categoryEls: Element[] = $el.find('div.coh-style-tags a').toArray(); @@ -63,7 +63,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('h1.coh-heading').text(); - const description: string | undefined = $$('div.coh-body').html() ?? item.description; + const description: string | null | undefined = $$('div.coh-body').html() ?? item.description; const pubDateStr: string | undefined = detailResponse.match(/"datePublished": "(.*?)",/)?.[1]; const upDatedStr: string | undefined = detailResponse.match(/"dateModified": "(.*?)",/)?.[1]; diff --git a/lib/routes/crush/index.ts b/lib/routes/crush/index.ts index f45b4855eb8d..d019ceaa75c9 100644 --- a/lib/routes/crush/index.ts +++ b/lib/routes/crush/index.ts @@ -46,10 +46,10 @@ async function handler(ctx): Promise<Data> { const $el = $(el); const p1 = $el.find('.p-1').first(); - const description = (p1.text() || '').trim(); + const description = p1.text().trim(); const publishedDiv = $el.children('div').last(); - const publishedRaw = (publishedDiv.text() || '').trim(); + const publishedRaw = publishedDiv.text(); // Example // Published at: September 20, 2025 12:44:36 PM diff --git a/lib/routes/csust/tggs.ts b/lib/routes/csust/tggs.ts index 6da0a2579cc5..cf08187db034 100644 --- a/lib/routes/csust/tggs.ts +++ b/lib/routes/csust/tggs.ts @@ -21,7 +21,7 @@ async function handler(): Promise<Data> { const $li = $(li); return { - title: $li.find('.newTitle').text().trim(), + title: $li.find('.newTitle').text(), link: new URL($li.find('a').attr('href')!, baseUrl).href, pubDate: timezone(parseDate($li.find('.data1').text().trim(), '发布时间 : YYYY-MM-DD'), 8), }; diff --git a/lib/routes/csust/utils.ts b/lib/routes/csust/utils.ts index ef9638ac1578..5913d0e97927 100644 --- a/lib/routes/csust/utils.ts +++ b/lib/routes/csust/utils.ts @@ -11,12 +11,7 @@ export async function getNoticeContent(item: NoticeItem): Promise<NoticeItem> { const pageTitle = $('title').text(); const $content = $('.v_news_content'); - $content.find('script, style, .vsbcontent_end').remove(); - $content.find('img[src], a[href]').each((_, element) => { - const $element = $(element); - const attribute = element.tagName === 'img' ? 'src' : 'href'; - $element.attr(attribute, new URL($element.attr(attribute)!, item.link).href); - }); + $content.find('style, .vsbcontent_end').remove(); return { ...item, diff --git a/lib/routes/csust/xkxs.ts b/lib/routes/csust/xkxs.ts index 2149799f52b1..c3ec76854adb 100644 --- a/lib/routes/csust/xkxs.ts +++ b/lib/routes/csust/xkxs.ts @@ -21,7 +21,7 @@ async function handler(): Promise<Data> { const $li = $(li); return { - title: $li.find('.newTitle').text().trim(), + title: $li.find('.newTitle').text(), link: new URL($li.find('a').attr('href')!, baseUrl).href, pubDate: timezone(parseDate($li.find('.data1').text().trim(), '发布时间 : YYYY-MM-DD'), 8), }; diff --git a/lib/routes/cugb/jwc.ts b/lib/routes/cugb/jwc.ts index 1c39123b2eec..89608df09a69 100644 --- a/lib/routes/cugb/jwc.ts +++ b/lib/routes/cugb/jwc.ts @@ -48,11 +48,11 @@ async function handler(ctx): Promise<Data> { .toArray() .map((el) => { const item = $(el); - const title = item.find('.list_con_main').text().trim(); + const title = item.find('.list_con_main').text(); return { title, link: new URL(item.attr('href') as string, rootUrl).href, - pubDate: timezone(parseDate(item.find('.list_con_time').text().trim()), 8), + pubDate: timezone(parseDate(item.find('.list_con_time').text()), 8), }; }) .filter((item) => item.title) @@ -63,7 +63,7 @@ async function handler(ctx): Promise<Data> { cache.tryGet(item.link as string, async () => { const { data: detailResponse } = await got(item.link as string); const content = load(detailResponse); - item.description = content('div.detail_content_box').html() ?? ''; + item.description = content('div.detail_content_box').html(); return item; }) ) diff --git a/lib/routes/cugb/news.ts b/lib/routes/cugb/news.ts index 792e63c5667e..3deab0b57d79 100644 --- a/lib/routes/cugb/news.ts +++ b/lib/routes/cugb/news.ts @@ -52,11 +52,10 @@ async function handler(ctx): Promise<Data> { const [date, author] = item .find('span') .text() - .trim() .split('|') .map((s) => s.trim()); return { - title: item.find('h3.tit').text().trim(), + title: item.find('h3.tit').text(), link: new URL(item.attr('href') as string, rootUrl).href, pubDate: timezone(parseDate(date), 8), author, @@ -68,7 +67,7 @@ async function handler(ctx): Promise<Data> { cache.tryGet(item.link as string, async () => { const { data: detailResponse } = await got(item.link as string); const content = load(detailResponse); - item.description = content('div.postbody').html() ?? ''; + item.description = content('div.postbody').html(); return item; }) ) diff --git a/lib/routes/cuilingmag/index.ts b/lib/routes/cuilingmag/index.ts index e2e612f9503b..d4171c079697 100644 --- a/lib/routes/cuilingmag/index.ts +++ b/lib/routes/cuilingmag/index.ts @@ -26,7 +26,7 @@ export const handler = async (ctx) => { .map((item) => { item = $(item); - const title = item.find('h3.new-list-h3, h3.title-font').first().text().trim(); + const title = item.find('h3.new-list-h3, h3.title-font').text().trim(); const src = item.find('img').first().prop('src'); const image = src ? new URL(src, rootUrl).href : undefined; diff --git a/lib/routes/cursor/blog.ts b/lib/routes/cursor/blog.ts index d73e83fe0d39..6db1ea037604 100644 --- a/lib/routes/cursor/blog.ts +++ b/lib/routes/cursor/blog.ts @@ -28,9 +28,9 @@ export const handler = async (ctx: Context): Promise<Data> => { const $el = $(el); const $link = $el.find('a').first(); - const title = $link.find('p').first().text().trim(); - const description = $link.find('p').eq(1).text().trim(); - const pubDate = parseDate($el.find('time').first().text().trim()); + const title = $link.find('p').first().text(); + const description = $link.find('p').eq(1).text(); + const pubDate = parseDate($el.find('time').first().text()); const href = $link.attr('href'); const link = href ? new URL(href, baseUrl).href : undefined; diff --git a/lib/routes/cursor/changelog.ts b/lib/routes/cursor/changelog.ts index a3965126ba24..496069982de5 100644 --- a/lib/routes/cursor/changelog.ts +++ b/lib/routes/cursor/changelog.ts @@ -29,11 +29,11 @@ export const handler = async (ctx: Context): Promise<Data> => { const $el: Cheerio<Element> = $(el); const timeEl = $el.find('time').first(); - const pubDateStr = timeEl.attr('datetime') || timeEl.text().trim(); - const versionLabel = timeEl.closest('a').find('.label').text().trim(); + const pubDateStr = timeEl.attr('datetime') || timeEl.text(); + const versionLabel = timeEl.closest('a').find('.label').text(); - const linkEl = $el.find('h1 a').first(); - const titleText = linkEl.length ? linkEl.text().trim() : $el.find('h1').first().text().trim(); + const linkEl = $el.find('h1 a'); + const titleText = linkEl.length ? linkEl.text() : $el.find('h1').text(); const title: string = versionLabel ? `[${versionLabel}] ${titleText}` : titleText; const linkUrl: string | undefined = linkEl.attr('href'); @@ -42,7 +42,7 @@ export const handler = async (ctx: Context): Promise<Data> => { guid = `cursor-changelog-${versionLabel}`; } - const description: string = $el.find('.prose').html() || ''; + const description = $el.find('.prose').html(); const processedItem: DataItem = { title, diff --git a/lib/routes/daoxuan/rss.ts b/lib/routes/daoxuan/rss.ts index d11710e1aab6..cf889b20246b 100644 --- a/lib/routes/daoxuan/rss.ts +++ b/lib/routes/daoxuan/rss.ts @@ -27,8 +27,8 @@ async function handler() { .toArray() .map((item) => { item = $(item); - const a = item.find('a.article-title').first(); - const timeElement = item.find('time').first(); + const a = item.find('a.article-title'); + const timeElement = item.find('time'); return { title: a.attr('title'), link: `https://daoxuan.cc${a.attr('href')}`, diff --git a/lib/routes/ddosi/index.ts b/lib/routes/ddosi/index.ts index e1e20c0a12b4..441c972e9486 100644 --- a/lib/routes/ddosi/index.ts +++ b/lib/routes/ddosi/index.ts @@ -25,9 +25,6 @@ async function handler() { const response = await got({ method: 'get', url, - headers: { - Referer: url, - }, headerGeneratorOptions: PRESETS.MODERN_IOS, }); const $ = load(response.data); diff --git a/lib/routes/decrypt/index.ts b/lib/routes/decrypt/index.ts index 78fef7c0ea2b..a64b3d8500e5 100644 --- a/lib/routes/decrypt/index.ts +++ b/lib/routes/decrypt/index.ts @@ -2,7 +2,6 @@ import { load } from 'cheerio'; import type { Data, Route } from '@/types'; import cache from '@/utils/cache'; -import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import parser from '@/utils/rss-parser'; @@ -48,33 +47,17 @@ async function handler(ctx): Promise<Data> { return {}; } - try { - const result = await extractFullText(item.link); - return { - title: item.title || 'Untitled', - link: item.link.split('?', 1)[0], // Clean URL by removing query parameters - pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, - description: result?.fullText ?? (item.content || ''), - author: item.creator || 'Decrypt', - category: result?.tags ? [...new Set([...(item.categories ?? []), ...result.tags])] : item.categories || [], - guid: item.guid || item.link, - image: result?.featuredImage ?? item.enclosure?.url, - }; - } catch (error: any) { - logger.warn(`Couldn't fetch full content for ${item.link}: ${error.message}`); - - // Fallback to RSS content - return { - title: item.title || 'Untitled', - link: item.link.split('?', 1)[0], - pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, - description: item.content || '', - author: item.creator || 'Decrypt', - category: item.categories || [], - guid: item.guid || item.link, - image: item.enclosure?.url, - }; - } + const result = await extractFullText(item.link); + return { + title: item.title || 'Untitled', + link: item.link.split('?', 1)[0], // Clean URL by removing query parameters + pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, + description: result?.fullText ?? item.content, + author: item.creator || 'Decrypt', + category: result?.tags ? [...new Set([...(item.categories ?? []), ...result.tags])] : item.categories || [], + guid: item.guid || item.link, + image: result?.featuredImage ?? item.enclosure?.url, + }; }) ) ); @@ -90,27 +73,22 @@ async function handler(ctx): Promise<Data> { } async function extractFullText(url: string): Promise<{ fullText: string; featuredImage: string; tags: string[] } | null> { - try { - const response = await ofetch(url); + const response = await ofetch(url); - const $ = load(response); + const $ = load(response); - const nextData = JSON.parse($('script#__NEXT_DATA__').text()); - const post = nextData.props.pageProps.post; + const nextData = JSON.parse($('script#__NEXT_DATA__').text()); + const post = nextData.props.pageProps.post; - if (post.content.length) { - const fullText = `<img src="${post.featuredImage.src}" alt="${post.featuredImage.alt}">` + post.content; + if (post.content.length) { + const fullText = `<img src="${post.featuredImage.src}" alt="${post.featuredImage.alt}">` + post.content; - return { - fullText, - featuredImage: post.featuredImage.src, - tags: post.tags.data.map((tag) => tag.name), - }; - } - - return null; - } catch (error) { - logger.error(`Error extracting full text from ${url}: ${error}`); - return null; + return { + fullText, + featuredImage: post.featuredImage.src, + tags: post.tags.data.map((tag) => tag.name), + }; } + + return null; } diff --git a/lib/routes/denonbu/news.ts b/lib/routes/denonbu/news.ts index 01baaefbe63f..889b501d9a4f 100644 --- a/lib/routes/denonbu/news.ts +++ b/lib/routes/denonbu/news.ts @@ -178,6 +178,7 @@ async function handler(ctx: Context): Promise<Data> { description: body, pubDate: timezone(parseDate(post_date), 9), category: category.map((x) => x.name), + link: link ?? undefined, }; if (media?.[0]) { @@ -187,9 +188,6 @@ async function handler(ctx: Context): Promise<Data> { result.image = imageUrl; } } - if (link) { - result.link = link; - } return result; }); diff --git a/lib/routes/dev.to/guides.ts b/lib/routes/dev.to/guides.ts index b4ad5ed216e8..70da6228fa3b 100644 --- a/lib/routes/dev.to/guides.ts +++ b/lib/routes/dev.to/guides.ts @@ -56,7 +56,7 @@ async function handler() { const coverImage = $article('.crayons-article__cover img').attr('src'); // Extract article content - const content = $article('.crayons-article__body').html() || ''; + const content = $article('.crayons-article__body').html(); // Extract author info const authorName = $article('.crayons-article__header__meta .fw-bold').first().text().trim(); @@ -69,7 +69,7 @@ async function handler() { // Extract tags const tags = $article('.spec__tags .crayons-tag') .toArray() - .map((tag) => $(tag).text().trim().replace('#', '')); + .map((tag) => $(tag).text().replace('#', '')); return { title: item.title, diff --git a/lib/routes/dev.to/top.ts b/lib/routes/dev.to/top.ts index 1542d57e0d06..390a4928cfe0 100644 --- a/lib/routes/dev.to/top.ts +++ b/lib/routes/dev.to/top.ts @@ -74,7 +74,7 @@ async function handler(ctx) { const coverImage = $('.crayons-article__cover img').attr('src'); // Extract article content - const content = $('.crayons-article__body').html() || ''; + const content = $('.crayons-article__body').html(); return { title: item.title, diff --git a/lib/routes/dgut/jwb.ts b/lib/routes/dgut/jwb.ts index 2f31cf7606f4..98ba32e7c520 100644 --- a/lib/routes/dgut/jwb.ts +++ b/lib/routes/dgut/jwb.ts @@ -46,7 +46,7 @@ async function handler(ctx) { const $a = $li.find('a.con'); return { title: $a.find('.tit').text().trim(), - pubDate: parseDate(`${$a.find('.year').text().trim()}-${$a.find('.day').text().trim()}`), + pubDate: parseDate(`${$a.find('.year').text()}-${$a.find('.day').text()}`), link: `${baseurl}${$a.attr('href')}`, }; }); diff --git a/lib/routes/dhu/jiaowu/news.ts b/lib/routes/dhu/jiaowu/news.ts index 593213564fb2..1b57f5ab09bf 100644 --- a/lib/routes/dhu/jiaowu/news.ts +++ b/lib/routes/dhu/jiaowu/news.ts @@ -64,7 +64,7 @@ async function handler(ctx) { try { const { data: response } = await got(url); const $ = load(response); - description = $('.wp_articlecontent').first().html() ?? ''; + description = $('.wp_articlecontent').html(); } catch { description = ''; } diff --git a/lib/routes/dhu/news/xsxx.ts b/lib/routes/dhu/news/xsxx.ts index 4db0e2f7941b..33ff11393b64 100644 --- a/lib/routes/dhu/news/xsxx.ts +++ b/lib/routes/dhu/news/xsxx.ts @@ -55,7 +55,7 @@ async function handler() { cache.tryGet(item.link, async () => { const { data: response } = await got(item.link); const $ = load(response); - item.description = $('.new_zwCot').first().html(); + item.description = $('.new_zwCot').html(); return item; }) ) diff --git a/lib/routes/dhu/xxgk/news.ts b/lib/routes/dhu/xxgk/news.ts index fc98fd30c698..a85a6709d36e 100644 --- a/lib/routes/dhu/xxgk/news.ts +++ b/lib/routes/dhu/xxgk/news.ts @@ -55,7 +55,7 @@ async function handler() { try { const { data: response } = await got(url); const $ = load(response); - description = $('.wp_articlecontent').first().html() ?? ''; + description = $('.wp_articlecontent').html(); } catch { description = ''; } diff --git a/lib/routes/dhu/yjs/news.ts b/lib/routes/dhu/yjs/news.ts index ef1eae126bb7..b526f1823e31 100644 --- a/lib/routes/dhu/yjs/news.ts +++ b/lib/routes/dhu/yjs/news.ts @@ -57,7 +57,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { const { data: response } = await got(item.link); const $ = load(response); - item.description = $('.wp_articlecontent').first().html(); + item.description = $('.wp_articlecontent').html(); return item; }) ) diff --git a/lib/routes/diariofruticola/filtro.ts b/lib/routes/diariofruticola/filtro.ts index 44bbbb882419..97ea630c3469 100644 --- a/lib/routes/diariofruticola/filtro.ts +++ b/lib/routes/diariofruticola/filtro.ts @@ -51,7 +51,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('h1.my-2').text(); - const description: string | undefined = $$('div.ck-content').html() ?? ''; + const description = $$('div.ck-content').html(); const pubDateStr: string | undefined = detailResponse.match(/"datePublished":\s"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})"/)?.[1] ?? undefined; const upDatedStr: string | undefined = detailResponse.match(/"dateModified":\s"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})"/)?.[1] ?? undefined; diff --git a/lib/routes/digitalpolicyalert/activity-tracker.ts b/lib/routes/digitalpolicyalert/activity-tracker.ts index aa62d9c49da0..063cb3c5bc13 100644 --- a/lib/routes/digitalpolicyalert/activity-tracker.ts +++ b/lib/routes/digitalpolicyalert/activity-tracker.ts @@ -46,9 +46,9 @@ export const handler = async (ctx: Context): Promise<Data> => { const $: CheerioAPI = load(targetResponse); const language = $('html').attr('lang') ?? 'en'; - const items: DataItem[] = (response.results ?? []).slice(0, limit).map((item): DataItem => { + const items: DataItem[] = response.results.slice(0, limit).map((item): DataItem => { const title: string = item.title; - const description: string | undefined = item.latest_event?.description ?? undefined; + const description: string | undefined = item.latest_event?.description; const pubDate: number | string = item.latest_event?.date; const linkUrl: string | undefined = item.slug ? `change/${item.slug}` : undefined; const categories: string[] = [ @@ -60,12 +60,11 @@ export const handler = async (ctx: Context): Promise<Data> => { item.type?.name, ]), ].filter(Boolean); - const authors: DataItem['author'] = - item.implementers?.map((author) => ({ - name: author.name, - url: undefined, - avatar: undefined, - })) ?? undefined; + const authors: DataItem['author'] = item.implementers?.map((author) => ({ + name: author.name, + url: undefined, + avatar: undefined, + })); const updated: number | string = pubDate; const processedItem: DataItem = { diff --git a/lib/routes/dnaindia/common.ts b/lib/routes/dnaindia/common.ts index 045efc462cfe..e67bc1d07fc2 100644 --- a/lib/routes/dnaindia/common.ts +++ b/lib/routes/dnaindia/common.ts @@ -54,7 +54,6 @@ export async function handler(ctx) { // Process description const description = $('div.article-description') - .clone() .children('div') .remove() .end() diff --git a/lib/routes/douban/other/later.ts b/lib/routes/douban/other/later.ts index f097ac2d9754..61fcdc49e18f 100644 --- a/lib/routes/douban/other/later.ts +++ b/lib/routes/douban/other/later.ts @@ -33,8 +33,8 @@ async function handler() { .map((ele) => { const description = $(ele).html(); const name = $('h3', ele).text().trim(); - const date = $('ul li', ele).eq(0).text().trim(); - const type = $('ul li', ele).eq(1).text().trim(); + const date = $('ul li', ele).eq(0).text(); + const type = $('ul li', ele).eq(1).text(); const link = $('a.thumb', ele).attr('href'); return { diff --git a/lib/routes/douban/people/status.ts b/lib/routes/douban/people/status.ts index cf7b8cdcd284..f9cd3ae8c9df 100644 --- a/lib/routes/douban/people/status.ts +++ b/lib/routes/douban/people/status.ts @@ -51,8 +51,6 @@ export const route: Route = { <img loading="lazy" src="/img/readable-douban.png" alt="豆瓣读书的可读豆瓣广播 RSS" />`, }; -const headers = { Referer: 'https://m.douban.com/' }; - function tryFixStatus(status) { let result = { isFixSuccess: true, why: '' }; const now = new Date(); @@ -491,7 +489,7 @@ async function getFullTextItems(items) { } else { const { data: { text }, - } = await got({ url, headers }); + } = await got(url); cache.set(url, text); item.status.text = text; } @@ -508,7 +506,7 @@ async function getFullTextItems(items) { // 存在reshared_status字段正常,但尝试获取时返回403的情况。比如原po被炸号就可能这样。 const { data: { text }, - } = await got({ url, headers }); + } = await got(url); cache.set(url, text); item.status.reshared_status.text = text; } catch { @@ -525,7 +523,7 @@ async function handler(ctx) { const items = await cache.tryGet( url, async () => { - const _r = await got({ url, headers }); + const _r = await got(url); return _r.data.items; }, config.cache.routeExpire, diff --git a/lib/routes/dpm/exhibitions.tsx b/lib/routes/dpm/exhibitions.tsx index dd9053744005..79d77388b6fc 100644 --- a/lib/routes/dpm/exhibitions.tsx +++ b/lib/routes/dpm/exhibitions.tsx @@ -76,7 +76,7 @@ export const route: Route = { const title = $item.find('a.aa').attr('title') || ''; // Filter out 结束 or 暂闭 status exhibition - const status = $item.find('.label').text().trim(); + const status = $item.find('.label').text(); if (status.includes('结束') || status.includes('暂闭')) { return null; } diff --git a/lib/routes/dw/utils.tsx b/lib/routes/dw/utils.tsx index 15eb2309f54c..25e52db68081 100644 --- a/lib/routes/dw/utils.tsx +++ b/lib/routes/dw/utils.tsx @@ -209,9 +209,7 @@ const processContent = (item, content) => { liveblog, imageI18n: i18n('Image', item.language), }); - if (content.trackingCategories) { - item.category = content.trackingCategories; - } + item.category = content.trackingCategories; if (content.firstPersonArray) { item.author = content.firstPersonArray.map((person) => person.fullName).join(', '); } diff --git a/lib/routes/dykszx/news.ts b/lib/routes/dykszx/news.ts index 2199dfe865d6..6632fc921caa 100644 --- a/lib/routes/dykszx/news.ts +++ b/lib/routes/dykszx/news.ts @@ -6,20 +6,20 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -const HOST = 'https://www.dykszx.com'; +const HOST = 'https://www.dykszx.cn'; const getContent = async (href) => { const newsPage = `${HOST}${href}`; const response = await got.get(newsPage); const $ = load(response.data); const newsTime = - $('body > div:nth-child(3) > div.page.w > div.shuxing.w') + $('.shuxing') .text() .trim() .match(/时间:(.*?)点击/g)?.[0] || ''; // 移除二维码 $('.sjlook').remove(); - const content = $('#show-body').html() || ''; + const content = $('#show-body').html(); return { newsTime, content, newsPage }; }; @@ -75,14 +75,14 @@ export const route: Route = { }, radar: [ { - source: ['www.dykszx.com/'], + source: ['www.dykszx.cn/'], target: '/news/all', }, ], name: '考试新闻发布', maintainers: ['zytomorrow'], handler, - url: 'www.dykszx.com', + url: 'www.dykszx.cn', description: `| 新闻中心 | 公务员考试 | 事业单位 | (职)业资格、职称考试 | 其他 | | :------: | :--------: | :------: | :--------------------: | :---: | | all | gwy | sydw | zyzc | other |`, diff --git a/lib/routes/ecnu/art.ts b/lib/routes/ecnu/art.ts index d4069f729a51..337222925494 100644 --- a/lib/routes/ecnu/art.ts +++ b/lib/routes/ecnu/art.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/bksy.ts b/lib/routes/ecnu/bksy.ts index 71f45cc62684..b8718061b5dd 100644 --- a/lib/routes/ecnu/bksy.ts +++ b/lib/routes/ecnu/bksy.ts @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) diff --git a/lib/routes/ecnu/cee.ts b/lib/routes/ecnu/cee.ts index 72910d18ee40..bedb8aae5cd6 100644 --- a/lib/routes/ecnu/cee.ts +++ b/lib/routes/ecnu/cee.ts @@ -12,23 +12,23 @@ export const route: Route = { example: '/ecnu/cee', radar: [ { - source: ['cee.ecnu.edu.cn'], + source: ['ieeic.ecnu.edu.cn'], target: '/cee', }, ], name: '通信与电子工程学院通知公告', maintainers: ['FrozenStarrrr', 'ChiyoYuki', 'ECNU-minus'], handler: async () => { - const baseUrl = 'https://cee.ecnu.edu.cn/'; + const baseUrl = 'https://ieeic.ecnu.edu.cn/'; const response = await got(`${baseUrl}tzgg_4170/list.htm`); const $ = load(response.data); const links = $('ul.news_list.list2 > li') .toArray() .map((el) => ({ - pubDate: timezone(parseDate($(el).find('.news_meta').text()), 8), + pubDate: timezone(parseDate($(el).find('.news_date').text()), 8), link: new URL($(el).find('a').attr('href'), baseUrl).href, - title: $(el).find('a').text(), + title: $(el).find('.news_title').text(), })); const items = await Promise.all( links.map((item) => @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) @@ -52,7 +44,7 @@ export const route: Route = { return { title: '通信与电子工程学院通知公告', - link: 'https://cee.ecnu.edu.cn/tzgg_4170/list.htm', + link: 'https://ieeic.ecnu.edu.cn/tzgg_4170/list.htm', item: items, }; }, diff --git a/lib/routes/ecnu/chem.ts b/lib/routes/ecnu/chem.ts index fe255e7b5fae..260e9ed26d07 100644 --- a/lib/routes/ecnu/chem.ts +++ b/lib/routes/ecnu/chem.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/chinese.ts b/lib/routes/ecnu/chinese.ts index c3d64dc9164a..8cd7464e0544 100644 --- a/lib/routes/ecnu/chinese.ts +++ b/lib/routes/ecnu/chinese.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/comm.ts b/lib/routes/ecnu/comm.ts index 2a077c302148..b8fc9e64665a 100644 --- a/lib/routes/ecnu/comm.ts +++ b/lib/routes/ecnu/comm.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/cs.ts b/lib/routes/ecnu/cs.ts index 65604f2d9894..f436597597de 100644 --- a/lib/routes/ecnu/cs.ts +++ b/lib/routes/ecnu/cs.ts @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.view-cnt'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) diff --git a/lib/routes/ecnu/cxcy.ts b/lib/routes/ecnu/cxcy.ts index b1fc8c20d569..6f757c857fca 100644 --- a/lib/routes/ecnu/cxcy.ts +++ b/lib/routes/ecnu/cxcy.ts @@ -68,7 +68,7 @@ export const route: Route = { $el.attr(attr, new URL(val, baseUrl).href); } }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/dase.ts b/lib/routes/ecnu/dase.ts index ffd5f6ecb29e..2312611e9754 100644 --- a/lib/routes/ecnu/dase.ts +++ b/lib/routes/ecnu/dase.ts @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) diff --git a/lib/routes/ecnu/dx.ts b/lib/routes/ecnu/dx.ts index 19c359650fb9..33562b165287 100644 --- a/lib/routes/ecnu/dx.ts +++ b/lib/routes/ecnu/dx.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/dxb.ts b/lib/routes/ecnu/dxb.ts index b924a5a77bbf..a9815bdfc4c9 100644 --- a/lib/routes/ecnu/dxb.ts +++ b/lib/routes/ecnu/dxb.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/ed.ts b/lib/routes/ecnu/ed.ts index 510088a06e5f..12ef35a34aa5 100644 --- a/lib/routes/ecnu/ed.ts +++ b/lib/routes/ecnu/ed.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.read'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/geoai.ts b/lib/routes/ecnu/geoai.ts index 16a447e445d9..1c5f986e6442 100644 --- a/lib/routes/ecnu/geoai.ts +++ b/lib/routes/ecnu/geoai.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/ghcollege.ts b/lib/routes/ecnu/ghcollege.ts index 417bf1ab3939..ac0c5cee9d17 100644 --- a/lib/routes/ecnu/ghcollege.ts +++ b/lib/routes/ecnu/ghcollege.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/history.ts b/lib/routes/ecnu/history.ts index 7af7d5a64a87..c9da793dee8e 100644 --- a/lib/routes/ecnu/history.ts +++ b/lib/routes/ecnu/history.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/mks.ts b/lib/routes/ecnu/mks.ts index 3d56dec355c2..8483a71cd95a 100644 --- a/lib/routes/ecnu/mks.ts +++ b/lib/routes/ecnu/mks.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/mxcsy.ts b/lib/routes/ecnu/mxcsy.ts index 5f7c0d9728eb..1d41464cb273 100644 --- a/lib/routes/ecnu/mxcsy.ts +++ b/lib/routes/ecnu/mxcsy.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/pharm.ts b/lib/routes/ecnu/pharm.ts index bc8fa1cb353e..30452a0a2555 100644 --- a/lib/routes/ecnu/pharm.ts +++ b/lib/routes/ecnu/pharm.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/philo.ts b/lib/routes/ecnu/philo.ts index 1fbc8d57c858..643fc53c4b88 100644 --- a/lib/routes/ecnu/philo.ts +++ b/lib/routes/ecnu/philo.ts @@ -44,7 +44,7 @@ export const route: Route = { $el.attr(attr, new URL(val, baseUrl).href); } }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/phy.ts b/lib/routes/ecnu/phy.ts index 528b483aa17e..f181e3166c2d 100644 --- a/lib/routes/ecnu/phy.ts +++ b/lib/routes/ecnu/phy.ts @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) diff --git a/lib/routes/ecnu/psy.ts b/lib/routes/ecnu/psy.ts index 07b056125c3e..acb08d10e477 100644 --- a/lib/routes/ecnu/psy.ts +++ b/lib/routes/ecnu/psy.ts @@ -36,14 +36,6 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); item.description = $read.html()?.trim(); return item; }) diff --git a/lib/routes/ecnu/sees.ts b/lib/routes/ecnu/sees.ts index fce7194d2f01..e9b5ab819994 100644 --- a/lib/routes/ecnu/sees.ts +++ b/lib/routes/ecnu/sees.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/sei.ts b/lib/routes/ecnu/sei.ts index 5f5d367c15d6..2dd0c68d1654 100644 --- a/lib/routes/ecnu/sei.ts +++ b/lib/routes/ecnu/sei.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/spm.ts b/lib/routes/ecnu/spm.ts index 6ec3bcb6e271..42ac25898a6a 100644 --- a/lib/routes/ecnu/spm.ts +++ b/lib/routes/ecnu/spm.ts @@ -39,15 +39,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; } // file to download diff --git a/lib/routes/ecnu/stat.ts b/lib/routes/ecnu/stat.ts index f23ef39a7e56..2d8de0c823c6 100644 --- a/lib/routes/ecnu/stat.ts +++ b/lib/routes/ecnu/stat.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecnu/tyxx.ts b/lib/routes/ecnu/tyxx.ts index 7b7a16d68787..83169b7a9358 100644 --- a/lib/routes/ecnu/tyxx.ts +++ b/lib/routes/ecnu/tyxx.ts @@ -36,15 +36,7 @@ export const route: Route = { const { data } = await got(item.link); const $ = load(data); const $read = $('div.wp_articlecontent'); - $read.find('img[src], a[href]').each((i, el) => { - const $el = $(el); - const attr = el.tagName === 'img' ? 'src' : 'href'; - const val = $el.attr(attr); - if (val) { - $el.attr(attr, new URL(val, baseUrl).href); - } - }); - item.description = $read.html()?.trim(); + item.description = $read.html(); return item; }) ) diff --git a/lib/routes/ecust/jwc/notice.ts b/lib/routes/ecust/jwc/notice.ts index 056a09284c69..5674de02260f 100644 --- a/lib/routes/ecust/jwc/notice.ts +++ b/lib/routes/ecust/jwc/notice.ts @@ -76,7 +76,7 @@ async function handler(ctx) { content(el).removeAttr(attr); } }); - const description = content('div.wp_articlecontent').first().html(); + const description = content('div.wp_articlecontent').html(); // merge same objects, replace two times instead of replace recursively description && (item.description = description.replaceAll(/<\/(p|span|strong)>\s*<\1>/g, '').replaceAll(/<\/(p|span|strong)>\s*<\1>/g, '')); return item; diff --git a/lib/routes/elamigos/index.ts b/lib/routes/elamigos/index.ts index 6333a1d847eb..96c26b259289 100644 --- a/lib/routes/elamigos/index.ts +++ b/lib/routes/elamigos/index.ts @@ -75,7 +75,7 @@ function extractGames($: any, limit: number, baseUrl: string): Array<{ title: st arrivedAtGameSection = true; // Found H1 date, fill all empty Games with the new Date. for (const game of games) { - if (game.pubDate === null || game.pubDate.trim() === '') { + if (game.pubDate === null || game.pubDate === '') { game.pubDate = match[0]; } } @@ -114,7 +114,7 @@ function extractLatestDate(pageHtml: string): Date | null { function sanitizeHtml(pageHtml: string): string { const $page = load(pageHtml); - $page('script, style, link, nav').remove(); + $page('style, link, nav').remove(); $page('*').each((_: number, elem: any) => { if (!elem.attribs) { diff --git a/lib/routes/eprice/rss.tsx b/lib/routes/eprice/rss.tsx index 28f0aa6bf004..ad8a2f95ba6e 100644 --- a/lib/routes/eprice/rss.tsx +++ b/lib/routes/eprice/rss.tsx @@ -48,11 +48,7 @@ async function handler(ctx) { const items = await Promise.all( feed.items.map((item) => cache.tryGet(item.link, async () => { - const response = await got(item.link, { - headers: { - Referer: `https://www.eprice.com.${region}`, - }, - }); + const response = await got(item.link); const $ = load(response.data); diff --git a/lib/routes/expats/czech-news.ts b/lib/routes/expats/czech-news.ts index bcbb3f937f59..6e58ded6e86d 100644 --- a/lib/routes/expats/czech-news.ts +++ b/lib/routes/expats/czech-news.ts @@ -48,7 +48,7 @@ export const handler = async (ctx: Context): Promise<Data> => { $$('div.promo-widget, div.eas').remove(); const title: string = $$('div.title h1').text(); - const description: string | undefined = $$('div#expats-article-content').html() ?? undefined; + const description = $$('div#expats-article-content').html(); const pubDateStr: string | undefined = $$('meta[property="article:published_time"]').attr('content'); const categories: string[] = [$$('meta[property="article:section"]').attr('content') ?? '']; const authorEls: Element[] = $$('span.written-by a').toArray(); diff --git a/lib/routes/f95zone/post.ts b/lib/routes/f95zone/post.ts index adc79a6b0806..cae9076463f4 100644 --- a/lib/routes/f95zone/post.ts +++ b/lib/routes/f95zone/post.ts @@ -47,7 +47,6 @@ Note: This route does not support Radar auto-detection because the post ID is in const response = await ofetch(link, { headers: { - referer: baseUrl, ...(config.f95zone.cookie && { cookie: config.f95zone.cookie }), }, }); @@ -56,7 +55,7 @@ Note: This route does not support Radar auto-detection because the post ID is in const title = $('h1.p-title-value').text().trim(); const post = $(`article[data-content="${postId}"]`); const content = post.find('.bbWrapper').html() || ''; - const author = post.attr('data-author') || ''; + const author = post.attr('data-author'); const postDate = post.find('time.u-dt').first().attr('datetime'); const tags = $('a.tagItem') .toArray() diff --git a/lib/routes/f95zone/thread.ts b/lib/routes/f95zone/thread.ts index 6bbf15b30eab..6d28b413e03f 100644 --- a/lib/routes/f95zone/thread.ts +++ b/lib/routes/f95zone/thread.ts @@ -50,13 +50,12 @@ Note: If you want to track a specific post's content changes (e.g., first post w const threadLink = `${baseUrl}/threads/${thread}/`; const headers = { - referer: baseUrl, ...(config.f95zone.cookie && { cookie: config.f95zone.cookie }), }; const firstPageResponse = await ofetch(threadLink, { headers }); const $firstPage = load(firstPageResponse); - const title = $firstPage('h1.p-title-value').text().trim(); + const title = $firstPage('h1.p-title-value').text(); const lastPageLink = $firstPage('ul.pageNav-main li.pageNav-page:last-child a').attr('href'); const totalPages = lastPageLink ? Number(lastPageLink.match(/page-(\d+)/)?.[1] || '1') : 1; @@ -64,14 +63,14 @@ Note: If you want to track a specific post's content changes (e.g., first post w const extractPosts = ($: CheerioAPI): DataItem[] => $('article.message') .toArray() - .flatMap((article) => { + .map((article) => { const $article = $(article); const postId = $article.attr('data-content')?.replace('post-', ''); if (!postId) { - return []; + return; } - const author = $article.find('.message-name a').text().trim(); + const author = $article.find('.message-name a').text(); const postDate = $article.find('time.u-dt').attr('datetime'); const content = $article.find('.bbWrapper').html() || ''; const postLink = `${threadLink}post-${postId}`; @@ -87,7 +86,8 @@ Note: If you want to track a specific post's content changes (e.g., first post w pubDate: postDate ? parseDate(postDate) : undefined, author, }; - }); + }) + .filter(Boolean); // Extract posts from the first page const allPosts: DataItem[] = [...extractPosts($firstPage)]; diff --git a/lib/routes/f95zone/utils.ts b/lib/routes/f95zone/utils.ts index 5af9178fb69e..c7b12b72af59 100644 --- a/lib/routes/f95zone/utils.ts +++ b/lib/routes/f95zone/utils.ts @@ -67,5 +67,5 @@ export const processContent = (html: string): string => { .filter((_, el) => !$(el).html()?.trim()) .remove(); - return $.html() || ''; + return $.html(); }; diff --git a/lib/routes/fantia/user.ts b/lib/routes/fantia/user.ts index 5244b8954cfc..62cbc662df9d 100644 --- a/lib/routes/fantia/user.ts +++ b/lib/routes/fantia/user.ts @@ -70,7 +70,6 @@ async function handler(ctx) { Cookie: config.fantia.cookies ?? '', 'X-CSRF-Token': csrfToken, Accept: 'application/json, text/plain, */*', - Referer: `${rootUrl}/`, 'X-Requested-With': 'XMLHttpRequest', }, }); diff --git a/lib/routes/fashionnetwork/index.ts b/lib/routes/fashionnetwork/index.ts index 4c603102d74a..807a74e864f7 100644 --- a/lib/routes/fashionnetwork/index.ts +++ b/lib/routes/fashionnetwork/index.ts @@ -29,7 +29,7 @@ export const handler = async (ctx) => { const title = item.find('h2.family-title').text(); - const src = item.find('img.item__img').first().prop('src') ?? undefined; + const src = item.find('img.item__img').prop('src') ?? undefined; const image = src ? new URL(src, rootUrl).href : undefined; const description = renderDescription({ @@ -72,7 +72,6 @@ export const handler = async (ctx) => { item.description = description; item.pubDate = timezone(parseDate($$('span.time-ago').first().text().trim()), 8); item.category = $$('div.newsTags') - .first() .find('div.news-tag') .toArray() .map((c) => $$(c).text()); diff --git a/lib/routes/firefox/breaches.ts b/lib/routes/firefox/breaches.ts index 5894e772e5d5..9a8d95ef222b 100644 --- a/lib/routes/firefox/breaches.ts +++ b/lib/routes/firefox/breaches.ts @@ -56,7 +56,7 @@ async function handler() { return { title: $('title').text(), - description: $('head meta[name=description]').attr('content').trim(), + description: $('head meta[name=description]').attr('content'), link: response.url, item: items, image: $('head meta[property=og:image]').attr('content'), diff --git a/lib/routes/fjdaily/index.ts b/lib/routes/fjdaily/index.ts index 259a25dc7790..77c32d336e16 100644 --- a/lib/routes/fjdaily/index.ts +++ b/lib/routes/fjdaily/index.ts @@ -188,8 +188,8 @@ async function handler(ctx) { cache.tryGet(item.link!, async () => { const detailResponse = await got(item.link!); const detail = load(detailResponse.data); - const pubDate = detail('#NewsArticlePubDay').text().trim(); - const author = detail('#NewsArticleAuthor').text().trim(); + const pubDate = detail('#NewsArticlePubDay').text(); + const author = detail('#NewsArticleAuthor').text(); const description = getItemDescription(detail); return { diff --git a/lib/routes/flyert/util.ts b/lib/routes/flyert/util.ts index d8ed803ba826..4369beae263d 100644 --- a/lib/routes/flyert/util.ts +++ b/lib/routes/flyert/util.ts @@ -41,7 +41,7 @@ const parseArticleList = ($: CheerioAPI, limit: number) => description, pubDate: pubDate ? parseDate(pubDate) : undefined, link, - author: item.find('div.subcat span.y a').first().text(), + author: item.find('div.subcat span.y a').text(), content: { html: description, text: item.find('div.wznr').text(), @@ -162,7 +162,7 @@ const parsePost = ($$: CheerioAPI, item) => { $$('i.pstatus').remove(); $$('div.tip').remove(); - const title = $$('span#thread_subject').text().trim(); + const title = $$('span#thread_subject').text(); const description = $$('div.post_message').first().html(); const pubDate = $$('span[title]').first().prop('title'); @@ -172,7 +172,7 @@ const parsePost = ($$: CheerioAPI, item) => { item.title = title; item.description = description; item.pubDate = pubDate ? timezone(parseDate(pubDate), 8) : item.pubDate; - item.author = $$('a.kmxi2').first().text(); + item.author = $$('a.kmxi2').text(); item.guid = guid; item.id = guid; item.content = { diff --git a/lib/routes/flyert/utils.ts b/lib/routes/flyert/utils.ts index 5dbb268c5def..8ba132b8f759 100644 --- a/lib/routes/flyert/utils.ts +++ b/lib/routes/flyert/utils.ts @@ -19,7 +19,7 @@ async function loadContent(link) { // 去除全文末尾多余内容 $('.lookMore').remove(); - $('script, style').remove(); + $('style').remove(); $('#loginDialog').remove(); // 获取第一个帖子对象 diff --git a/lib/routes/freebuf/index.ts b/lib/routes/freebuf/index.ts index 0e1edc5555ca..686c654a4025 100644 --- a/lib/routes/freebuf/index.ts +++ b/lib/routes/freebuf/index.ts @@ -37,7 +37,6 @@ async function handler(ctx) { const options = { headers: { - referer: 'https://www.freebuf.com', accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', }, query: { diff --git a/lib/routes/fxiaoke/crm.ts b/lib/routes/fxiaoke/crm.ts index ec386098df26..9a2849a1661e 100644 --- a/lib/routes/fxiaoke/crm.ts +++ b/lib/routes/fxiaoke/crm.ts @@ -61,7 +61,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { const resp = await got(item.link); const $ = load(resp.data); - const firstViewBox = $('.body-wrapper-article').first(); + const firstViewBox = $('.body-wrapper-article'); firstViewBox.find('img').each((_, img) => { img = $(img); diff --git a/lib/routes/gameapps/index.tsx b/lib/routes/gameapps/index.tsx index b8ab6ae25062..93843734db34 100644 --- a/lib/routes/gameapps/index.tsx +++ b/lib/routes/gameapps/index.tsx @@ -30,11 +30,7 @@ async function handler() { const items = await Promise.all( feed.items.map((item) => cache.tryGet(item.link, async () => { - const response = await ofetch(item.link, { - headers: { - Referer: baseUrl, - }, - }); + const response = await ofetch(item.link); const $ = load(response); item.title = ($('meta[property="og:title"]').attr('content') ?? $('.news-title h1').text()).replace(' - 香港手機遊戲網 GameApps.hk', ''); diff --git a/lib/routes/gamer/hot.ts b/lib/routes/gamer/hot.ts index 4b1fe468208e..0cb15e8d9165 100644 --- a/lib/routes/gamer/hot.ts +++ b/lib/routes/gamer/hot.ts @@ -28,12 +28,7 @@ export const route: Route = { async function handler(ctx) { const rootUrl = `https://forum.gamer.com.tw/B.php?bsn=${ctx.req.param('bsn')}`; - const response = await got({ - url: rootUrl, - headers: { - Referer: 'https://forum.gamer.com.tw', - }, - }); + const response = await got(rootUrl); const $ = load(response.data); const list = $('div.popular__card-list div.popular__card-img a') diff --git a/lib/routes/gdmuseum/exhibition.tsx b/lib/routes/gdmuseum/exhibition.tsx index b42baebd3fbf..150e30a18a2c 100644 --- a/lib/routes/gdmuseum/exhibition.tsx +++ b/lib/routes/gdmuseum/exhibition.tsx @@ -67,10 +67,9 @@ export const route: Route = { const rawLink = $item.attr('href'); const itemLink = rawLink ? new URL(rawLink, baseUrl).href : ''; - const rawSrc = $item.find('img').attr('src'); - const imgUrl = rawSrc ? new URL(rawSrc, baseUrl).href : undefined; + const imgUrl = $item.find('img').attr('src'); - const textDurationAndLocation = $item.find('.quitxt.qui-dot').text().trim(); + const textDurationAndLocation = $item.find('.quitxt.qui-dot').text(); const [fullDuration = '', location = ''] = textDurationAndLocation.split('|').map((p) => p.trim()); const { startDate, endDate } = parseExhibitionDuration(fullDuration); diff --git a/lib/routes/gdmuseum/news.ts b/lib/routes/gdmuseum/news.ts index 2271f9b77144..a3968bbdcbbd 100644 --- a/lib/routes/gdmuseum/news.ts +++ b/lib/routes/gdmuseum/news.ts @@ -36,8 +36,8 @@ export const route: Route = { const itemLink = new URL(rawLink, baseUrl).href; const title = $item.find('h3.h3.qui-dot').text(); - const day = $item.find('time b').text().trim(); - const yearMonth = $item.find('time i').text().trim(); + const day = $item.find('time b').text(); + const yearMonth = $item.find('time i').text(); const pubDate = timezone(parseDate(`${yearMonth}-${day}`), 8); diff --git a/lib/routes/gdufs/news.ts b/lib/routes/gdufs/news.ts index 031928e6fcc6..d63f17e5882d 100644 --- a/lib/routes/gdufs/news.ts +++ b/lib/routes/gdufs/news.ts @@ -53,7 +53,7 @@ async function handler() { try { const articleRes = await got(fullLink); const $$ = load(articleRes.body); - const description = $$('.v_news_content').html()?.trim() || ''; + const description = $$('.v_news_content').html()?.trim(); let author = ''; const authorSpans = $$('.nav01 h6 .ll span'); diff --git a/lib/routes/gdufs/xwxy/index.ts b/lib/routes/gdufs/xwxy/index.ts index 8242b5a4e432..678a7a54822b 100644 --- a/lib/routes/gdufs/xwxy/index.ts +++ b/lib/routes/gdufs/xwxy/index.ts @@ -104,7 +104,7 @@ const handler = async (ctx) => { } }); - const content = $content.html() || ''; + const content = $content.html(); // 提取作者/编辑等信息,并去除"发布时间"和日期 const metaTexts = $$('.show01 p i') .toArray() diff --git a/lib/routes/gigazine/en.ts b/lib/routes/gigazine/en.ts index baa148eebc64..7eeb5d2b5a33 100644 --- a/lib/routes/gigazine/en.ts +++ b/lib/routes/gigazine/en.ts @@ -22,13 +22,12 @@ const getAbsoluteUrl = (path: string | undefined) => (path ? new URL(path, ROOT_ const getArticleAuthor = ($: CheerioAPI) => $('#article .items p') .text() - .match(/Posted by\s+(\S.*)$/)?.[1] - ?.trim(); + .match(/Posted by\s+(\S.*)$/)?.[1]; const getArticleCategories = ($: CheerioAPI) => [ ...new Set( $('#article .items p a[href*="/gsc_news/en/C"]') .toArray() - .map((element) => $(element).text().trim()) + .map((element) => $(element).text()) .filter(Boolean) ), ]; @@ -37,7 +36,6 @@ const fetchDescription = async (item: DataItem) => { const $ = load(articleHtml); const content = $('#article .cntimage'); - content.find('script').remove(); content.find('h1.title, time.yeartime').remove(); content.find('img').each((_, img) => { @@ -55,7 +53,7 @@ const fetchDescription = async (item: DataItem) => { } }); - item.description = content.html()?.trim() ?? ''; + item.description = content.html()?.trim(); item.image = getAbsoluteUrl($('meta[property="og:image"]').attr('content')) ?? item.image; item.author = getArticleAuthor($) ?? item.author; const categories = getArticleCategories($); @@ -93,7 +91,7 @@ export const route: Route = { async function handler(ctx) { const limit = Number(ctx.req.query('limit')) || DEFAULT_LIMIT; - const html = await ofetch(LIST_URL, getRequestOptions(ROOT_URL)); + const html = await ofetch(LIST_URL); const $ = load(html); const list: DataItem[] = $('.card') @@ -105,7 +103,7 @@ async function handler(ctx) { const link = path ? new URL(path, ROOT_URL).href : undefined; const title = anchor.find('span').text(); const pubDate = parseDate(card.find('time').attr('datetime') ?? ''); - const category = card.find('.catab').text().trim(); + const category = card.find('.catab').text(); const imagePath = card.find('.thumb img').attr('src') ?? card.find('.thumb img').attr('data-src'); return { diff --git a/lib/routes/github/activity.ts b/lib/routes/github/activity.ts index abb4ed5e9c17..eb4a222dfd66 100644 --- a/lib/routes/github/activity.ts +++ b/lib/routes/github/activity.ts @@ -47,7 +47,7 @@ export const route: Route = { item: feed.items.map((item) => ({ title: item.title ?? '', link: item.link, - description: sanitizeHtml(item.content?.replaceAll(/href="\/(.+?)"/g, 'href="https://github.com/$1"') ?? '', { allowedTags: [...sanitizeHtml.defaults.allowedTags, 'img'] }), + description: sanitizeHtml(item.content ?? '', { allowedTags: [...sanitizeHtml.defaults.allowedTags, 'img'] }), pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, author: item.author, guid: item.id, diff --git a/lib/routes/github/advisor.ts b/lib/routes/github/advisor.ts index d99a41df6e93..2acfa2018d3c 100644 --- a/lib/routes/github/advisor.ts +++ b/lib/routes/github/advisor.ts @@ -83,7 +83,7 @@ async function handler(ctx) { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.comment-body').first().html() || ''; + item.description = $('.comment-body').html(); return item; }) diff --git a/lib/routes/github/topic.ts b/lib/routes/github/topic.ts index a905cb77c692..fbfe9308578a 100644 --- a/lib/routes/github/topic.ts +++ b/lib/routes/github/topic.ts @@ -58,7 +58,7 @@ async function handler(ctx) { const category = item .find('.topic-tag') .toArray() - .map((item) => $(item).text().trim()); + .map((item) => $(item).text()); const pubDate = parseDate(item.find('relative-time').attr('datetime')); return { diff --git a/lib/routes/go/jihs/idwr.ts b/lib/routes/go/jihs/idwr.ts index 08f6ed243086..edb06a11f022 100644 --- a/lib/routes/go/jihs/idwr.ts +++ b/lib/routes/go/jihs/idwr.ts @@ -29,7 +29,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $pEl: Cheerio<Element> = $el.parent('p'); const title: string = $pEl.prev('h2').text(); - const description: string | undefined = $pEl.html() ?? undefined; + const description = $pEl.html(); const pubDateStr: string | undefined = $pEl.text().match(/〔(\d{4}年\d{1,2}月\d{1,2}日)発行〕/)?.[1]; const linkUrl: string | undefined = $el.attr('href'); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/google/developers.ts b/lib/routes/google/developers.ts index 58ae3c49d4e2..f7be7bfdc5b3 100644 --- a/lib/routes/google/developers.ts +++ b/lib/routes/google/developers.ts @@ -47,12 +47,12 @@ async function handler(ctx: Context) { const items = $('.search-result') .toArray() .map((element) => { - const dateCategory = $(element).find('.search-result__eyebrow').text().trim(); + const dateCategory = $(element).find('.search-result__eyebrow').text(); const [date, category] = dateCategory.split(' / ', 2); const titleElement = $(element).find('.search-result__title a'); - const title = titleElement.text().trim(); + const title = titleElement.text(); const link = titleElement.attr('href'); - const summary = $(element).find('.search-result__summary').text().trim(); + const summary = $(element).find('.search-result__summary').text(); return { title, diff --git a/lib/routes/google/research.ts b/lib/routes/google/research.ts index 88f36f11742d..456dffddcb9c 100644 --- a/lib/routes/google/research.ts +++ b/lib/routes/google/research.ts @@ -25,7 +25,7 @@ export const route: Route = { .toArray() .map((eleItem) => { const item = $(eleItem); - const a = item.find('a').first(); + const a = item.find('a'); return { title: a.find('.headline-5').text(), link: `${baseUrl}${a.attr('href')}`, diff --git a/lib/routes/gov/beijing/bjedu/gh.ts b/lib/routes/gov/beijing/bjedu/gh.ts index 98e918d8c780..def9acd74f1f 100644 --- a/lib/routes/gov/beijing/bjedu/gh.ts +++ b/lib/routes/gov/beijing/bjedu/gh.ts @@ -50,7 +50,7 @@ async function handler(ctx) { return { title: item.text().trim(), link: item.attr('href').startsWith('http') ? item.attr('href').replace(/^http:/, 'https:') : new URL(item.attr('href'), link).href, - pubDate: item.prev().length ? timezone(parseDate(item.prev().text().trim(), 'YYYY-MM-DD'), 8) : null, + pubDate: item.prev().length ? timezone(parseDate(item.prev().text(), 'YYYY-MM-DD'), 8) : null, }; }); @@ -64,7 +64,7 @@ async function handler(ctx) { const $ = load(response); item.title = item.title.endsWith('...') ? $('.con-h h1').text().trim() : item.title; - item.pubDate = timezone(parseDate($('.con-h span').eq(0).text().trim(), 'YYYY-MM-DD HH:mm:ss'), 8); + item.pubDate = timezone(parseDate($('.con-h span').eq(0).text(), 'YYYY-MM-DD HH:mm:ss'), 8); item.author = $('.con-h span').eq(1).text().trim(); item.description = $('.content_font').html(); diff --git a/lib/routes/gov/chongqing/sydwgkzp.ts b/lib/routes/gov/chongqing/sydwgkzp.ts index 07cb7fc50198..e8f32805a649 100644 --- a/lib/routes/gov/chongqing/sydwgkzp.ts +++ b/lib/routes/gov/chongqing/sydwgkzp.ts @@ -56,7 +56,7 @@ async function handler(ctx: Context): Promise<Data> { .toArray() .map((item) => { item = $(item); - const title = item.find('a').first(); + const title = item.find('a'); return { // 文章标题 title: title.text(), @@ -74,7 +74,7 @@ async function handler(ctx: Context): Promise<Data> { const { data: response } = await got(item.link); const $ = load(response); // 主题正文 - item.description = $('.trs_editor_view').first().html(); + item.description = $('.trs_editor_view').html(); return item; }) ) diff --git a/lib/routes/gov/hainan/iitb/tzgg.ts b/lib/routes/gov/hainan/iitb/tzgg.ts index 46446751cf76..50a11e0d5983 100644 --- a/lib/routes/gov/hainan/iitb/tzgg.ts +++ b/lib/routes/gov/hainan/iitb/tzgg.ts @@ -34,7 +34,7 @@ async function handler() { const titleElement = elem.find('.list-right_title a'); const link = titleElement.attr('href') || ''; - const title = titleElement.text().trim() || ''; + const title = titleElement.text() || ''; const dateText = elem.find('td[align="left"]').text().replace('发布时间:', '').trim(); const department = elem.find('.column-name').text().trim(); diff --git a/lib/routes/gov/huizhou/zwgk/index.ts b/lib/routes/gov/huizhou/zwgk/index.ts index 6d8be0bdf942..7097cd67e5ae 100644 --- a/lib/routes/gov/huizhou/zwgk/index.ts +++ b/lib/routes/gov/huizhou/zwgk/index.ts @@ -55,7 +55,7 @@ async function handler(ctx) { .map((item) => ({ title: $(item).find('a').text().trim(), link: $(item).find('a').attr('href'), - pubDate: timezone(parseDate($(item).find('li.li_art_date').text().trim()), 8), + pubDate: timezone(parseDate($(item).find('li.li_art_date').text()), 8), })); const items = await Promise.all( diff --git a/lib/routes/gov/mee/nnsa.ts b/lib/routes/gov/mee/nnsa.ts index 5af463ef36e2..cc2f66b74a47 100644 --- a/lib/routes/gov/mee/nnsa.ts +++ b/lib/routes/gov/mee/nnsa.ts @@ -50,7 +50,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('meta[name="ArticleTitle"]').attr('content') ?? item.title; - const description: string | undefined = $$('div.Custom_UnionStyle').html() ?? undefined; + const description = $$('div.Custom_UnionStyle').html(); const pubDateStr: string | undefined = $$('meta[name="PubDate"]').attr('content'); const categoryEls: Array<Cheerio<Element>> = [$$('meta[name="ColumnName"]'), $$('meta[name="ColumnType"]'), $$('meta[name="ContentSource"]'), $$('meta[name="source"]')]; const categories: string[] = [...new Set(categoryEls.map((el) => $$(el)?.attr('content') ?? '').filter(Boolean))]; diff --git a/lib/routes/gov/mee/ywdt.ts b/lib/routes/gov/mee/ywdt.ts index 8a7b1057237c..57f58a44d73f 100644 --- a/lib/routes/gov/mee/ywdt.ts +++ b/lib/routes/gov/mee/ywdt.ts @@ -55,7 +55,7 @@ async function handler(ctx) { .find('.mobile_none li , .mobile_clear li') .toArray() .map((item) => { - const title = $(item).find('a.cjcx_biaob').text().trim(); + const title = $(item).find('a.cjcx_biaob').text(); const href = $(item).find('a').attr('href'); let absolute_path; diff --git a/lib/routes/gov/mem/zfxxgkpt.ts b/lib/routes/gov/mem/zfxxgkpt.ts index e4c6c35ed31b..d41c7a306c37 100644 --- a/lib/routes/gov/mem/zfxxgkpt.ts +++ b/lib/routes/gov/mem/zfxxgkpt.ts @@ -74,8 +74,8 @@ async function handler(ctx) { const content = load(detailResponse); const description = content('#content').html(); - const author = content('td.td_lable:contains("所属机构")').next('td').text().trim(); - const category = content('td.td_lable:contains("主题分类")').next('td').text().trim(); + const author = content('td.td_lable:contains("所属机构")').next('td').text(); + const category = content('td.td_lable:contains("主题分类")').next('td').text(); return { ...item, diff --git a/lib/routes/gov/mfa/wjdt.ts b/lib/routes/gov/mfa/wjdt.ts index d0d435b587f8..0ba270c50c9d 100644 --- a/lib/routes/gov/mfa/wjdt.ts +++ b/lib/routes/gov/mfa/wjdt.ts @@ -72,7 +72,7 @@ async function handler(ctx) { const content = load(detailResponse.data); item.description = content('#News_Body_Txt_A').html(); - item.pubDate = timezone(parseDate(content('.time span').last().text()), 8); + item.pubDate = timezone(parseDate(content('.time span').text()), 8); item.category = content('meta[name="Keywords"]').attr('content')?.split(';') ?? []; return item; diff --git a/lib/routes/gov/miit/wjfb.ts b/lib/routes/gov/miit/wjfb.ts index f5bf820dbeb1..a2ac209f7349 100644 --- a/lib/routes/gov/miit/wjfb.ts +++ b/lib/routes/gov/miit/wjfb.ts @@ -70,9 +70,7 @@ async function handler(ctx) { const detailResponse = await got(item.link); const content = load(detailResponse.data); - item.description = content('#con_con') - .html() - ?.replaceAll(/(<iframe.*?src=")([^"]*)(".*?>)/g, (_match, p1, p2, p3) => p1 + rootUrl + p2 + p3); + item.description = content('#con_con').html(); return item; }) diff --git a/lib/routes/gov/miit/wjgs.ts b/lib/routes/gov/miit/wjgs.ts index 482502cb855a..30dbbed643b1 100644 --- a/lib/routes/gov/miit/wjgs.ts +++ b/lib/routes/gov/miit/wjgs.ts @@ -59,12 +59,6 @@ async function handler() { const { data } = await got(item.link); const $ = load(data); - $('iframe').each((_, e) => { - e = $(e); - if (e.attr('src').startsWith('/')) { - e.attr('src', new URL(e.attr('src'), baseUrl).href); - } - }); item.author = $('.cinfo') .text() .match(/来源:(.*)/)[1]; diff --git a/lib/routes/gov/miit/yjzj.ts b/lib/routes/gov/miit/yjzj.ts index bc7f7870eb85..b8f63fcab79b 100644 --- a/lib/routes/gov/miit/yjzj.ts +++ b/lib/routes/gov/miit/yjzj.ts @@ -69,9 +69,7 @@ async function handler() { const detailResponse = await got(item.link); const content = load(detailResponse.data); - item.description = content('#con_con') - .html() - ?.replaceAll(/(<iframe.*?src=")([^"]*)(".*?>)/g, (_match, p1, p2, p3) => p1 + rootUrl + p2 + p3); + item.description = content('#con_con').html(); return item; }) diff --git a/lib/routes/gov/miit/zcjd.ts b/lib/routes/gov/miit/zcjd.ts index 2deb670138ec..cc13f0b4c19b 100644 --- a/lib/routes/gov/miit/zcjd.ts +++ b/lib/routes/gov/miit/zcjd.ts @@ -59,12 +59,6 @@ async function handler() { const { data } = await got(item.link); const $ = load(data); - $('iframe').each((_, e) => { - e = $(e); - if (e.attr('src').startsWith('/')) { - e.attr('src', new URL(e.attr('src'), baseUrl).href); - } - }); item.author = $('.cinfo') .text() .match(/来源:(.*)/)[1]; diff --git a/lib/routes/gov/moa/gjs.ts b/lib/routes/gov/moa/gjs.ts index d8c5a0cd7205..33276dec2742 100644 --- a/lib/routes/gov/moa/gjs.ts +++ b/lib/routes/gov/moa/gjs.ts @@ -57,7 +57,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('meta[name="ArticleTitle"]').attr('content') ?? ''; - const description: string = $$('div.TRS_Editor').html() ?? ''; + const description = $$('div.TRS_Editor').html(); const pubDateStr: string | undefined = $$('meta[name="PubDate"]').attr('content'); const linkUrl: string | undefined = $$('meta[name="Url"]').attr('content'); const categoryEls: Element[] = $$('meta[name="ColumnName"], meta[name="ContentSource"], meta[name="Keywords"]').toArray(); diff --git a/lib/routes/gov/mof/gss.ts b/lib/routes/gov/mof/gss.ts index 38e6cbaede0f..196858d0736c 100644 --- a/lib/routes/gov/mof/gss.ts +++ b/lib/routes/gov/mof/gss.ts @@ -37,7 +37,7 @@ const handler = async (ctx: Context): Promise<Data | null> => { cache.tryGet(item.link!, async () => { const { data: detailResponse } = await got(item.link); const content = load(detailResponse); - item.description = content('div.my_doccontent').html() ?? ''; + item.description = content('div.my_doccontent').html(); item.author = author; return item; }) diff --git a/lib/routes/gov/mot/index.ts b/lib/routes/gov/mot/index.ts index 8a184c9ac21f..9af943b55a23 100644 --- a/lib/routes/gov/mot/index.ts +++ b/lib/routes/gov/mot/index.ts @@ -53,7 +53,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('h1').first().text(); - const description: string | undefined = $$('div.TRS_UEDITOR').html() ?? undefined; + const description = $$('div.TRS_UEDITOR').html(); const pubDateStr: string | undefined = $$('meta[name="PubDate"]').attr('content'); const categories: string[] = [ ...new Set( diff --git a/lib/routes/gov/nppa/channels.ts b/lib/routes/gov/nppa/channels.ts index 913cf4cf38ab..6c9a5c97e1cd 100644 --- a/lib/routes/gov/nppa/channels.ts +++ b/lib/routes/gov/nppa/channels.ts @@ -45,7 +45,7 @@ async function handler(ctx: Context): Promise<Data> { const $ = load(response); item.title = $('.m3page_t').text().trim() || $('head title').text(); - item.description = $('.m3pageEdit').html() ?? ''; + item.description = $('.m3pageEdit').html(); return item; }) diff --git a/lib/routes/gov/pudong/zwgk.ts b/lib/routes/gov/pudong/zwgk.ts index a08f7ef62718..e7486c1bf6d2 100644 --- a/lib/routes/gov/pudong/zwgk.ts +++ b/lib/routes/gov/pudong/zwgk.ts @@ -49,7 +49,7 @@ async function handler() { cache.tryGet(item.link, async () => { const response = await ofetch(item.link); const $ = load(response); - item.description = $('#ivs_content').first().html(); + item.description = $('#ivs_content').html(); return item; }) ) diff --git a/lib/routes/gov/shenzhen/hrss/szksy/index.ts b/lib/routes/gov/shenzhen/hrss/szksy/index.ts index 67d4b7e2b85c..bfaa62273059 100644 --- a/lib/routes/gov/shenzhen/hrss/szksy/index.ts +++ b/lib/routes/gov/shenzhen/hrss/szksy/index.ts @@ -58,7 +58,7 @@ async function handler(ctx) { return { title: tag.text().trim(), link: tag.attr('href'), - pubDate: timezone(parseDate(tag2.text().trim(), 'YYYY/MM/DD'), 0), + pubDate: timezone(parseDate(tag2.text(), 'YYYY/MM/DD'), 0), }; }); diff --git a/lib/routes/gov/shenzhen/szlh/index.ts b/lib/routes/gov/shenzhen/szlh/index.ts index ad9b7b993022..2bd7232503cf 100644 --- a/lib/routes/gov/shenzhen/szlh/index.ts +++ b/lib/routes/gov/shenzhen/szlh/index.ts @@ -55,13 +55,13 @@ async function handler(ctx) { .toArray() .map((item) => { item = $(item); - const a = item.find('a').first(); + const a = item.find('a'); // Extract the date from <i> - const date = a.find('i').text().trim(); + const date = a.find('i').text(); - // Clone and remove <i> to get only the visible text - const textOnly = a.clone().find('i').remove().end().text().trim(); + // Remove <i> to get only the visible text + const textOnly = a.find('i').remove().end().text(); return { title: textOnly, diff --git a/lib/routes/gov/shenzhen/zjj/index.ts b/lib/routes/gov/shenzhen/zjj/index.ts index ff5a57498b36..4060e8e22047 100644 --- a/lib/routes/gov/shenzhen/zjj/index.ts +++ b/lib/routes/gov/shenzhen/zjj/index.ts @@ -57,12 +57,12 @@ async function handler(ctx) { // 使用“map()”方法遍历数组,并从每个元素中解析需要的数据。 .map((item) => { item = $(item); - const a = item.find('a').first(); + const a = item.find('a'); return { title: a.text(), // `link` 需要一个绝对 URL,但 `a.attr('href')` 返回一个相对 URL。 link: a.attr('href'), - pubDate: timezone(parseDate(item.find('span').first().text(), 'YY-MM-DD'), 0), + pubDate: timezone(parseDate(item.find('span').text(), 'YY-MM-DD'), 0), }; }); diff --git a/lib/routes/gov/zj/czt/zfcg.ts b/lib/routes/gov/zj/czt/zfcg.ts index 116edec97404..c765b8b8d48c 100644 --- a/lib/routes/gov/zj/czt/zfcg.ts +++ b/lib/routes/gov/zj/czt/zfcg.ts @@ -66,7 +66,7 @@ async function handler(ctx: Context) { $('.ann-block [class], .ann-block [style]').removeAttr('class').removeAttr('style'); return { ...item, - description: $('.ann-block').html() ?? '', + description: $('.ann-block').html(), category: [...new Set([item.category, data.projectName, ...data.categoryNames])], }; }) diff --git a/lib/routes/gov/zj/search.ts b/lib/routes/gov/zj/search.ts index dd83a564a34d..2979e06fc67d 100644 --- a/lib/routes/gov/zj/search.ts +++ b/lib/routes/gov/zj/search.ts @@ -59,11 +59,11 @@ export const route: Route = { const title = $('.titleWrapper>a'); const footer = $('.sourceTime>span'); return { - title: title.text().trim() || '', + title: title.text().trim(), link: title.attr('href') || '', - pubDate: parseDate(footer.eq(1).text().trim().replace('时间:', '')) || '', - author: footer.eq(0).text().trim().replace('来源:', '') || '', - description: $('.newsDescribe>a').text() || '', + pubDate: parseDate(footer.eq(1).text().replace('时间:', '')) || '', + author: footer.eq(0).text().trim().replace('来源:', ''), + description: $('.newsDescribe>a').text(), }; }) || []; const res = {}; diff --git a/lib/routes/grainoil/category.ts b/lib/routes/grainoil/category.ts index d513409866af..f4d66f85eb7c 100644 --- a/lib/routes/grainoil/category.ts +++ b/lib/routes/grainoil/category.ts @@ -55,7 +55,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.m_tit h2').text(); - const description: string = $$('div.TRS_Editor').html() ?? ''; + const description = $$('div.TRS_Editor').html(); const authors: DataItem['author'] = $$('div.m_tit h2 a').first().text(); const processedItem: DataItem = { diff --git a/lib/routes/greasyfork/scripts.ts b/lib/routes/greasyfork/scripts.ts index a3fb6a335b14..085b25a13f81 100644 --- a/lib/routes/greasyfork/scripts.ts +++ b/lib/routes/greasyfork/scripts.ts @@ -54,7 +54,7 @@ async function handler(ctx) { const list = $('.script-list').find('article'); return { - title: $('title').first().text(), + title: $('title').text(), link: currentUrl, description: $('meta[name=description]').attr('content'), item: list?.toArray().map((item) => { diff --git a/lib/routes/grubstreet/utils.ts b/lib/routes/grubstreet/utils.ts index 685586c6b905..ab7f24f47932 100644 --- a/lib/routes/grubstreet/utils.ts +++ b/lib/routes/grubstreet/utils.ts @@ -49,12 +49,9 @@ function ProcessFeed(list, caches) { link: itemUrl, guid: itemUrl, pubDate: item.date, + author: bylineString, }; - if (bylineString) { - single.author = bylineString; - } - const { description } = await loadContent(itemUrl); single.description = description; diff --git a/lib/routes/guancha/member.ts b/lib/routes/guancha/member.ts index 0632137c7c92..3ad0bab76fbe 100644 --- a/lib/routes/guancha/member.ts +++ b/lib/routes/guancha/member.ts @@ -84,7 +84,7 @@ async function handler(ctx) { default: items = response.data.data[category].map((item) => { - let timeArray = item.media_time && item.media_time.trim().split(/\D+/, 3); + let timeArray = item.media_time && item.media_time.split(/\D+/, 3); timeArray &&= timeArray.filter((item) => item !== ''); let itunes_duration; if (timeArray) { diff --git a/lib/routes/gxmzu/lib.ts b/lib/routes/gxmzu/lib.ts index c46527f8abfc..4462075e43cf 100644 --- a/lib/routes/gxmzu/lib.ts +++ b/lib/routes/gxmzu/lib.ts @@ -46,7 +46,7 @@ async function handler() { return null; } return { - title: $link.text().trim(), + title: $link.text(), link: new URL(href, pageUrl).href, pubDate: parsePubDate($item.find('span').text()), }; diff --git a/lib/routes/gxmzu/utils/index.ts b/lib/routes/gxmzu/utils/index.ts index beb5b78f19f4..ba869ce80c77 100644 --- a/lib/routes/gxmzu/utils/index.ts +++ b/lib/routes/gxmzu/utils/index.ts @@ -59,21 +59,6 @@ async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promi const $content = $(selectors.content); - $content.find('a').each((_, el) => { - const $a = $(el); - const href = $a.attr('href'); - if (href) { - $a.attr('href', new URL(href, item.link).href); - } - }); - $content.find('img').each((_, el) => { - const $img = $(el); - const src = $img.attr('src'); - if (src) { - $img.attr('src', new URL(src, item.link).href); - } - }); - const title = $(selectors.title).text().trim(); const pubDate = selectors.date ? parsePubDate($(selectors.date).text()) : undefined; @@ -81,7 +66,7 @@ async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promi ...item, title: title || item.title, pubDate: pubDate ?? item.pubDate, - description: $content.html() ?? item.description, + description: $content.html(), }; } diff --git a/lib/routes/hackernews/index.ts b/lib/routes/hackernews/index.ts index 53979fc0b634..bab0642d356d 100644 --- a/lib/routes/hackernews/index.ts +++ b/lib/routes/hackernews/index.ts @@ -120,18 +120,14 @@ async function handler(ctx) { `  <small><a href="${rootUrl}/item?id=${content(el).attr('id')}">` + `${content(el).find('.age').attr('title')}</a></small></div>`; - const commentText = comment.clone(); - - commentText.find('p').remove(); - commentText.html(`<p>${commentText.text()}</p>`); - commentText.append( - comment - .find('p') - .toArray() - .map((p) => `<p>${content(p).html()}</p>`) - ); - - item.description += `<div>${commentText.html()}</div></div>`; + const leading = content(`<p>${comment.contents().not('p').text()}</p>`); + const paragraphs = comment + .find('p') + .toArray() + .map((p) => `<p>${content(p).html()}</p>`) + .join(''); + + item.description += `<div>${content.html(leading)}${paragraphs}</div></div>`; }); } else if (item.comments !== 'discuss' && type === 'comments_list') { item.title = item.onStory; diff --git a/lib/routes/hamel/index.ts b/lib/routes/hamel/index.ts index 1b0654c52fa9..6202c31ee583 100644 --- a/lib/routes/hamel/index.ts +++ b/lib/routes/hamel/index.ts @@ -36,8 +36,8 @@ async function handler() { const $date = $item.find('.listing-date'); const href = $link.attr('href'); - const title = $link.text().trim(); - const dateStr = $date.text().trim(); + const title = $link.text(); + const dateStr = $date.text(); if (!href || !title || !dateStr) { return null; @@ -64,7 +64,7 @@ async function handler() { return { ...item, - description: $detail('.content').html() || '', + description: $detail('.content').html(), } as DataItem; } catch { return item; diff --git a/lib/routes/hebeimuseum/list.tsx b/lib/routes/hebeimuseum/list.tsx index f03d4c30e1b2..d4e11f967196 100644 --- a/lib/routes/hebeimuseum/list.tsx +++ b/lib/routes/hebeimuseum/list.tsx @@ -17,7 +17,7 @@ const extractDates = (durationStr: string) => { return { startDate, endDate }; } - const parts = durationStr.split(/[-—~]+/).map((p) => p.trim()); // currently ——and- is used, add — or ~ for redundency + const parts = durationStr.split(/[-—~]+/); // currently ——and- is used, add — or ~ for redundency const startStr = parts[0]; const endStr = parts[1]; @@ -111,7 +111,7 @@ export const route: Route = { }); const content = load(detailResponse.data); - const pubDateRaw = content('.article .info .infowrap img.icon_time').next('span').text().replaceAll('时间:', '').trim(); + const pubDateRaw = content('.article .info .infowrap img.icon_time').next('span').text().replaceAll('时间:', ''); const pubDate = parseDate(pubDateRaw); // Default path: return as news, no detail information for return @@ -136,20 +136,13 @@ export const route: Route = { const texts = rawText.split(/(?=展览名称:|展览时间:|时间:|展览地点:|展出地点:|地点:)/); // use fullDration to extract startDate and endDate, if fullDuration is not exist, return empty data - const fullDuration = texts - .find((text) => text.includes('时间:')) - ?.replaceAll(/(?:展览)?时间:/g, '') - ?.trim(); + const fullDuration = texts.find((text) => text.includes('时间:'))?.replaceAll(/(?:展览)?时间:/g, ''); if (!fullDuration) { return {} as Record<string, any>; } - let location = - texts - .find((text) => text.includes('地点:')) - ?.replaceAll(/(?:展(?:览|出))?地点:/g, '') - ?.trim() || ''; + let location = texts.find((text) => text.includes('地点:'))?.replaceAll(/(?:展(?:览|出))?地点:/g, '') || ''; const locMatch = location.match(/^.*?展厅/) || ['']; diff --git a/lib/routes/hexun/index.ts b/lib/routes/hexun/index.ts index aefd556a78e1..8a6861a2a2dd 100644 --- a/lib/routes/hexun/index.ts +++ b/lib/routes/hexun/index.ts @@ -35,7 +35,7 @@ async function handler() { const a = element.find('a'); const link = a.attr('href')?.replace('http://', 'https://') || ''; - const title = a.text() || ''; + const title = a.text(); const timeSpan = element.find('span'); const dateText = timeSpan.text().slice(1, timeSpan.text().length - 1); @@ -59,7 +59,7 @@ async function handler() { const $ = load(decoder.decode(response.data)); - item.description = $('.art_contextBox').html() || ''; + item.description = $('.art_contextBox').html(); return item; }) diff --git a/lib/routes/hit/today.ts b/lib/routes/hit/today.ts index 8374719f1294..58976f8797e9 100644 --- a/lib/routes/hit/today.ts +++ b/lib/routes/hit/today.ts @@ -41,11 +41,7 @@ async function handler(ctx) { const host = 'https://today.hit.edu.cn'; const category = ctx.req.param('category'); - const response = await got(host + '/category/' + category, { - headers: { - Referer: host, - }, - }); + const response = await got(host + '/category/' + category); const $ = load(response.data); const list = $('.paragraph li') @@ -61,21 +57,11 @@ async function handler(ctx) { list.map((item) => cache.tryGet(item.link, async () => { try { - const response = await got(item.link, { - headers: { - Referer: host, - }, - }); + const response = await got(item.link); const $ = load(response.data); - item.pubDate = timezone(parseDate($('.left-attr.first').text().trim()), 8); - item.description = - $('.article-content').html() && - $('.article-content') - .html() - .replaceAll('src="/', () => `src="${new URL('.', host).href}`) - .replaceAll('href="/', () => `href="${new URL('.', host).href}`) - .trim(); + item.pubDate = timezone(parseDate($('.left-attr.first').text()), 8); + item.description = $('.article-content').html()?.trim(); } catch { // intranet item.description = '请进行统一身份认证后查看全文'; diff --git a/lib/routes/hitsz/due-tzgg.ts b/lib/routes/hitsz/due-tzgg.ts index 16078cde7917..d7686323fc02 100644 --- a/lib/routes/hitsz/due-tzgg.ts +++ b/lib/routes/hitsz/due-tzgg.ts @@ -44,7 +44,7 @@ export const handler = async () => { } const title = $el.find('span').text().trim(); - const pubDateStr = $el.find('label').text().trim(); + const pubDateStr = $el.find('label').text(); return { title, diff --git a/lib/routes/hitwh/today.ts b/lib/routes/hitwh/today.ts index 0cac40116a1f..4669c0c61cec 100644 --- a/lib/routes/hitwh/today.ts +++ b/lib/routes/hitwh/today.ts @@ -23,13 +23,13 @@ export const route: Route = { }, radar: [ { - source: ['hitwh.edu.cn/1024/list.htm', 'hitwh.edu.cn/'], + source: ['today.hitwh.edu.cn/1024/list.htm', 'today.hitwh.edu.cn/'], }, ], name: '今日工大 - 通知公告', maintainers: ['raptazure'], handler, - url: 'hitwh.edu.cn/1024/list.htm', + url: 'today.hitwh.edu.cn/1024/list.htm', }; async function handler() { @@ -50,26 +50,14 @@ async function handler() { item: await Promise.all( links.map((item) => cache.tryGet(item.link, async () => { - if (type(item.link) === 'htm') { - try { - const { data } = await got(item.link); - const $ = load(data); - item.description = - $('div.wp_articlecontent').html() && - $('div.wp_articlecontent') - .html() - .replaceAll('src="/', () => `src="${baseUrl}/`) - .replaceAll('href="/', () => `href="${baseUrl}/`) - .trim(); - return item; - } catch { - // intranet - item.description = '请进行统一身份认证之后再访问'; - return item; - } + if (type(item.link) !== 'htm') { + // file to download + item.description = '此链接为文件,点击以下载'; + return item; } - // file to download - item.description = '此链接为文件,点击以下载'; + const { data } = await got(item.link); + const $ = load(data); + item.description = $('div.wp_articlecontent').html() ?? '请进行统一身份认证之后再访问'; return item; }) ) diff --git a/lib/routes/hlju/news.ts b/lib/routes/hlju/news.ts index 16f667f20a08..5bc11024577d 100644 --- a/lib/routes/hlju/news.ts +++ b/lib/routes/hlju/news.ts @@ -104,7 +104,7 @@ async function handler(ctx) { if (content.length > 0) { // 清理内容 - content.find('script, style, .print, .share').remove(); + content.find('style, .print, .share').remove(); description = content.html() || ''; } else { description = '内容获取失败,请点击查看原文'; diff --git a/lib/routes/hnmuseum/exhibitions.tsx b/lib/routes/hnmuseum/exhibitions.tsx index c15a76089877..3382f166380a 100644 --- a/lib/routes/hnmuseum/exhibitions.tsx +++ b/lib/routes/hnmuseum/exhibitions.tsx @@ -30,7 +30,7 @@ const formatStr = (dateStr: string | undefined): string | undefined => { return undefined; } - const match = dateStr.trim().match(/(\d{4})年\s*(\d{1,2})月\s*(\d{1,2})日/); + const match = dateStr.match(/(\d{4})年\s*(\d{1,2})月\s*(\d{1,2})日/); if (match) { const year = match[1]; @@ -111,7 +111,7 @@ export const route: Route = { selector: '#block-views-a784821b4fd9f41563c7164fd2a2f96e .views-row', type: 'permanent' as const, extra: ($item: Cheerio<Element>) => ({ - location: $item.find('.views_zhanting .field-content').text().trim(), + location: $item.find('.views_zhanting .field-content').text(), fullDuration: $item.find('.views_startdate .field-content').text().trim(), }), }, @@ -119,7 +119,7 @@ export const route: Route = { selector: '#block-views-chen-lie-block .views-row', type: 'special' as const, extra: ($item: Cheerio<Element>) => ({ - location: $item.find('.views_zhanting .field-content').text().trim(), + location: $item.find('.views_zhanting .field-content').text(), }), }, { diff --git a/lib/routes/hongkong/chp.ts b/lib/routes/hongkong/chp.ts index 916cd5f1d9cc..28a0f6ad7b04 100644 --- a/lib/routes/hongkong/chp.ts +++ b/lib/routes/hongkong/chp.ts @@ -100,7 +100,7 @@ async function handler(ctx) { const content = load(detailResponse.data); - content('#btmNav, script').remove(); + content('#btmNav').remove(); content('.contHeader, .title_display_date').remove(); content('.printBtn, .bookmarkBtn, .qrBtn, .qr-content').remove(); diff --git a/lib/routes/hotukdeals/hottest.ts b/lib/routes/hotukdeals/hottest.ts index 805f22a37149..54b89464514e 100644 --- a/lib/routes/hotukdeals/hottest.ts +++ b/lib/routes/hotukdeals/hottest.ts @@ -28,11 +28,7 @@ export const route: Route = { }; async function handler() { - const data = await got.get('https://www.hotukdeals.com/', { - headers: { - Referer: 'https://www.hotukdeals.com/', - }, - }); + const data = await got.get('https://www.hotukdeals.com/'); const dom = new JSDOM(data.data, { runScripts: 'dangerously', diff --git a/lib/routes/hpoi/utils.ts b/lib/routes/hpoi/utils.ts index 8e6297cd5a65..1bc64f2a841d 100644 --- a/lib/routes/hpoi/utils.ts +++ b/lib/routes/hpoi/utils.ts @@ -28,9 +28,6 @@ const ProcessFeed = async (type, id, order) => { let response = await got({ method: 'get', url: link, - headers: { - Referer: host, - }, }); let $ = load(response.data); @@ -39,9 +36,6 @@ const ProcessFeed = async (type, id, order) => { const overviewResponse = await got({ method: 'get', url: overviewLink, - headers: { - Referer: host, - }, }); const $overview = load(overviewResponse.data); @@ -53,9 +47,6 @@ const ProcessFeed = async (type, id, order) => { response = await got({ method: 'get', url: link, - headers: { - Referer: host, - }, }); $ = load(response.data); } diff --git a/lib/routes/hrbeu/cec/list.ts b/lib/routes/hrbeu/cec/list.ts index 188c11e76a8a..4f9d9eb4bc9a 100644 --- a/lib/routes/hrbeu/cec/list.ts +++ b/lib/routes/hrbeu/cec/list.ts @@ -37,19 +37,14 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const response = await got(`${rootUrl}/${id}/list.htm`, { - headers: { - Referer: rootUrl, - }, - }); + const response = await got(`${rootUrl}/${id}/list.htm`); const $ = load(response.data); const bigTitle = $('div.column-news-box') .find('h2.column-title') .text() - .replaceAll(/[\s·]/g, '') - .trim(); + .replaceAll(/[\s·]/g, ''); const list = $('a.column-news-item') .toArray() @@ -59,7 +54,7 @@ async function handler(ctx) { link = `${rootUrl}${link}`; } return { - title: $(item).find('span.column-news-title').text().trim(), + title: $(item).find('span.column-news-title').text(), pubDate: parseDate($(item).find('span.column-news-date').text()), link, }; diff --git a/lib/routes/hrbeu/gx/card.ts b/lib/routes/hrbeu/gx/card.ts index 2fa03e43a84c..4788b3841357 100644 --- a/lib/routes/hrbeu/gx/card.ts +++ b/lib/routes/hrbeu/gx/card.ts @@ -18,11 +18,7 @@ async function handler(ctx) { const id = ctx.req.param('id') || ''; const toUrl = id === '' ? `${rootUrl}/${column}.htm` : `${rootUrl}/${column}/${id}.htm`; - const response = await got(toUrl, { - headers: { - Referer: rootUrl, - }, - }); + const response = await got(toUrl); const $ = load(response.data); diff --git a/lib/routes/hrbeu/gx/list.ts b/lib/routes/hrbeu/gx/list.ts index dc0ab50fd973..8e00e8332c4e 100644 --- a/lib/routes/hrbeu/gx/list.ts +++ b/lib/routes/hrbeu/gx/list.ts @@ -19,11 +19,7 @@ async function handler(ctx) { const id = ctx.req.param('id') || ''; const toUrl = id === '' ? `${rootUrl}/${column}.htm` : `${rootUrl}/${column}/${id}.htm`; - const response = await got(toUrl, { - headers: { - Referer: rootUrl, - }, - }); + const response = await got(toUrl); const $ = load(response.data); diff --git a/lib/routes/hrbeu/sec/list.ts b/lib/routes/hrbeu/sec/list.ts index e59677033158..aa34a8c86dc6 100644 --- a/lib/routes/hrbeu/sec/list.ts +++ b/lib/routes/hrbeu/sec/list.ts @@ -35,19 +35,14 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const response = await got(`${rootUrl}/${id}/list.htm`, { - headers: { - Referer: rootUrl, - }, - }); + const response = await got(`${rootUrl}/${id}/list.htm`); const $ = load(response.data); const bigTitle = $('div [class=lanmuInnerMiddleBigClass_right]') .find('div [portletmode=simpleColumnAttri]') .text() - .replaceAll(/[\s·]/g, '') - .trim(); + .replaceAll(/[\s·]/g, ''); const list = $('li.list_item') .toArray() diff --git a/lib/routes/hrbeu/uae/news.ts b/lib/routes/hrbeu/uae/news.ts index 7019c2716043..10c95560932d 100644 --- a/lib/routes/hrbeu/uae/news.ts +++ b/lib/routes/hrbeu/uae/news.ts @@ -37,11 +37,7 @@ async function handler(ctx) { const host = 'http://uae.hrbeu.edu.cn'; const url = `${host}/${id}.htm`; - const response = await got(url, { - headers: { - Referer: host, - }, - }); + const response = await got(url); const $ = load(response.data); const title = $('h2').text(); diff --git a/lib/routes/hrbeu/ugs/news.ts b/lib/routes/hrbeu/ugs/news.ts index c42946f76002..1e862963e2e5 100644 --- a/lib/routes/hrbeu/ugs/news.ts +++ b/lib/routes/hrbeu/ugs/news.ts @@ -132,11 +132,7 @@ async function handler(ctx) { const author = ctx.req.param('author') || 'gztz'; const category = ctx.req.param('category') || 'all'; const link = baseUrl + authorMap[author][category] + '/list.htm'; - const response = await got(link, { - headers: { - Referer: baseUrl, - }, - }); + const response = await got(link); const $ = load(response.data); const list = $('.wp_article_list_table .border9') @@ -157,7 +153,7 @@ async function handler(ctx) { const response = await got(item.link); const $ = load(response.data); - item.description = $('.wp_articlecontent').html().trim(); + item.description = $('.wp_articlecontent').html(); } else { item.description = '此链接为文件,请点击下载'; } diff --git a/lib/routes/hrbeu/yjsy/list.ts b/lib/routes/hrbeu/yjsy/list.ts index 0535096611cb..b70b75f772ff 100644 --- a/lib/routes/hrbeu/yjsy/list.ts +++ b/lib/routes/hrbeu/yjsy/list.ts @@ -36,11 +36,7 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const response = await got(`${rootUrl}/${id}/list.htm`, { - headers: { - Referer: rootUrl, - }, - }); + const response = await got(`${rootUrl}/${id}/list.htm`); const $ = load(response.data); diff --git a/lib/routes/hrbust/cs.ts b/lib/routes/hrbust/cs.ts index 042272326e6d..854a0d11f89e 100644 --- a/lib/routes/hrbust/cs.ts +++ b/lib/routes/hrbust/cs.ts @@ -54,7 +54,7 @@ async function handler(ctx) { .map((item) => { const element = $(item); const link = new URL(element.find('a').attr('href'), rootUrl).href; - const pubDateText = element.find('span.news_meta').text().trim(); + const pubDateText = element.find('span.news_meta').text(); const pubDate = pubDateText ? timezone(parseDate(pubDateText), 8) : null; return { title: element.find('a').text().trim(), diff --git a/lib/routes/hrbust/jwzx.ts b/lib/routes/hrbust/jwzx.ts index 68f4a0a0efd0..b9282b9bf42b 100644 --- a/lib/routes/hrbust/jwzx.ts +++ b/lib/routes/hrbust/jwzx.ts @@ -59,7 +59,7 @@ async function handler(ctx) { const element = $(item); const link = new URL(element.find('a').attr('href'), rootUrl).href; const title = element.find('a').text().trim(); - const pubDateText = element.find('span').text().trim(); + const pubDateText = element.find('span').text(); const pubDate = timezone(parseDate(pubDateText), 8); return { title, diff --git a/lib/routes/huggingface/blog-community.ts b/lib/routes/huggingface/blog-community.ts index 35974348d536..5811760b1a5f 100644 --- a/lib/routes/huggingface/blog-community.ts +++ b/lib/routes/huggingface/blog-community.ts @@ -99,7 +99,7 @@ async function handler(ctx) { $('.mb-4, .mb-6, .not-prose, h1').remove(); return { ...item, - description: $('.blog-content').html() ?? undefined, + description: $('.blog-content').html(), }; }) ) diff --git a/lib/routes/huggingface/blog-zh.ts b/lib/routes/huggingface/blog-zh.ts index 7913d2729d5c..3ad730562484 100644 --- a/lib/routes/huggingface/blog-zh.ts +++ b/lib/routes/huggingface/blog-zh.ts @@ -73,7 +73,7 @@ async function handler() { $('.mb-4, .mb-6, .not-prose, h1').remove(); return { ...item, - description: $('.blog-content').html() ?? undefined, + description: $('.blog-content').html(), }; }) ) diff --git a/lib/routes/huggingface/blog.ts b/lib/routes/huggingface/blog.ts index ca3d49764ba7..18050ee46918 100644 --- a/lib/routes/huggingface/blog.ts +++ b/lib/routes/huggingface/blog.ts @@ -74,7 +74,7 @@ async function handler() { $('.mb-4, .mb-6, .not-prose, h1').remove(); return { ...item, - description: $('.blog-content').html() ?? undefined, + description: $('.blog-content').html(), }; }) ) diff --git a/lib/routes/huijin-inv/news.ts b/lib/routes/huijin-inv/news.ts index d6ac69b275ba..6ba1d575cb94 100644 --- a/lib/routes/huijin-inv/news.ts +++ b/lib/routes/huijin-inv/news.ts @@ -33,7 +33,7 @@ async function handler(): Promise<Data> { const indexPage = await ofetch(redirectURL); const $: CheerioAPI = load(indexPage); const title = $('title').text()?.trim(); - const author = $('div.logo a').attr('title')?.trim(); + const author = $('div.logo a').attr('title'); const items: DataItem[] = $('div.infor-list-item') .toArray() .map((listItem) => { diff --git a/lib/routes/humanlayer/blog.ts b/lib/routes/humanlayer/blog.ts index 270ce63ea137..f0ce67a1250f 100644 --- a/lib/routes/humanlayer/blog.ts +++ b/lib/routes/humanlayer/blog.ts @@ -42,9 +42,9 @@ async function handler() { .map((el) => { const $el = $(el); const href = $el.attr('href')!; - const title = $el.find('h2').text().trim(); - const metaLine = $el.find('p.text-sm').text().trim(); - const description = $el.find('p[style]').text().trim(); + const title = $el.find('h2').text(); + const metaLine = $el.find('p.text-sm').text(); + const description = $el.find('p[style]').text(); // meta format: "Author · Date · Read time · #tag1 #tag2" const parts = metaLine.split('·').map((s) => s.trim()); diff --git a/lib/routes/hunau/utils/news-content.ts b/lib/routes/hunau/utils/news-content.ts index 9051ff307e72..177a7a3e54f2 100644 --- a/lib/routes/hunau/utils/news-content.ts +++ b/lib/routes/hunau/utils/news-content.ts @@ -20,7 +20,7 @@ export const newsContent = async (link, department = '') => { } // 解析日期 - const extractDate = ($('.info').first().html()?.match(reg) || [])[0]; + const extractDate = ($('.info').html()?.match(reg) || [])[0]; const pubDate = timezone(parseDate(extractDate, 'YYYY-MM-DD', 'zh-cn'), 8); // 解析文章 const newsContent = $(element).first(); diff --git a/lib/routes/ieee/journal.ts b/lib/routes/ieee/journal.ts index e1299e00f8f1..18e63063648e 100644 --- a/lib/routes/ieee/journal.ts +++ b/lib/routes/ieee/journal.ts @@ -45,7 +45,7 @@ async function handler(ctx) { const $ = load(response); const target = $('script[type="text/javascript"]:contains("xplGlobal.document.metadata")'); - const code = target.text() || ''; + const code = target.text(); // 捕获等号右侧的 JSON(最小匹配直到紧随的分号) const m = code.match(/xplGlobal\.document\.metadata\s*=\s*(\{[\s\S]*?\})\s*;/); diff --git a/lib/routes/iheima/index.ts b/lib/routes/iheima/index.ts index 2c231fc7d48a..4dec9456e8c7 100644 --- a/lib/routes/iheima/index.ts +++ b/lib/routes/iheima/index.ts @@ -21,7 +21,6 @@ async function handler() { responseType: 'json', headers: { Accept: 'application/json, text/javascript, */*; q=0.01', - Referer: 'https://www.iheima.com/', 'X-Requested-With': 'XMLHttpRequest', }, }); diff --git a/lib/routes/iiilab/index.ts b/lib/routes/iiilab/index.ts index baf94ca659e1..ac3235e07a97 100644 --- a/lib/routes/iiilab/index.ts +++ b/lib/routes/iiilab/index.ts @@ -32,7 +32,7 @@ async function handler() { item: '.aw-common-list > div', title: `$('a').first().text()`, link: `$('a').first().attr('href')`, - description: `$('.markitup-box').first().text()`, + description: `$('.markitup-box').text()`, pubDate: `parseDate($('.text-color-999').first().text(), 'YYYY-MM-DD HH:mm')`, guid: Buffer.from(`$('a').attr('href')`).toString('base64'), }, diff --git a/lib/routes/in-en/index.ts b/lib/routes/in-en/index.ts index 9e52bf56170d..57681e6d7c7f 100644 --- a/lib/routes/in-en/index.ts +++ b/lib/routes/in-en/index.ts @@ -69,14 +69,14 @@ export const route: Route = { const $el = $(el); const $a = $el.find('.listTxt h5 a'); const link = $a.attr('href') ?? ''; - const title = $a.attr('title')?.trim() || $a.text().trim(); + const title = $a.attr('title') || $a.text(); const pubDateRaw = $el.find('.listTxt .prompt > i').text().trim(); - const author = $el.find('.listTxt .prompt > span').first().text().replace('来源:', '').trim(); + const author = $el.find('.listTxt .prompt > span').first().text().replace('来源:', ''); const category = $el .find('.listTxt .prompt > span:not(:first-of-type) em a') .toArray() - .map((a) => $(a).text().trim()) + .map((a) => $(a).text()) .filter(Boolean); return { @@ -95,9 +95,9 @@ export const route: Route = { const detail = await ofetch(item.link!); const $d = load(detail); - item.description = $d('#article').html() ?? undefined; + item.description = $d('#article').html(); - const detailAuthor = $d('p.source a').text().trim(); + const detailAuthor = $d('p.source a').text(); if (detailAuthor) { item.author = detailAuthor; } diff --git a/lib/routes/inceptionlabs/blog.ts b/lib/routes/inceptionlabs/blog.ts index f6e33ca3f693..1958907526c1 100644 --- a/lib/routes/inceptionlabs/blog.ts +++ b/lib/routes/inceptionlabs/blog.ts @@ -51,7 +51,7 @@ async function handler() { const title = $el .find('h6.framer-text') .toArray() - .map((h6) => $(h6).text().trim()) + .map((h6) => $(h6).text()) .find((text) => text && text !== 'Read story'); if (!title) { @@ -69,11 +69,11 @@ async function handler() { const dateBlocks = $el .find('[data-framer-name="Date"] p.framer-text') .toArray() - .map((p) => $(p).text().trim()); + .map((p) => $(p).text()); // Category: featured cards use data-framer-name="Category"; standard cards use dateBlocks[0] const categoryEl = $el.find('[data-framer-name="Category"]'); - const category = categoryEl.length > 0 ? categoryEl.first().text().trim() : (dateBlocks[0] ?? ''); + const category = categoryEl.length > 0 ? categoryEl.first().text() : (dateBlocks[0] ?? ''); // Date: featured cards have a single Date block (the date itself); standard cards have it at index 1 const pubDate = dateBlocks.length >= 2 ? dateBlocks[1] : (dateBlocks[0] ?? ''); @@ -94,10 +94,10 @@ async function handler() { const $post = load(postHtml); // Full article content - const contentHtml = $post('[data-framer-name="Content"]').first().html() ?? ''; + const contentHtml = $post('[data-framer-name="Content"]').first().html(); // Author name from the first [data-framer-name="Author"] RichTextContainer - const author = $post('[data-framer-name="Author"] p.framer-text').first().text().trim(); + const author = $post('[data-framer-name="Author"] p.framer-text').first().text(); return { ...post, diff --git a/lib/routes/infoq/recommend.ts b/lib/routes/infoq/recommend.ts index 830f0a7a3683..2296ddd42601 100644 --- a/lib/routes/infoq/recommend.ts +++ b/lib/routes/infoq/recommend.ts @@ -33,9 +33,6 @@ async function handler(ctx) { const pageUrl = 'https://www.infoq.cn'; const resp = await got.post(apiUrl, { - headers: { - Referer: pageUrl, - }, json: { size: ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30, }, diff --git a/lib/routes/infzm/hot.ts b/lib/routes/infzm/hot.ts index 6af39dadbb13..4242f9130c47 100644 --- a/lib/routes/infzm/hot.ts +++ b/lib/routes/infzm/hot.ts @@ -24,9 +24,6 @@ async function handler(): Promise<Data> { const { data } = await got<ContentsResponse>({ method: 'get', url: 'https://www.infzm.com/hot_contents', - headers: { - Referer: link, - }, }); const resultItem = await fetchArticles(data.data.hot_contents); diff --git a/lib/routes/infzm/utils.ts b/lib/routes/infzm/utils.ts index 1ccd8e08a335..e263d55262cf 100644 --- a/lib/routes/infzm/utils.ts +++ b/lib/routes/infzm/utils.ts @@ -25,7 +25,7 @@ export async function fetchArticles(data) { const $ = load(response.data); return { title: subject, - description: $('div.nfzm-content__content').html() ?? '', + description: $('div.nfzm-content__content').html(), pubDate: timezone(publish_time, 8).toUTCString(), link, author, diff --git a/lib/routes/inoreader/index.ts b/lib/routes/inoreader/index.ts index 0e3ed6f21827..205f462482b3 100644 --- a/lib/routes/inoreader/index.ts +++ b/lib/routes/inoreader/index.ts @@ -35,7 +35,6 @@ async function handler(ctx) { title: $('.header_text').text().trim(), link: currentUrl, item: entries.toArray().map((item) => { - const content = $(item).clone(); const header = $(item).prev(); const pubDate = $('div.article_author .au1', header) .contents() @@ -48,7 +47,7 @@ async function handler(ctx) { link: $('a.title_link', header).attr('href'), author: $('div.article_author span span', header).text().trim() + ' via ' + $('div.article_author a.feed_link', header).text().trim(), pubDate: parseDate(pubDate, ['MMM DD YYYY HH:mm:ss', 'HH:mm:ss']), - description: $(content).html(), + description: $(item).html(), }; }), allowEmpty: true, diff --git a/lib/routes/investor/index.ts b/lib/routes/investor/index.ts index 0f573059c45f..64cc265a9e3f 100644 --- a/lib/routes/investor/index.ts +++ b/lib/routes/investor/index.ts @@ -52,8 +52,8 @@ export const handler = async (ctx: Context): Promise<Data> => { const detailResponse = await ofetch(item.link); const $$: CheerioAPI = load(detailResponse); - const title: string = $$('div.text_content_detail_title h1').text() ?? item.title; - const description: string | undefined = $$('div.trs_editor_view').html() ?? undefined; + const title: string = $$('div.text_content_detail_title h1').text(); + const description = $$('div.trs_editor_view').html(); const pubDateStr: string | undefined = $$('div.base_info_left span') .toArray() .some((el) => $$(el).text().includes('时间')) diff --git a/lib/routes/ipsw.dev/index.tsx b/lib/routes/ipsw.dev/index.tsx index 3a934b787cca..a2c0589d75bc 100644 --- a/lib/routes/ipsw.dev/index.tsx +++ b/lib/routes/ipsw.dev/index.tsx @@ -23,9 +23,6 @@ async function handler(ctx) { const resp = await got({ method: 'get', url: link, - headers: { - Referer: 'https://ipsw.dev/', - }, }); const $ = load(resp.data); diff --git a/lib/routes/ipsw/index.ts b/lib/routes/ipsw/index.ts index 657da347c84f..53f8199efe31 100644 --- a/lib/routes/ipsw/index.ts +++ b/lib/routes/ipsw/index.ts @@ -45,9 +45,6 @@ async function handler(ctx) { const response = await got({ method: 'get', url: link, - headers: { - Referer: host, - }, }); const $ = load(response.data); const list = pname.includes(',') @@ -79,14 +76,11 @@ async function handler(ctx) { const response = await got({ method: 'get', url: itemUrl, - headers: { - Referer: host, - }, }); const $ = load(response.data); const description = $('div.selector__wizard').html(); let removeString; - removeString = pname.includes(',') ? $('div.table-responsive table tr').first().find('td').text().trim() : $('tr.firmware').first().find('td').eq(2).text().trim(); + removeString = pname.includes(',') ? $('div.table-responsive table tr').first().find('td').text().trim() : $('tr.firmware').first().find('td').eq(2).text(); // 处理发布日期,以表格第一行的日期为最新的发布日期 removeString = removeString.replace('th', '').replace('nd', '').replace('st', '').replace('rd', ''); const rdate = removeString.replaceAll(' ', ','); diff --git a/lib/routes/itsec/news.ts b/lib/routes/itsec/news.ts index 6677ffec05fd..be4ec4191ad3 100644 --- a/lib/routes/itsec/news.ts +++ b/lib/routes/itsec/news.ts @@ -40,10 +40,10 @@ async function handler() { .toArray() .map((item) => { const $item = $(item); - const $link = $item.find('a').first(); + const $link = $item.find('a'); const title = $link.text() || $link.prop('title'); const href = $link.prop('href'); - const date = $item.find('span').first().text().trim(); + const date = $item.find('span').text(); if (!title || !href) { return null; @@ -66,10 +66,10 @@ async function handler() { const $detail = load(detailResponse); const title = $detail('.article-tit').text() || item.title; - const date = $detail('.article .date').text().trim(); + const date = $detail('.article .date').text(); const author = $detail('.article .from').last().text().trim(); - $detail('#js_content script, #js_content style').remove(); + $detail('#js_content style').remove(); const description = $detail('#js_content .TRS_Editor').html() || $detail('#js_content').html(); return { diff --git a/lib/routes/j-test/news.ts b/lib/routes/j-test/news.ts index f8b7088a9a9c..716e6c149e1c 100644 --- a/lib/routes/j-test/news.ts +++ b/lib/routes/j-test/news.ts @@ -51,7 +51,7 @@ async function handler() { cache.tryGet(item.link, async () => { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.content > table').html() ?? ''; + item.description = $('.content > table').html(); return item; }) ) diff --git a/lib/routes/jandan/index.ts b/lib/routes/jandan/index.ts index 05e6d163066c..c03ce7ec94b9 100644 --- a/lib/routes/jandan/index.ts +++ b/lib/routes/jandan/index.ts @@ -9,7 +9,7 @@ export const route: Route = { path: '/', example: '/jandan', name: 'Feed', - maintainers: ['nczitzk', 'bigfei', 'pseudoyu'], + maintainers: ['lonelykid', 'nczitzk', 'bigfei', 'pseudoyu'], parameters: {}, features: { requireConfig: false, @@ -33,7 +33,7 @@ async function handler(): Promise<{ link: string; item: DataItem[]; }> { - const rootUrl = 'http://i.jandan.net'; + const rootUrl = 'https://i.jandan.net'; const feed = await parser.parseURL(`${rootUrl}/feed/`); const items = await Promise.all( feed.items.map((item) => @@ -52,7 +52,7 @@ async function handler(): Promise<{ }); const single: DataItem = { title: item.title || '', - description: $('.entry').html() || '', + description: $('.entry').html(), pubDate: item.pubDate, link: item.link, author: item['dc:creator'], diff --git a/lib/routes/jandan/section.ts b/lib/routes/jandan/section.ts index bb9432cc3590..3afd29339c5f 100644 --- a/lib/routes/jandan/section.ts +++ b/lib/routes/jandan/section.ts @@ -6,7 +6,7 @@ export const route: Route = { path: '/:category/:type?', example: '/jandan/top', name: 'Section', - maintainers: ['nczitzk', 'pseudoyu'], + maintainers: ['kobemtl', 'Xuanwo', 'xyqfer', '9uanhuo', 'nczitzk', 'pseudoyu'], parameters: { category: { description: '板块', @@ -27,6 +27,10 @@ export const route: Route = { label: '随手拍', value: 'ooxx', }, + { + label: '女装', + value: 'beauty', + }, { label: '无聊图', value: 'pic', @@ -82,37 +86,22 @@ async function handler(ctx): Promise<{ category = category.replace(/#.*$/, ''); const type = ctx.req.param('type') ?? '4hr'; - const rootUrl = 'http://i.jandan.net'; + const rootUrl = 'https://i.jandan.net'; const currentUrl = `${rootUrl}/${category}`; - let result: { title: string; items: DataItem[] }; + let result: { title: string; items: DataItem[]; link?: string }; - try { - if (category === 'top') { - result = await handleTopSection(rootUrl, type); - } else if (category === 'bbs') { - result = await handleForumSection(rootUrl); - } else { - result = await handleCommentSection(rootUrl, category); - } - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - result = { - title: `煎蛋 - ${category}`, - items: [ - { - title: `抓取出错: ${category}`, - description: `抓取 ${category} 分区时出现错误: ${errorMessage}`, - link: currentUrl, - pubDate: new Date(), - }, - ], - }; + if (category === 'top') { + result = await handleTopSection(rootUrl, type); + } else if (category === 'bbs') { + result = await handleForumSection(rootUrl); + } else { + result = await handleCommentSection(rootUrl, category); } return { title: result.title, - link: currentUrl, + link: result.link ?? currentUrl, item: result.items, }; } diff --git a/lib/routes/jandan/utils.ts b/lib/routes/jandan/utils.ts index e68451e3fa4e..66cb3da01624 100644 --- a/lib/routes/jandan/utils.ts +++ b/lib/routes/jandan/utils.ts @@ -1,45 +1,35 @@ import { load } from 'cheerio'; +import sanitizeHtml from 'sanitize-html'; import type { DataItem } from '@/types'; +import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; /** - * Extract page ID from script tags in HTML + * Extract page ID from script tags and title */ -export const extractPageId = async (url: string, referer: string): Promise<string> => { - const response = await ofetch(url, { - headers: { - Referer: referer, - Accept: 'application/json, text/plain, */*', - }, - }); +export const extractPageMeta = (url: string) => + cache.tryGet(`jandan:pageMeta:${url}`, async () => { + const response = await ofetch(url); - const $ = load(response); - let pageId = ''; + const $ = load(response); - $('script').each((_, script) => { - const content = $(script).html() || ''; - const match = content.match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/); - if (match) { - pageId = match[1]; - } + return { + pageId: + $('script:contains("PAGE")') + .text() + .match(/PAGE\s*=\s*\{\s*id\s*:\s*(\d+)\s*\}/)?.[1] ?? '', + title: $('title').text().trim(), + }; }); - return pageId; -}; - /** * Handle the top section (热榜) */ export const handleTopSection = async (rootUrl: string, type: string): Promise<{ title: string; items: DataItem[] }> => { const apiUrl = `${rootUrl}/api/top/${type}`; - const response = await ofetch(apiUrl, { - headers: { - Referer: rootUrl, - Accept: 'application/json, text/plain, */*', - }, - }); + const response = await ofetch(apiUrl); let title = '热榜'; switch (type) { @@ -54,189 +44,86 @@ export const handleTopSection = async (rootUrl: string, type: string): Promise<{ break; } - if (response.code === 0 && response.data && Array.isArray(response.data)) { - const items = response.data.map((item) => { - const content = item.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn'))); + if (response.code !== 0) { + throw new Error(`未能获取热榜数据: ${title}`); + } - return { - author: item.author, - title: `${item.author}: ${content.replaceAll(/<[^>]+>/g, '')}`, - description: content, - pubDate: parseDate(item.date), - link: `${rootUrl}/t/${item.id}`, - } as DataItem; - }); + const items = response.data.map((item) => { + const content = item.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn'))); - return { title, items }; - } + return { + author: item.author, + title: `${item.author}: ${sanitizeHtml(content, { allowedTags: [], allowedAttributes: {} })}`, + description: content, + pubDate: parseDate(item.date_gmt), + link: `${rootUrl}/t/${item.id}`, + } as DataItem; + }); - return { - title, - items: [ - { - title: `获取失败: ${title}`, - description: '未能获取热榜数据', - link: `${rootUrl}/top`, - pubDate: new Date(), - }, - ], - }; + return { title, items }; }; /** * Handle the forum/bbs section (鱼塘) */ -export const handleForumSection = async (rootUrl: string): Promise<{ title: string; items: DataItem[] }> => { +export const handleForumSection = async (rootUrl: string): Promise<{ title: string; items: DataItem[]; link: string }> => { const title = '煎蛋 - 鱼塘'; - const currentUrl = `${rootUrl}/bbs`; - - try { - const forumId = await extractPageId(currentUrl, rootUrl); - - if (!forumId) { - return { - title, - items: [ - { - title: `获取失败: ${title}`, - description: '无法获取论坛ID', - link: currentUrl, - pubDate: new Date(), - }, - ], - }; - } - - const apiUrl = `${rootUrl}/api/forum/posts/${forumId}?page=1`; - const forumData = await ofetch(apiUrl, { - headers: { - Referer: currentUrl, - Accept: 'application/json, text/plain, */*', - }, - }); - - if (forumData.code === 0 && forumData.data && forumData.data.list && Array.isArray(forumData.data.list)) { - const items = forumData.data.list.map((post) => { - const content = post.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn'))); - - return { - author: post.author_name, - title: post.title || `${post.author_name}发表了新主题`, - description: content, - pubDate: parseDate(post.update_time || post.create_time), - link: `${rootUrl}/bbs#/topic/${post.post_id}`, - category: post.reply_count > 0 ? [`${post.reply_count}条回复`] : undefined, - } as DataItem; - }); - - return { title, items }; - } + const currentUrl = `${rootUrl}/new/forum`; - return { - title, - items: [ - { - title: `获取失败: ${title}`, - description: '未能获取鱼塘数据', - link: currentUrl, - pubDate: new Date(), - }, - ], - }; - } catch (error) { - return { - title, - items: [ - { - title: '解析错误: 鱼塘', - description: `解析鱼塘页面时出错: ${error instanceof Error ? error.message : String(error)}`, - link: currentUrl, - pubDate: new Date(), - }, - ], - }; + const forumId = 112928; + const apiUrl = `${rootUrl}/api/forum/posts/${forumId}?page=1`; + const forumData = await ofetch(apiUrl); + + if (forumData.code !== 0) { + throw new Error('未能获取鱼塘数据'); } + + const items = forumData.data.list.map( + (post) => + ({ + author: post.author_name, + title: post.title, + pubDate: parseDate(post.create_time), + updated: parseDate(post.update_time), + link: `${rootUrl}/new/forum/topic/${post.post_id}`, + category: post.reply_count > 0 ? [`${post.reply_count}条回复`] : undefined, + }) as DataItem + ); + + return { title, items, link: currentUrl }; }; /** - * Handle other sections (问答, 树洞, 随手拍, 无聊图) + * Handle other sections (问答, 树洞, 随手拍, 女装, 无聊图) */ export const handleCommentSection = async (rootUrl: string, category: string): Promise<{ title: string; items: DataItem[] }> => { const currentUrl = `${rootUrl}/${category}`; - try { - const pageId = await extractPageId(currentUrl, rootUrl); + const { pageId, title: pageTitle } = await extractPageMeta(currentUrl); + const title = pageTitle || `煎蛋 - ${category}`; - const response = await ofetch(currentUrl, { - headers: { - Referer: rootUrl, - Accept: 'application/json, text/plain, */*', - }, - }); + if (!pageId) { + throw new Error('无法从页面中获取到帖子ID,可能网站结构已变更'); + } - const $ = load(response); - const title = String($('title').text().trim()) || `煎蛋 - ${category}`; - - if (!pageId) { - return { - title, - items: [ - { - title: `无法解析: ${title}`, - description: '无法从页面中获取到帖子ID,可能网站结构已变更', - link: currentUrl, - pubDate: new Date(), - }, - ], - }; - } - - const apiUrl = `${rootUrl}/api/comment/post/${pageId}?order=desc&page=1`; - const commentsData = await ofetch(apiUrl, { - headers: { - Referer: currentUrl, - Accept: 'application/json, text/plain, */*', - }, - }); - - if (commentsData.code === 0 && commentsData.data && commentsData.data.list && Array.isArray(commentsData.data.list)) { - const items = commentsData.data.list.map((comment) => { - const content = comment.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn'))); - - return { - author: comment.author, - title: `${comment.author}: ${content.replaceAll(/<[^>]+>/g, '')}`, - description: content, - pubDate: parseDate(comment.date_gmt || comment.date), - link: `${rootUrl}/t/${comment.id}`, - } as DataItem; - }); - - return { title, items }; - } + const apiUrl = `${rootUrl}/api/comment/post/${pageId}?order=desc&page=0`; + const commentsData = await ofetch(apiUrl); - return { - title, - items: [ - { - title: `暂无内容: ${title || category}`, - description: '没有获取到内容,可能需要更新解析规则', - link: currentUrl, - pubDate: new Date(), - }, - ], - }; - } catch { - return { - title: `煎蛋 - ${category}`, - items: [ - { - title: `解析错误: ${category}`, - description: '解析页面时出错', - link: currentUrl, - pubDate: new Date(), - }, - ], - }; + if (commentsData.code !== 0) { + throw new Error('没有获取到内容,可能需要更新解析规则'); } + + const items = commentsData.data.list.map((comment) => { + const content = comment.content.replaceAll(/img src="(.*?)"/g, (match, src) => match.replace(src, () => src.replace(/^https?:\/\/(\w+)\.moyu\.im/, 'https://$1.sinaimg.cn'))); + + return { + author: comment.author, + title: `${comment.author}: ${sanitizeHtml(content, { allowedTags: [], allowedAttributes: {} })}`, + description: content, + pubDate: parseDate(comment.date_gmt), + link: `${rootUrl}/t/${comment.id}`, + } as DataItem; + }); + + return { title, items }; }; diff --git a/lib/routes/jianshu/home.ts b/lib/routes/jianshu/home.ts index 6afc77c95223..a59d7a067c6a 100644 --- a/lib/routes/jianshu/home.ts +++ b/lib/routes/jianshu/home.ts @@ -36,9 +36,6 @@ async function handler() { const response = await got({ method: 'get', url: 'https://www.jianshu.com', - headers: { - Referer: 'https://www.jianshu.com', - }, }); const data = response.data; diff --git a/lib/routes/jiemian/account.ts b/lib/routes/jiemian/account.ts index 139e2425c0a0..d71c41b982e9 100644 --- a/lib/routes/jiemian/account.ts +++ b/lib/routes/jiemian/account.ts @@ -1,9 +1,20 @@ -import type { Route } from '@/types'; +import type { Context } from 'hono'; -import { handler } from './common'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import type { Data, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { fetchArticle } from './common'; + +const categoryMap: Record<string, { ckey: string; name: string }> = { + '1': { ckey: 'finance_config_index', name: '财经号' }, + '2': { ckey: 'city_config_index', name: '城市号' }, + '3': { ckey: 'media_config_index', name: '媒体号' }, +}; export const route: Route = { - path: '/account/main/1', + path: '/account/main/:id', parameters: { id: '分类 id,见下表,可在对应分类页 URL 中找到' }, name: '界面号', example: '/jiemian/account/main/1', @@ -13,3 +24,39 @@ export const route: Route = { | ----------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------- | | 1 | 2 | 3 |`, }; + +async function handler(ctx: Context): Promise<Data> { + const { id } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; + + const category = categoryMap[id]; + if (!category) { + throw new InvalidParameterError('Invalid id'); + } + + const response = await ofetch<string>('https://papi.jiemian.com/page/api/officialAccount/get_index_lists', { + query: { + ckey: category.ckey, + page: 1, + }, + }); + + const list = JSON.parse(response.slice(response.indexOf('(') + 1, response.lastIndexOf(')'))) + .result.slice(0, limit) + .map((article) => ({ + title: article.title, + description: article.summary, + link: article.url, + pubDate: parseDate(article.publish_time, 'X'), + author: article.source_name, + image: article.image, + })); + + const items = await Promise.all(list.map((item) => fetchArticle(item))); + + return { + title: `界面新闻 - ${category.name}`, + link: `https://www.jiemian.com/account/main/${id}.html`, + item: items, + }; +} diff --git a/lib/routes/jiemian/common.tsx b/lib/routes/jiemian/common.tsx index 1805ee95a6d5..18b1ff7934d3 100644 --- a/lib/routes/jiemian/common.tsx +++ b/lib/routes/jiemian/common.tsx @@ -1,3 +1,4 @@ +import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; @@ -7,14 +8,13 @@ import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +const rootUrl = 'https://www.jiemian.com'; + export const handler = async (ctx): Promise<Data> => { - const { category, id } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50; - const rootUrl = 'https://www.jiemian.com'; - // Reason: lists.ts uses :id param, other routes use :category or hardcoded paths - const pathSegment = category || (id ? `lists/${id}` : ''); - const currentUrl = new URL(pathSegment ? `${pathSegment}.html` : '', rootUrl).href; + const category = ctx.req.path.replace(/^\/jiemian\//, ''); + const currentUrl = new URL(category ? `${category}.html` : '', rootUrl).href; const response = await ofetch(currentUrl); @@ -45,62 +45,85 @@ export const handler = async (ctx): Promise<Data> => { items = await Promise.all( Object.values(items) .slice(0, limit) - .map((item) => - cache.tryGet(item.link, async () => { - const detailResponse = await ofetch(item.link); - - const content = load(detailResponse); - const image = content('div.article-img img').first(); - const video = content('#video-player').first(); - content('p.report-view').remove(); - - item.title = content('div.article-header h1').eq(0).text(); - item.description = renderDescription({ - image: image - ? { - src: image.prop('src'), - alt: image.next('p').text() || item.title, - } - : undefined, - video: video - ? { - src: video.prop('data-url'), - poster: video.prop('data-poster'), - width: video.prop('width'), - height: video.prop('height'), - } - : undefined, - intro: content('div.article-header p').text(), - description: content('div.article-content').html(), - }); - item.author = content('span.author') - .first() - .find('a') - .toArray() - .map((a) => content(a).text()) - .join('/'); - item.category = content('meta.meta-container a') - .toArray() - .map((c) => content(c).text()); - item.pubDate = parseDate(content('div.article-info span[data-article-publish-time]').prop('data-article-publish-time'), 'X'); - item.upvotes = content('span.opt-praise__count').text() ? Number(content('span.opt-praise__count').text()) : 0; - item.comments = content('span.opt-comment__count').text() ? Number(content('span.opt-comment__count').text()) : 0; - - return item; - }) - ) + .map((item) => fetchArticle(item)) ); + return { + item: items, + ...feedMeta($, currentUrl), + }; +}; + +export const parseCardList = (html: string) => { + const $ = load(html, null, false); + + return $('li.card-list') + .toArray() + .map((el) => { + const $item = $(el); + return { + title: $item.find('h3.card-list__title').text(), + link: $item.find('.card-list__content > a').attr('href'), + image: $item.find('.card-list__img img').attr('src'), + }; + }); +}; + +export const fetchArticle = (item) => + cache.tryGet(item.link, async () => { + const detailResponse = await ofetch(item.link); + + const content = load(detailResponse); + const image = content('div.article-img img').first(); + const video = content('#video-player').first(); + content('p.report-view').remove(); + + item.title = content('div.article-header h1').eq(0).text(); + item.description = renderDescription({ + image: image + ? { + src: image.prop('src'), + alt: image.next('p').text() || item.title, + } + : undefined, + video: video + ? { + src: video.prop('data-url'), + poster: video.prop('data-poster'), + width: video.prop('width'), + height: video.prop('height'), + } + : undefined, + intro: content('div.article-header p').text(), + description: content('div.article-content').html(), + }); + item.author = content('span.author') + .first() + .find('a') + .toArray() + .map((a) => content(a).text()) + .join('/'); + item.category = content('meta.meta-container a') + .toArray() + .map((c) => content(c).text()); + // article: div.article-info, video: div.article-header__info + item.pubDate = parseDate(content('div.article-info, div.article-header__info').find('span[data-article-publish-time]').first().prop('data-article-publish-time'), 'X'); + item.upvotes = content('span.opt-praise__count').text() ? Number(content('span.opt-praise__count').text()) : 0; + item.comments = content('span.opt-comment__count').text() ? Number(content('span.opt-comment__count').text()) : 0; + + return item; + }); + +export const feedMeta = ($: CheerioAPI, currentUrl: string) => { const title = $('title').text(); const titleSplits = title.split(/_/); - const image = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; + const image = new URL($('link[rel="icon"]').prop('href')!, rootUrl).href; return { - item: items, title, link: currentUrl, description: $('meta[name="description"]').prop('content'), - language: $('html').prop('lang'), + language: $('html').prop('lang') as Data['language'], image, icon: image, logo: image, @@ -109,7 +132,7 @@ export const handler = async (ctx): Promise<Data> => { }; }; -const renderDescription = ({ +export const renderDescription = ({ image, intro, video, diff --git a/lib/routes/jiemian/lists.ts b/lib/routes/jiemian/lists.ts index 7e7d7c986abd..7af7b051c310 100644 --- a/lib/routes/jiemian/lists.ts +++ b/lib/routes/jiemian/lists.ts @@ -1,6 +1,10 @@ -import type { Route } from '@/types'; +import { load } from 'cheerio'; +import type { Context } from 'hono'; -import { handler } from './common'; +import type { Data, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; + +import { feedMeta, fetchArticle, handler as commonHandler, parseCardList } from './common'; export const route: Route = { path: '/lists/:id', @@ -97,3 +101,71 @@ export const route: Route = { :::`, }; + +async function handler(ctx: Context): Promise<Data> { + const { id } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; + + const currentUrl = `https://www.jiemian.com/lists/${id}.html`; + const pageResponse = await ofetch(currentUrl); + const $ = load(pageResponse); + + const loadMore = $('div.channel-load-more'); + if (loadMore.length === 0) { + const kbId = id.endsWith('kb') + ? id + : $('a.active[data-url]') + .attr('data-url') + ?.match(/lists\/(\d+kb)\.html/)?.[1]; + if (!kbId) { + return commonHandler(ctx); + } + + const kuaixunResponse = await ofetch('https://papi.jiemian.com/page/api/kuaixun/getlistmore', { + query: { + cid: kbId, + start_time: Math.floor(Date.now() / 1000), + page: 1, + tagid: kbId.replace('kb', ''), + }, + }); + + const items = await Promise.all( + kuaixunResponse.result.list.slice(0, limit).map((flash) => + fetchArticle({ + title: flash.title, + link: `https://www.jiemian.com/article/${flash.id}.html`, + }) + ) + ); + + return { + item: items, + ...feedMeta($, currentUrl), + }; + } + + const listResponse = await ofetch<string>('https://a.jiemian.com/index.php', { + query: { + m: 'newLists', + a: 'loadMore', + tid: loadMore.attr('data-tid'), + page: 1, + // request a fixed template so the returned markup is always the card list + tpl: 'sub-card', + cid: id, + repeat: '', + list_type: loadMore.attr('data-list_type'), + }, + }); + + const { html } = JSON.parse(listResponse.slice(listResponse.indexOf('(') + 1, listResponse.lastIndexOf(')'))); + const list = parseCardList(html); + + const items = await Promise.all(list.slice(0, limit).map((item) => fetchArticle(item))); + + return { + item: items, + ...feedMeta($, currentUrl), + }; +} diff --git a/lib/routes/jiemian/special.ts b/lib/routes/jiemian/special.ts index 782221141cab..36ce3dc479cf 100644 --- a/lib/routes/jiemian/special.ts +++ b/lib/routes/jiemian/special.ts @@ -3,8 +3,8 @@ import type { Route } from '@/types'; import { handler } from './common'; export const route: Route = { - path: '/special/1192', - parameters: { id: '分类 id,见下表,可在对应分类页 URL 中找到' }, + path: '/special/:id', + parameters: { id: '分类 id,可在对应分类页 URL 中找到' }, name: '专题', example: '/jiemian/special/1192', maintainers: ['nczitzk', 'pseudoyu'], diff --git a/lib/routes/jiemian/video.ts b/lib/routes/jiemian/video.ts index ed1f2e6dc5a3..ae92c9773a41 100644 --- a/lib/routes/jiemian/video.ts +++ b/lib/routes/jiemian/video.ts @@ -1,9 +1,15 @@ -import type { Route } from '@/types'; +import { load } from 'cheerio'; +import type { Context } from 'hono'; -import { handler } from './common'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { feedMeta, fetchArticle, parseCardList, renderDescription } from './common'; export const route: Route = { - path: '/video/lists/258_1', + path: '/video/lists/:id', parameters: { id: '分类 id,见下表,可在对应分类页 URL 中找到' }, name: '视频', example: '/jiemian/video/lists/258_1', @@ -11,9 +17,86 @@ export const route: Route = { handler, description: `| [界面 Vnews](https://www.jiemian.com/video/lists/258_1.html) | [直播](https://www.jiemian.com/videoLive/lists_1.html) | [箭厂](https://www.jiemian.com/video/lists/195_1.html) | [面谈](https://www.jiemian.com/video/lists/111_1.html) | [品牌创酷](https://www.jiemian.com/video/lists/226_1.html) | [番 茄社](https://www.jiemian.com/video/lists/567_1.html) | | ------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ---------------------------------------------------------- | --------------------------------------------------------- | -| 258\\_1 | videoLive/lists\\_1 | 195\\_1 | 111\\_1 | 226\\_1 | 567\\_1 | +| 258\\_1 | videoLive | 195\\_1 | 111\\_1 | 226\\_1 | 567\\_1 | | [商业微史记](https://www.jiemian.com/video/lists/882_1.html) | | ------------------------------------------------------------ | | 882\\_1 |`, }; + +async function handler(ctx: Context): Promise<Data> { + const { id } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; + + if (id === 'videoLive') { + const currentUrl = 'https://www.jiemian.com/videoLive/lists_1.html'; + const [pageResponse, listResponse] = await Promise.all([ofetch(currentUrl), ofetch('https://papi.jiemian.com/page/api/livevideo/moreHistroyLiveVideo', { parseResponse: JSON.parse })]); + + const items = await Promise.all( + listResponse.data.slice(0, limit).map((live) => + cache.tryGet(live.url, async () => { + const detailResponse = await ofetch<string>('https://a.jiemian.com/index.php', { + query: { + m: 'video_live', + a: 'loadLiveNew', + id: live.id, + }, + }); + const { data } = JSON.parse(detailResponse.slice(detailResponse.indexOf('(') + 1, detailResponse.lastIndexOf(')'))); + + return { + title: live.title, + description: renderDescription({ + image: data.back_url_mp4 ? undefined : { src: live.image, alt: live.title }, + video: data.back_url_mp4 + ? { + src: data.back_url_mp4, + poster: data.image || live.image, + type: 'video/mp4', + } + : undefined, + description: live.summary, + }), + link: live.url, + pubDate: parseDate(live.publish_time, 'X'), + category: [live.cate_name], + image: live.image, + }; + }) + ) + ); + + return { + item: items, + ...feedMeta(load(pageResponse), currentUrl), + }; + } + + const tid = id.split('_', 1)[0]; + const currentUrl = `https://www.jiemian.com/video/lists/${id}.html`; + const [pageResponse, listResponse] = await Promise.all([ + ofetch(currentUrl), + ofetch<string>('https://a.jiemian.com/index.php', { + query: { + m: 'newLists', + a: 'loadMore', + tid, + page: 1, + tpl: 'sub-card', + cid: tid, + repeat: '', + list_type: 'video', + }, + }), + ]); + + const { html } = JSON.parse(listResponse.slice(listResponse.indexOf('(') + 1, listResponse.lastIndexOf(')'))); + const list = parseCardList(html); + + const items = await Promise.all(list.slice(0, limit).map((item) => fetchArticle(item))); + + return { + item: items, + ...feedMeta(load(pageResponse), currentUrl), + }; +} diff --git a/lib/routes/jiemian/vip.ts b/lib/routes/jiemian/vip.ts index 43102dd9407c..2c20b0fe6eec 100644 --- a/lib/routes/jiemian/vip.ts +++ b/lib/routes/jiemian/vip.ts @@ -1,9 +1,13 @@ -import type { Route } from '@/types'; +import { load } from 'cheerio'; +import type { Context } from 'hono'; -import { handler } from './common'; +import type { Data, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; + +import { feedMeta, fetchArticle, parseCardList } from './common'; export const route: Route = { - path: '/pro/lists/12', + path: '/pro/lists/:id', parameters: { id: '分类 id,见下表,可在对应分类页 URL 中找到' }, name: 'VIP', example: '/jiemian/pro/lists/12', @@ -17,3 +21,35 @@ export const route: Route = { | ----------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------- | | 16 | 17 | 18 | 1 | 19 |`, }; + +async function handler(ctx: Context): Promise<Data> { + const { id } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; + + const currentUrl = `https://www.jiemian.com/pro/lists/${id}.html`; + const [pageResponse, listResponse] = await Promise.all([ + ofetch(currentUrl), + ofetch<string>('https://a.jiemian.com/index.php', { + query: { + m: 'newLists', + a: 'loadMore', + tid: id, + page: 1, + tpl: 'sub-card', + cid: '', + repeat: '', + list_type: 'pay', + }, + }), + ]); + + const { html } = JSON.parse(listResponse.slice(listResponse.indexOf('(') + 1, listResponse.lastIndexOf(')'))); + const list = parseCardList(html); + + const items = await Promise.all(list.slice(0, limit).map((item) => fetchArticle(item))); + + return { + item: items, + ...feedMeta(load(pageResponse), currentUrl), + }; +} diff --git a/lib/routes/jinse/lives.ts b/lib/routes/jinse/lives.ts index eff7a75b354e..2ffb85d5d590 100644 --- a/lib/routes/jinse/lives.ts +++ b/lib/routes/jinse/lives.ts @@ -64,35 +64,34 @@ async function handler(ctx) { }, }); - const items = - response.list - .flatMap((l) => l.lives) - .slice(0, limit) - .map((item) => ({ - title: item.content_prefix, - link: new URL(`lives/${item.id}.html`, rootUrl).href, - description: renderDescription({ - images: - item.images?.map((i) => ({ - src: i.url.replace(/_[^\W_]+(\.\w+)$/, '_true$1'), - width: i.width, - height: i.height, - })) ?? [], - description: item.content, - original: item.link - ? { - link: item.link, - name: item.link_name, - } - : undefined, - }), - author: item.show_source_name, - guid: `jinse-lives-${item.id}`, - pubDate: parseDate(item.created_at, 'X'), - upvotes: item.up_counts ?? 0, - downvotes: item.down_counts ?? 0, - comments: item.comment_count ?? 0, - })) ?? []; + const items = response.list + .flatMap((l) => l.lives) + .slice(0, limit) + .map((item) => ({ + title: item.content_prefix, + link: new URL(`lives/${item.id}.html`, rootUrl).href, + description: renderDescription({ + images: + item.images?.map((i) => ({ + src: i.url.replace(/_[^\W_]+(\.\w+)$/, '_true$1'), + width: i.width, + height: i.height, + })) ?? [], + description: item.content, + original: item.link + ? { + link: item.link, + name: item.link_name, + } + : undefined, + }), + author: item.show_source_name, + guid: `jinse-lives-${item.id}`, + pubDate: parseDate(item.created_at, 'X'), + upvotes: item.up_counts ?? 0, + downvotes: item.down_counts ?? 0, + comments: item.comment_count ?? 0, + })); const { data: currentResponse } = await got(currentUrl); diff --git a/lib/routes/jinse/timeline.ts b/lib/routes/jinse/timeline.ts index c3f3ee5b2318..4cc80e0b7426 100644 --- a/lib/routes/jinse/timeline.ts +++ b/lib/routes/jinse/timeline.ts @@ -75,7 +75,7 @@ async function handler(ctx) { // Reason: API returns mixed domains (jinse.com, m.jinse.com.cn, jinse.com.cn), // normalize all to www.jinse.com.cn since old domains are dead - const link = item.jump_url.replace(/\/\/(www\.|m\.)?jinse\.com(?!\.cn)/, '//www.jinse.com.cn').replace('//m.jinse.com.cn', '//www.jinse.com.cn'); + const link = item.jump_url.replace(/\/\/(www\.|m\.)?jinse\.com(\.cn)?/, '//www.jinse.com.cn'); return { title: item.title, diff --git a/lib/routes/jisilu/util.ts b/lib/routes/jisilu/util.ts index f0cb443897a1..48a69e6d2693 100644 --- a/lib/routes/jisilu/util.ts +++ b/lib/routes/jisilu/util.ts @@ -68,7 +68,7 @@ const processItems: ($: CheerioAPI, targetEl: Cheerio<Element>, limit: number) = const isAnswer: boolean = item.link ? /answer_id/.test(item.link) : false; - const description: string = (isAnswer ? $$('div.markitup-box').last() : $$('div.markitup-box').first()).html() ?? ''; + const description = (isAnswer ? $$('div.markitup-box').last() : $$('div.markitup-box').first()).html(); const metaStr: string = $$(isAnswer ? 'div.aw-dynamic-topic-meta' : 'div.aw-question-detail-meta') .find('span.aw-text-color-999') @@ -97,7 +97,7 @@ const processItems: ($: CheerioAPI, targetEl: Cheerio<Element>, limit: number) = author, content: { html: description, - text: $$('div.aw-question-detail-txt').first().text(), + text: $$('div.aw-question-detail-txt').text(), }, updated: updatedStr ? timezone(parseDate(updatedStr), 8) : item.updated, }; diff --git a/lib/routes/jlu/ccst/xwzx/index.ts b/lib/routes/jlu/ccst/xwzx/index.ts index 0105f006a8c9..07e15800797b 100644 --- a/lib/routes/jlu/ccst/xwzx/index.ts +++ b/lib/routes/jlu/ccst/xwzx/index.ts @@ -47,7 +47,7 @@ async function handler(ctx: any) { const linkEl = el.find('a'); const dateEl = el.find('.date'); - const dateStr = dateEl.text().trim(); + const dateStr = dateEl.text(); const title = linkEl.text().trim(); const rawLink = linkEl.attr('href')!.replaceAll('..', ''); // Replace all occurrences of '..' const link = `${baseUrl}${encodeURI(rawLink)}`; // Encode the URL properly diff --git a/lib/routes/jlu/jwc.ts b/lib/routes/jlu/jwc.ts index 885aad094c4e..0cf45796314e 100644 --- a/lib/routes/jlu/jwc.ts +++ b/lib/routes/jlu/jwc.ts @@ -35,12 +35,12 @@ async function handler() { const linkEl = el.find('a'); const YMDiv = el.find('.tm p'); - const YMStr = YMDiv.text().trim(); + const YMStr = YMDiv.text(); const DDiv = el.find('.tm span'); - const DStr = DDiv.text().trim(); + const DStr = DDiv.text(); const titleDiv = el.find('.s3-info p'); - const title = titleDiv.text().trim(); + const title = titleDiv.text(); const link = `${baseUrl}/${linkEl.attr('href')}`; diff --git a/lib/routes/joneslanglasalle/index.ts b/lib/routes/joneslanglasalle/index.ts index cc071d69bdbf..f455e65abb0b 100644 --- a/lib/routes/joneslanglasalle/index.ts +++ b/lib/routes/joneslanglasalle/index.ts @@ -71,11 +71,11 @@ export const handler = async (ctx: Context): Promise<Data> => { }, }); - const items: DataItem[] = (response.hits?.hits || []).map((hit) => { + const items: DataItem[] = response.hits.hits.map((hit) => { const source = hit._source; return { title: source.title, - description: source.description || source.subTitle || '', + description: source.description || source.subTitle, link: source.pageUrl, pubDate: source.datePublished ? parseDate(source.datePublished) : undefined, category: [...(source.topics || []), ...(source.industries || [])], diff --git a/lib/routes/jou/utils/index.ts b/lib/routes/jou/utils/index.ts index edfd2a7fbcbf..10bba260b7be 100644 --- a/lib/routes/jou/utils/index.ts +++ b/lib/routes/jou/utils/index.ts @@ -59,21 +59,6 @@ async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promi const $content = $(selectors.content); - $content.find('a').each((_, el) => { - const $a = $(el); - const href = $a.attr('href'); - if (href) { - $a.attr('href', new URL(href, item.link).href); - } - }); - $content.find('img').each((_, el) => { - const $img = $(el); - const src = $img.attr('src'); - if (src) { - $img.attr('src', new URL(src, item.link).href); - } - }); - const title = $(selectors.title).text().trim(); const pubDate = selectors.date ? parsePubDate($(selectors.date).text()) : undefined; @@ -81,7 +66,7 @@ async function fetchArticle(item: NoticeItem, selectors: DetailSelectors): Promi ...item, title: title || item.title, pubDate: pubDate ?? item.pubDate, - description: $content.html() ?? item.description, + description: $content.html(), }; } diff --git a/lib/routes/jpmorganchase/research.ts b/lib/routes/jpmorganchase/research.ts index f5397c29d66b..8fd3d9bfff61 100644 --- a/lib/routes/jpmorganchase/research.ts +++ b/lib/routes/jpmorganchase/research.ts @@ -82,7 +82,7 @@ function fetchDataItem(entry: IndexEntry): Promise<DataItem> { .toArray() .map((el) => $(el).text().trim()); articleDate = $('.date').text().trim() || entry.date; - description = $('.root').children('div').children('div:eq(1)').html() || ''; + description = $('.root').children('div').children('div:eq(1)').html(); } return { diff --git a/lib/routes/jumeili/home.ts b/lib/routes/jumeili/home.ts index 65f63df18490..45d3b100549e 100644 --- a/lib/routes/jumeili/home.ts +++ b/lib/routes/jumeili/home.ts @@ -49,7 +49,6 @@ async function handler(ctx) { const cookie = config.jumeili.cookie; const response = await ofetch(link, { headers: { - referer: baseUrl, 'user-agent': config.trueUA, accept: 'application/json, text/javascript, */*; q=0.01', cookie, @@ -77,7 +76,6 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { const article = await ofetch(item.link, { headers: { - referer: baseUrl, 'user-agent': config.trueUA, accept: 'application/json, text/javascript, */*; q=0.01', cookie, diff --git a/lib/routes/kanxue/topic.ts b/lib/routes/kanxue/topic.ts index edefb572d77d..e8a9f6358e43 100644 --- a/lib/routes/kanxue/topic.ts +++ b/lib/routes/kanxue/topic.ts @@ -86,9 +86,6 @@ async function handler(ctx) { const response = await got({ method: 'get', url: baseUrl + path, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data); diff --git a/lib/routes/kcna/news.tsx b/lib/routes/kcna/news.tsx index 7bafd17a84e7..f4146a8a6810 100644 --- a/lib/routes/kcna/news.tsx +++ b/lib/routes/kcna/news.tsx @@ -1,16 +1,16 @@ import { load } from 'cheerio'; +import type { Context } from 'hono'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; import pMap from 'p-map'; -import sanitizeHtml from 'sanitize-html'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -import { fetchPhoto, fetchVideo, fixDesc } from './utils'; +import { fetchPhoto, fixDesc } from './utils'; export const route: Route = { path: '/:lang/:category?', @@ -27,9 +27,13 @@ export const route: Route = { }, radar: [ { - source: ['www.kcna.kp/:lang', 'www.kcna.kp/:lang/category/articles/q/1ee9bdb7186944f765208f34ecfb5407.kcmsf', 'www.kcna.kp/:lang/category/articles.kcmsf'], + source: ['www.kcna.kp/:lang'], target: '/:lang', }, + { + source: ['www.kcna.kp/:lang/article/list/:category'], + target: '/:lang/:category', + }, ], name: 'News', maintainers: ['Rongronggg9'], @@ -40,40 +44,43 @@ export const route: Route = { | Category | \`:category\` | | ---------------------------------------------------------------- | ---------------------------------- | -| WPK General Secretary **Kim Jong Un**'s Revolutionary Activities | \`54c0ca4ca013a92cc9cf95bd4004c61a\` | -| Latest News (default) | \`1ee9bdb7186944f765208f34ecfb5407\` | -| Top News | \`5394b80bdae203fadef02522cfb578c0\` | -| Home News | \`b2b3bcc1b0a4406ab0c36e45d5db58db\` | -| Documents | \`a8754921399857ebdbb97a98a1e741f5\` | -| World | \`593143484cf15d48ce85c26139582395\` | -| Society-Life | \`93102e5a735d03979bc58a3a7aefb75a\` | -| External | \`0f98b4623a3ef82aeea78df45c423fd0\` | -| News Commentary | \`12c03a49f7dbe829bceea8ac77088c21\` |`, +| WPK General Secretary **Kim Jong Un**'s Revolutionary Activities | \`b0721b9f23054ddc7fe56c2811a12715\` | +| Latest News (default) | \`a666dda1282180e0ee1b4427b0574ae7\` | +| Top News | \`6a47505ba5268fd7749c0fe11e4b24b4\` | +| Home News | \`2f7d854121ccbbfbe6feae9fdcc3556e\` | +| Documents | \`1afa96195f9b303902490a126ab7285f\` | +| World | \`ecc14533d88be93068af4178946b1b05\` | +| Social Life | \`680e40b40899891bbe75a7072e3285e7\` | +| External | \`e2f336db98b5e69c75e0da264e037e8d\` | +| Revolutionary Anecdote | \`503e9b606704f9b1c625fa5755928cd3\` | +| Always in Memory of People | \`7bc083f00425be6aadfb828fba1cb5a7\` |`, }; -async function handler(ctx) { - const { lang, category = '1ee9bdb7186944f765208f34ecfb5407' } = ctx.req.param(); +async function handler(ctx: Context) { + const { lang, category = 'a666dda1282180e0ee1b4427b0574ae7' } = ctx.req.param(); const rootUrl = 'http://www.kcna.kp'; - const pageUrl = `${rootUrl}/${lang}/category/articles/q/${category}.kcmsf`; + const pageUrl = `${rootUrl}/${lang}/article/list/${category}`; - const response = await got(pageUrl); - const $ = load(response.data); + const response = await ofetch(pageUrl); + const $ = load(response); - // fix <nobr><span class="fSpecCs">???</span></nobr> - const title = sanitizeHtml($('head > title').text(), { allowedTags: [], allowedAttributes: {} }); + const title = $('head > title').text(); - const list = $('.article-link li a') + const list = $('.article h5') .toArray() .map((item) => { - item = $(item); - const dateElem = item.find('.publish-time'); - const dateString = dateElem.text().match(/\d+\.\d+\.\d+/); - dateElem.remove(); + const $item = $(item); + const a = $item.find('a'); + const dateString = $item + .find('span') + .text() + .match(/\d+\.\d+\.\d+/)![0]; + return { - title: item.text(), - link: rootUrl + item.attr('href'), - pubDate: timezone(parseDate(dateString[0]), 9), + title: a.text().trim(), + link: new URL(a.attr('href')!, rootUrl).href, + pubDate: timezone(parseDate(dateString, 'YYYY.M.D'), 9), }; }); @@ -84,31 +91,17 @@ async function handler(ctx) { list, (item) => cache.tryGet(item.link, async () => { - const response = await got(item.link); - const $ = load(response.data); - item.title = $('article-main-title').text() || item.title; - - const dateElem = $('.publish-time'); - const dateString = dateElem.text().match(/\d+\.\d+\.\d+/); - dateElem.remove(); - item.pubDate = dateString ? timezone(parseDate(dateString[0]), 9) : item.pubDate; - - const description = fixDesc($, $('.article-content-body .content-wrapper')); - - // add picture and video - const media = $('.media-icon a') - .toArray() - .map((elem) => rootUrl + elem.attribs.href); - let photo, video; - await Promise.all( - media.map(async (medium) => { - if (medium.includes('/photo/')) { - photo = await fetchPhoto(ctx, medium); - } else if (medium.includes('/video/')) { - video = await fetchVideo(ctx, medium); - } - }) - ); + const response = await ofetch(item.link); + const $ = load(response); + + const container = $('article .container'); + const gallery = container.find('a.gallery_button').attr('href'); + container.find('h1, a.right_button').remove(); + + const description = fixDesc($, container); + + // add picture + const photo = gallery ? await fetchPhoto(new URL(gallery, rootUrl).href) : ''; item.description = renderToString( <> @@ -119,12 +112,6 @@ async function handler(ctx) { {raw(photo)} </> ) : null} - {video ? ( - <> - <br /> - {raw(video)} - </> - ) : null} </> ); diff --git a/lib/routes/kcna/utils.ts b/lib/routes/kcna/utils.ts index bf9329b62d39..f95249d0d114 100644 --- a/lib/routes/kcna/utils.ts +++ b/lib/routes/kcna/utils.ts @@ -1,61 +1,25 @@ import { load } from 'cheerio'; import cache from '@/utils/cache'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; +import ofetch from '@/utils/ofetch'; -const rootUrl = 'http://www.kcna.kp'; - -const parseJucheDate = (dateString) => { - if (!dateString) { - return null; - } - - // https://en.wikipedia.org/wiki/Juche_calendar - const dateMatch = dateString.match(/(\d+)\D(\d+)\D(\d+)/); - const [jucheYear, month, day] = dateMatch ? dateMatch.slice(1) : [null, null, null]; - if (jucheYear && month && day) { - const year = Number(jucheYear) + 1911; - return parseDate(`${year}-${month}-${day}`, 'YYYY-M-D'); - } - return null; -}; - -const fixDesc = ($, elem) => { - // <nobr><span className='fSpecCs'>???</span></nobr> => <b>???</b> +export const fixDesc = ($, elem) => { + // <august_name>???</august_name> => <b>???</b> const $elem = $(elem); - $elem.find('.fSpecCs').each((_, item) => { - if (item.parent.name === 'nobr') { - $(item).unwrap(); - } + $elem.find('august_name').each((_, item) => { item.name = 'b'; - item.attribs = {}; }); - return elem.html(); + return $elem.html(); }; -const fetchPhoto = (ctx, url) => +export const fetchPhoto = (url) => cache.tryGet(url, async () => { - const res = await got(url); - const $ = load(res.data); - let html = ''; - $('.content img').each((_, item) => { - const src = item.attribs.src; - if (src) { - html += html ? `<br><img src="${src}">` : `<img src="${src}">`; - } - }); - return html; + const response = await ofetch(url); + const $ = load(response); + + return $('.gallery img') + .removeAttr('class') + .toArray() + .map((item) => $.html(item)) + .join('<br>'); }); - -const fetchVideo = (ctx, url) => - cache.tryGet(url, async () => { - const res = await got(url); - const $ = load(res.data); - const js = $('script[type="text/javascript"]:not([src])').html(); - let sources = js.match(/<[^>]*source[^>]+src[^>]+>/g); - sources &&= sources.map((item) => item.replaceAll("'", '"').replaceAll(/src="([^"]+)"/g, (_match, p1) => `src="${rootUrl}${p1}"`)); - return `<video controls preload="metadata">${sources.join('\n')}</video>`; - }); - -export { fetchPhoto, fetchVideo, fixDesc, parseJucheDate }; diff --git a/lib/routes/keylol/index.ts b/lib/routes/keylol/index.ts index ba45d69cff61..c3e74a6b513f 100644 --- a/lib/routes/keylol/index.ts +++ b/lib/routes/keylol/index.ts @@ -147,7 +147,7 @@ async function handler(ctx) { .toArray() .map((c) => content(c).text()); - const pubDateEm = content('img.authicn').first().next(); + const pubDateEm = content('img.authicn').next(); const pubDateText = pubDateEm.find('span').prop('title') ?? pubDateEm.text(); const pubDateMatches = pubDateText.match(/(\d{4}(?:-\d{1,2}){2} (?:\d{2}:){2}\d{2})/) ?? undefined; if (pubDateMatches) { diff --git a/lib/routes/kiro/blog.ts b/lib/routes/kiro/blog.ts index 762b43df5575..85f0eb5892e4 100644 --- a/lib/routes/kiro/blog.ts +++ b/lib/routes/kiro/blog.ts @@ -52,7 +52,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('header h1').text(); - const description: string | undefined = $$('div.prose').html() ?? undefined; + const description = $$('div.prose').html(); const pubDateStr: string | undefined = $$('time').text(); const authorEl: Cheerio<Element> = $$('img.aspect-square').parent().parent(); const authors: DataItem['author'] = [ diff --git a/lib/routes/kiro/changelog.ts b/lib/routes/kiro/changelog.ts index cc63e9111595..2e07badb4299 100644 --- a/lib/routes/kiro/changelog.ts +++ b/lib/routes/kiro/changelog.ts @@ -26,7 +26,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $el: Cheerio<Element> = $(el); const title = `${$el.parent().find('span').text()} ${$el.find('h3').text()}`; - const description: string | undefined = $el.parent().parent().find('div.prose').html() ?? undefined; + const description = $el.parent().parent().find('div.prose').html(); const pubDateStr: string | undefined = $el.parent().parent().parent().find('time').text(); const linkUrl: string | undefined = $el.attr('href'); const upDatedStr: string | undefined = pubDateStr; @@ -58,7 +58,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title = `${$$('article span').first().text()} ${$$('article h3').text()}`; - const description: string | undefined = $$('div.prose').html() ?? undefined; + const description = $$('div.prose').html(); const pubDateStr: string | undefined = $$('time').text(); const image: string | undefined = $$('meta[property="og:image"]').attr('content'); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/kleinanzeigen/utils/parse-listing-page.ts b/lib/routes/kleinanzeigen/utils/parse-listing-page.ts index d9587033de45..b8cfc3c13d2f 100644 --- a/lib/routes/kleinanzeigen/utils/parse-listing-page.ts +++ b/lib/routes/kleinanzeigen/utils/parse-listing-page.ts @@ -16,7 +16,7 @@ export const parseListingPage = ($: CheerioAPI): Promise<DataItem[]> => .toArray() .map((item) => { const $item = $(item); - const article = $item.find('article').first(); + const article = $item.find('article'); return getProductPage(`https://www.kleinanzeigen.de${article.attr('data-href')}`); }) ); diff --git a/lib/routes/kovidgoyal/kitty/changelog.ts b/lib/routes/kovidgoyal/kitty/changelog.ts index 0b6febdeca0e..76cc2a37c55a 100644 --- a/lib/routes/kovidgoyal/kitty/changelog.ts +++ b/lib/routes/kovidgoyal/kitty/changelog.ts @@ -44,7 +44,7 @@ async function handler() { const $section = $(section); // Extract version and date from h3 title - const titleText = $section.find('h3').first().text().trim(); + const titleText = $section.find('h3').text(); const versionMatch = titleText.match(/^([\d.]+)\s*\[([^\]]+)\]/); if (!versionMatch) { diff --git a/lib/routes/last-origin/news.ts b/lib/routes/last-origin/news.ts index 13b23752280f..8312f970d6da 100644 --- a/lib/routes/last-origin/news.ts +++ b/lib/routes/last-origin/news.ts @@ -35,9 +35,9 @@ async function handler() { const list = $('.contents .news_wrap') .toArray() .map((item) => { - const title = $(item).find('.news_title').text().trim(); + const title = $(item).find('.news_title').text(); const link = new URL($(item).find('a').attr('href')!, baseUrl).href; - const date = $(item).find('time').text().trim(); + const date = $(item).find('time').text(); const pubDate = timezone(parseDate(date), 9); return { title, @@ -52,7 +52,7 @@ async function handler() { cache.tryGet(item.link, async () => { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.news_contents_editor').html() ?? ''; + item.description = $('.news_contents_editor').html(); return item; }) ) diff --git a/lib/routes/logrocket/index.ts b/lib/routes/logrocket/index.ts index 1bfe4aa59589..f8de8e5aadfb 100644 --- a/lib/routes/logrocket/index.ts +++ b/lib/routes/logrocket/index.ts @@ -36,7 +36,7 @@ async function handler(ctx) { .map((item) => { item = $(item); const a = item.find('a').first(); - const title = item.find('.post-card-title').first(); + const title = item.find('.post-card-title'); return { title: title.text(), link: a.attr('href'), diff --git a/lib/routes/malaysiakini/index.ts b/lib/routes/malaysiakini/index.ts index 4ee1d8badaa6..008a3b32c517 100644 --- a/lib/routes/malaysiakini/index.ts +++ b/lib/routes/malaysiakini/index.ts @@ -161,9 +161,7 @@ async function handler(ctx) { if (response.data.stories.author) { item.author = response.data.stories.author; } - if (response.data.stories.tags) { - item.category = response.data.stories.tags; - } + item.category = response.data.stories.tags; return item; }) ) diff --git a/lib/routes/mashiro/index.ts b/lib/routes/mashiro/index.ts index d11534a300c3..ff1c9ddaae1d 100644 --- a/lib/routes/mashiro/index.ts +++ b/lib/routes/mashiro/index.ts @@ -49,7 +49,7 @@ export const route: Route = { cache.tryGet(item.link, async () => { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.article-content').first().html(); + item.description = $('.article-content').html(); return item; }) ) diff --git a/lib/routes/medium/parse-article.ts b/lib/routes/medium/parse-article.ts index cf9236060752..b09da035dfff 100644 --- a/lib/routes/medium/parse-article.ts +++ b/lib/routes/medium/parse-article.ts @@ -21,7 +21,7 @@ async function parse(url, cookie = '') { article.find('header').remove(); // get and remove title - const title = article.find('h1').first(); + const title = article.find('h1'); const titleText = title.text(); title.remove(); // remove title from html diff --git a/lib/routes/meritalk/articles.ts b/lib/routes/meritalk/articles.ts index a602e2b50563..11382d0c9168 100644 --- a/lib/routes/meritalk/articles.ts +++ b/lib/routes/meritalk/articles.ts @@ -43,7 +43,7 @@ async function handler() { const a = $item.find('.news-block-title a'); const link = a.attr('href'); return { - title: a.text().trim(), + title: a.text(), link: link as string, pubDate: parseDate($item.find('time[datetime]').attr('datetime') as string), category: $item @@ -60,8 +60,8 @@ async function handler() { const { data: response } = await got(item.link); const $ = load(response); - const featuredImage = $('.single-featured-image').first().html() || ''; - const fullContent = $('.single-body').first().html() || ''; + const featuredImage = $('.single-featured-image').html() || ''; + const fullContent = $('.single-body').html() || ''; item!.description = renderDescription({ featuredImage, fullContent, diff --git a/lib/routes/meteoblue/weathernews.ts b/lib/routes/meteoblue/weathernews.ts index e960b645276c..7402078f86ee 100644 --- a/lib/routes/meteoblue/weathernews.ts +++ b/lib/routes/meteoblue/weathernews.ts @@ -37,7 +37,7 @@ async function handler() { // Get title and link from h3 > a const $link = $article.find('h3[itemprop="headline"] a[itemprop="mainEntityOfPage"]'); - const title = $link.text().trim(); + const title = $link.text(); const link = $link.attr('href'); if (!title || !link) { @@ -50,7 +50,7 @@ async function handler() { // Extract author from the time element text const $authorMeta = $article.find('meta[itemprop="author"]'); - const author = $authorMeta.attr('content')?.trim() || 'meteoblue'; + const author = $authorMeta.attr('content') || 'meteoblue'; // Get description from itemprop="description" const $description = $article.find('div[itemprop="description"]'); diff --git a/lib/routes/mhlw/monthly-labour-survey.ts b/lib/routes/mhlw/monthly-labour-survey.ts index a39aecd1da36..b994e062e8fa 100644 --- a/lib/routes/mhlw/monthly-labour-survey.ts +++ b/lib/routes/mhlw/monthly-labour-survey.ts @@ -40,14 +40,14 @@ async function handler(ctx: Context) { .toArray() .flatMap((row) => { const $row = $(row); - const year = $row.find('h3').text().trim(); + const year = $row.find('h3').text(); return $row .find('ul.ico-link li a') .toArray() .map((a) => { const $a = $(a); return { - title: `${year}${$a.text().trim()}`, + title: `${year}${$a.text()}`, link: new URL($a.attr('href')!, baseUrl).href, }; }) @@ -61,12 +61,12 @@ async function handler(ctx: Context) { const response = await fetchPage(item.link!); const $ = load(response); - const dateText = $('.prt-topContents .al-right').text().trim(); + const dateText = $('.prt-topContents .al-right').text(); const cleanedDate = dateText.replaceAll(/([^)]+)/g, ''); const content = $('#contentsInner'); content.find('.prt-topContents, .prt-linkNavi, .prt-plugin').remove(); - item.title = $('h1#pageTitle').text().trim() || item.title; + item.title = $('h1#pageTitle').text() || item.title; item.pubDate = timezone(parseDate(cleanedDate, 'YYYY年M月D日'), 9); item.description = content.html()?.trim(); @@ -76,8 +76,8 @@ async function handler(ctx: Context) { ); return { - title: $('head title').text().trim(), - description: $('meta[name="description"]').attr('content')?.trim(), + title: $('head title').text(), + description: $('meta[name="description"]').attr('content'), link, image: `${baseUrl}/favicon.ico`, language: $('html').attr('lang'), diff --git a/lib/routes/mi/utils.tsx b/lib/routes/mi/utils.tsx index 464c60aa6744..d7206d121365 100644 --- a/lib/routes/mi/utils.tsx +++ b/lib/routes/mi/utils.tsx @@ -22,9 +22,6 @@ dayjs.extend(utc); */ export const getCrowdfundingList = async (): Promise<CrowdfundingList[]> => { const response = await ofetch<DataResponse<CrowdfundingData>>('https://m.mi.com/v1/crowd/crowd_home', { - headers: { - referer: 'https://m.mi.com/', - }, method: 'POST', }); return response.data.list; diff --git a/lib/routes/misskon/utils.ts b/lib/routes/misskon/utils.ts index c45554a41599..7e2ada14a8cd 100644 --- a/lib/routes/misskon/utils.ts +++ b/lib/routes/misskon/utils.ts @@ -14,7 +14,6 @@ const getPosts = async (searchParams) => { $('input').each((_, el) => { $(el).replaceWith($(el).attr('value') || ''); }); - $('script').remove(); return { title: item.title.rendered, link: item.link, diff --git a/lib/routes/mit/hanlab.ts b/lib/routes/mit/hanlab.ts index 41472da306a1..66fc862f8ccc 100644 --- a/lib/routes/mit/hanlab.ts +++ b/lib/routes/mit/hanlab.ts @@ -42,8 +42,8 @@ async function handler() { const titleEl = el.find('h3.text-title'); const title = titleEl.text().trim(); const link = new URL(el.attr('href') ?? '', rootUrl).href; - const description = el.find('p.text-tldr').html() ?? undefined; - const dateText = el.find('div.text-date').text().trim(); + const description = el.find('p.text-tldr').html(); + const dateText = el.find('div.text-date').text(); const pubDate = parseDate(dateText); return { diff --git a/lib/routes/mit/scratch/user-comments.ts b/lib/routes/mit/scratch/user-comments.ts index e6602916edf9..31ea27bd872b 100644 --- a/lib/routes/mit/scratch/user-comments.ts +++ b/lib/routes/mit/scratch/user-comments.ts @@ -42,7 +42,7 @@ export const route: Route = { .toArray() .map((el) => { const comment = $(el); - const author = comment.find('.name a').first().text().trim(); + const author = comment.find('.name a').text(); const contentHtml = comment.find('.content').html()?.trim() || ''; const textContent = comment.find('.content').text().trim(); const commentId = comment.attr('data-comment-id'); diff --git a/lib/routes/miyuki/news.ts b/lib/routes/miyuki/news.ts index 1b1bcd2e079e..71d97c990132 100644 --- a/lib/routes/miyuki/news.ts +++ b/lib/routes/miyuki/news.ts @@ -90,7 +90,7 @@ function normalizePhotoLists($, content) { const items = list .children('li') .toArray() - .flatMap((item) => { + .map((item) => { const photoItem = $(item); photoItem.find('img.for_sp').remove(); photoItem.find('img').each((__, image) => { @@ -101,13 +101,13 @@ function normalizePhotoLists($, content) { return; } - img.attr('src', new URL(src, ORIGIN).href); img.removeAttr('class'); }); const html = photoItem.html(); - return hasMeaningfulHtml(html) ? [`<div>${html!.trim()}</div>`] : []; - }); + return hasMeaningfulHtml(html) ? `<div>${html!.trim()}</div>` : undefined; + }) + .filter(Boolean); list.replaceWith(items.join('<br /><br />')); }); diff --git a/lib/routes/mrinalxdev/blog.ts b/lib/routes/mrinalxdev/blog.ts index e9485d133041..57d346c77135 100644 --- a/lib/routes/mrinalxdev/blog.ts +++ b/lib/routes/mrinalxdev/blog.ts @@ -83,7 +83,7 @@ async function handler() { $('a[href*="buymeacoffee"]').parent().remove(); // Extract main content - typically in body after nav - const content = $('body').html() || ''; + const content = $('body').html(); const dataItem: DataItem = { title: item.title, diff --git a/lib/routes/my-formosa/index.ts b/lib/routes/my-formosa/index.ts index a027e322e967..339a73e115c5 100644 --- a/lib/routes/my-formosa/index.ts +++ b/lib/routes/my-formosa/index.ts @@ -21,50 +21,47 @@ export const route: Route = { }, radar: [ { - source: ['my-formosa.com/'], + source: ['m.my-formosa.com.tw/'], }, ], - name: '首页', + name: '首頁', maintainers: ['dzx-dzx'], handler, - url: 'my-formosa.com', + url: 'm.my-formosa.com.tw', }; -async function fetch(url) { - const raw = await ofetch(url, { responseType: 'arrayBuffer' }); - const decoder = new TextDecoder('big5'); - return decoder.decode(raw); -} - async function handler() { - const rootUrl = 'http://www.my-formosa.com/'; - - const res = await fetch(rootUrl); + const rootUrl = 'https://m.my-formosa.com.tw'; + const res = await ofetch(rootUrl); const $ = load(res); const items = await Promise.all( - $('#featured-news h3 a') + $('ul.local-list li .cont h1 a') .toArray() .map((item) => { - item = $(item); + const $item = $(item); - const title = item.text(); - const link = new URL(item.attr('href'), rootUrl).href; + const title = $item.text(); + const link = new URL($item.attr('href')!, rootUrl).href; return cache.tryGet(link, async () => { - const res = await fetch(link); + const res = await ofetch(link); const $ = load(res); - const isTV = new URL(link).pathname.startsWith('/TV'); + const pubDate = $('.news_header .info') + .text() + .match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)?.[0]; + const media = $.html($('.news_header > img, .news_header > .media')); + const summary = $.html($('.product > h2')); return { title, link, - author: $('.page-header~#featured-news h4').text(), - category: $("meta[name='keywords']").attr('content').split(',').filter(Boolean), - pubDate: timezone(parseDate((isTV ? $('.icon-calendar')[0].next.data : $('.date').text()).trim()), 8), - description: (isTV ? $('.post-item').html() : $('.body').html()).replaceAll(/\/News.*?\.jpg/g, (match) => `http://my-formosa.com${match}`), + author: $('.cont h1 a').text(), + category: [$('.story_header button').text()], + pubDate: pubDate ? timezone(parseDate(pubDate), 8) : undefined, + description: media + summary + ($('.body').html() ?? ''), }; }); }) diff --git a/lib/routes/my-formosa/namespace.ts b/lib/routes/my-formosa/namespace.ts index 8cd9c24c6c35..f56c9293113b 100644 --- a/lib/routes/my-formosa/namespace.ts +++ b/lib/routes/my-formosa/namespace.ts @@ -2,6 +2,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '美麗島電子報', - url: 'my-formosa.com', + url: 'my-formosa.com.tw', lang: 'zh-TW', }; diff --git a/lib/routes/mycard520/news.ts b/lib/routes/mycard520/news.ts index d9ecd3e3a490..090713585404 100644 --- a/lib/routes/mycard520/news.ts +++ b/lib/routes/mycard520/news.ts @@ -30,7 +30,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $aEl: Cheerio<Element> = $el.find('a'); const title: string = $el.find('div.text_box p').text(); - const description: string | undefined = $aEl.html() ?? undefined; + const description = $aEl.html(); const pubDateStr: string | undefined = $el.find('div.date').text().trim(); const linkUrl: string | undefined = $aEl.attr('href'); const image: string | undefined = $el.find('div.img_box img').attr('src'); @@ -42,8 +42,8 @@ export const handler = async (ctx: Context): Promise<Data> => { pubDate: pubDateStr ? parseDate(pubDateStr) : undefined, link: linkUrl, content: { - html: description ?? '', - text: description ?? '', + html: description, + text: description, }, image, banner: image, @@ -67,12 +67,12 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$pageBox: Cheerio<Element> = $$('div.page_box'); const title: string = $$pageBox.find('h2').text(); - const pubDateStr: string | undefined = $$('div.date').first().text(); + const pubDateStr: string | undefined = $$('div.date').text(); const upDatedStr: string | undefined = pubDateStr; $$pageBox.find('h2, div.date, .the_champ_sharing_container').remove(); - const description: string | undefined = $$pageBox.html() ?? item.description; + const description: string | null | undefined = $$pageBox.html() ?? item.description; const processedItem: DataItem = { title, diff --git a/lib/routes/nankai/ai-notice.ts b/lib/routes/nankai/ai-notice.ts index cecd989b4389..0c5d70b22dba 100644 --- a/lib/routes/nankai/ai-notice.ts +++ b/lib/routes/nankai/ai-notice.ts @@ -72,18 +72,18 @@ export const route: Route = { // 提取标题和链接 const $titleLink = titleCell.find('a'); - const title = $titleLink.text().trim(); + const title = $titleLink.text(); let link = $titleLink.attr('href') || ''; // 处理相对链接 link = link && !link.startsWith('http') ? `${baseUrl}/${link}` : link; // 提取日期 - const dateStr = dateCell.text().trim(); + const dateStr = dateCell.text(); const pubDate = dateStr.includes('/') ? timezone(parseDate(dateStr, 'YYYY/MM/DD'), 8) : timezone(parseDate(dateStr), 8); // 提取来源 - const source = sourceCell.text().trim(); + const source = sourceCell.text(); return { title, @@ -100,31 +100,12 @@ export const route: Route = { list.map((item) => item ? cache.tryGet(item.link, async () => { - try { - const { data: response } = await got(item.link); - const $ = load(response); - - const $description = $('.v_news_content'); - - // 处理相对链接,转换为绝对链接 - if ($description.length > 0) { - // 处理图片 - $description.find('img').each((i, el) => { - const $el = $(el); - let src = $el.attr('src'); - - if (src && !src.startsWith('http')) { - src = `${baseUrl}${src}`; - $el.attr('src', src); - } - }); - } - - item.description = $description.html() || item.title; - } catch { - // 如果获取详细内容失败,返回基本信息 - item.description = item.title + ' (获取详细内容失败)'; - } + const { data: response } = await got(item.link); + const $ = load(response); + + const $description = $('.v_news_content'); + + item.description = $description.html() || item.title; return item; }) : null diff --git a/lib/routes/nankai/graduate-notice.ts b/lib/routes/nankai/graduate-notice.ts index b8e668edaeb5..4eb7d7bd9857 100644 --- a/lib/routes/nankai/graduate-notice.ts +++ b/lib/routes/nankai/graduate-notice.ts @@ -29,7 +29,7 @@ export const route: Route = { maintainers: ['ladeng07'], description: `| 最新动态 | 综合信息 | 招生工作 | 培养管理 | 国际交流 | 学科建设 | 学位管理 | | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -| zxdt | 82 | 83 | 84 | 85 | 86 | 87 |`, +| zxdt | 82 | 83 | 72 | 73 | xkjs | xwgl |`, url: 'graduate.nankai.edu.cn', handler: async (ctx) => { // 从 URL 参数中获取通知分类 @@ -43,10 +43,10 @@ export const route: Route = { zxdt: '最新动态', '82': '综合信息', '83': '招生工作', - '84': '培养管理', - '85': '国际交流', - '86': '学科建设', - '87': '学位管理', + '72': '培养管理', + '73': '国际交流', + xkjs: '学科建设', + xwgl: '学位管理', }; const categoryName = categoryMap[type] || '最新动态'; @@ -68,7 +68,7 @@ export const route: Route = { link = link && !link.startsWith('http') ? `${baseUrl}${link}` : link; // 提取日期 - const dateStr = $timeDiv.text().trim(); + const dateStr = $timeDiv.text(); const pubDate = timezone(parseDate(dateStr, 'YYYY-MM-DD'), 8); return { @@ -85,67 +85,32 @@ export const route: Route = { const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - try { - const { data: response } = await got(item.link); - const $ = load(response); - - // 尝试多种内容选择器 - const $description = $('.wp_articlecontent'); - - // 处理相对链接,转换为绝对链接 - if ($description.length > 0) { - // 处理链接 - $description.find('a').each((i, el) => { - const $el = $(el); - const href = $el.attr('href'); - if (href && !href.startsWith('http')) { - if (href.startsWith('/')) { - $el.attr('href', `${baseUrl}${href}`); - } else { - $el.attr('href', `${baseUrl}/${href}`); - } - } - }); - - // 处理图片 - $description.find('img').each((i, el) => { - const $el = $(el); - let src = $el.attr('src'); - - if (src && !src.startsWith('http')) { - src = src.startsWith('/') ? `${baseUrl}${src}` : `${baseUrl}/${src}`; - $el.attr('src', src); + const { data: response } = await got(item.link); + const $ = load(response); + + // 尝试多种内容选择器 + const $description = $('.wp_articlecontent'); + + if ($description.length > 0) { + // 处理PDF播放器div,提取PDF链接 + $description.find('.wp_pdf_player').each((i, el) => { + const $el = $(el); + const pdfSrc = $el.attr('pdfsrc'); + const sudyfileAttr = ($el.attr('sudyfile-attr') || '{}').replaceAll("'", '"'); + const sudyfileAttrJson = JSON.parse(sudyfileAttr); + const fileName = sudyfileAttrJson.title || '未命名文件.pdf'; + if (pdfSrc) { + let pdfUrl = pdfSrc; + if (!pdfUrl.startsWith('http')) { + pdfUrl = `${baseUrl}${pdfUrl}`; } - }); - - // 处理PDF播放器div,提取PDF链接 - $description.find('.wp_pdf_player').each((i, el) => { - const $el = $(el); - const pdfSrc = $el.attr('pdfsrc'); - const sudyfileAttr = ($el.attr('sudyfile-attr') || '{}').replaceAll("'", '"'); - - try { - const sudyfileAttrJson = JSON.parse(sudyfileAttr); - const fileName = sudyfileAttrJson.title || '未命名文件.pdf'; - if (pdfSrc) { - let pdfUrl = pdfSrc; - if (!pdfUrl.startsWith('http')) { - pdfUrl = `${baseUrl}${pdfUrl}`; - } - // 替换PDF播放器为下载链接 - $el.replaceWith(`<p><a href="${pdfUrl}" target="_blank">📄 ${fileName}</a></p>`); - } - } catch { - // 如果解析失败,保留原始内容 - } - }); - } - - item.description = $description.html() || item.title; - } catch { - // 如果获取详细内容失败,返回基本信息 - item.description = item.title + ' (获取详细内容失败)'; + // 替换PDF播放器为下载链接 + $el.replaceWith(`<p><a href="${pdfUrl}" target="_blank">📄 ${fileName}</a></p>`); + } + }); } + + item.description = $description.html() || item.title; return item; }) ) diff --git a/lib/routes/nankai/jwc.ts b/lib/routes/nankai/jwc.ts index cfaebe42050f..0467f0f12f42 100644 --- a/lib/routes/nankai/jwc.ts +++ b/lib/routes/nankai/jwc.ts @@ -44,8 +44,8 @@ export const route: Route = { const $dateMonth = $item.find('.d .d-m'); // 构建完整的日期 - const day = $dateDay.text().trim(); // 格式:04 - const monthYear = $dateMonth.text().trim(); // 格式:2025/06 + const day = $dateDay.text(); // 格式:04 + const monthYear = $dateMonth.text(); // 格式:2025/06 const fullDate = `${monthYear}/${day}`; // 2025/06/04 let linkStr = $link.attr('href'); @@ -55,7 +55,7 @@ export const route: Route = { } return { - title: $link.text().trim(), + title: $link.text(), link: linkStr, pubDate: timezone(parseDate(fullDate, 'YYYY/MM/DD'), 8), }; @@ -80,19 +80,6 @@ export const route: Route = { // 获取文章内容 const content = $('.page-news-con .wp_articlecontent'); if (content.length > 0) { - // 处理PDF链接,转换为绝对链接 - content.find('a').each((i, el) => { - const $el = $(el); - const href = $el.attr('href'); - if (href && !href.startsWith('http')) { - if (href.startsWith('/')) { - $el.attr('href', `${baseUrl}${href}`); - } else { - $el.attr('href', `${baseUrl}/${href}`); - } - } - }); - // 处理PDF播放器div,提取PDF链接 content.find('.wp_pdf_player').each((i, el) => { const $el = $(el); diff --git a/lib/routes/nankai/notice.ts b/lib/routes/nankai/notice.ts index 60aa99154c09..ee1ea2cc270b 100644 --- a/lib/routes/nankai/notice.ts +++ b/lib/routes/nankai/notice.ts @@ -37,7 +37,7 @@ export const route: Route = { .map((item) => { const $item = $(item); const $time = $item.find('.time'); - const day = $time.find('.time-d').text().trim(); + const day = $time.find('.time-d').text(); const monthYear = $time.contents().last().text().trim(); const pubDate = timezone(parseDate(`${monthYear}-${day}`, 'YYYY-MM-DD'), 8); @@ -46,7 +46,7 @@ export const route: Route = { href = href.startsWith('http') ? href : new URL(href, baseUrl).href; return { - title: $link.text().trim(), + title: $link.text(), link: href, pubDate, }; @@ -55,21 +55,16 @@ export const route: Route = { const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - try { - // 判断link如果是https://xb.nankai.edu.cn/的则为校内访问的 - if (item.link.includes('xb.nankai.edu.cn')) { - item.description = '该通知可能需要校内访问权限'; - } else { - const { data: detailResponse } = await got(item.link); - const $detail = load(detailResponse); + // 判断link如果是https://xb.nankai.edu.cn/的则为校内访问的 + if (item.link.includes('xb.nankai.edu.cn')) { + item.description = '该通知可能需要校内访问权限'; + } else { + const { data: detailResponse } = await got(item.link); + const $detail = load(detailResponse); - // 提取正文内容 - const content = $detail('.wp_articlecontent').html() || ''; - item.description = content; - } - } catch { - // 如果提取正文内容失败,则返回默认内容 - item.description = '正文内容获取失败'; + // 提取正文内容 + const content = $detail('.wp_articlecontent').html(); + item.description = content; } return item; }) diff --git a/lib/routes/nankai/yzb.ts b/lib/routes/nankai/yzb.ts index 4507d99cdb77..5b5125bcea73 100644 --- a/lib/routes/nankai/yzb.ts +++ b/lib/routes/nankai/yzb.ts @@ -66,7 +66,7 @@ export const route: Route = { cache.tryGet(item.link.toString(), async () => { const { data: response } = await got(item.link); const $ = load(response); - item.description = $('.read').first().html(); + item.description = $('.read').html(); // 提取PDF链接,先转换为数组再使用map const pdfLinks = $('div[pdfsrc$=".pdf"]') diff --git a/lib/routes/natgeo/natgeo.ts b/lib/routes/natgeo/natgeo.ts index 5cd0bcc3fba4..984ec2c962ea 100644 --- a/lib/routes/natgeo/natgeo.ts +++ b/lib/routes/natgeo/natgeo.ts @@ -10,7 +10,7 @@ import { parseDate } from '@/utils/parse-date'; async function loadContent(link) { const data = await ofetch(link); const $ = load(data); - const dtStr = $('.content-title-area').find('h6').first().text().replaceAll(' ', ' ').trim(); + const dtStr = $('.content-title-area').find('h6').first().text().replaceAll(' ', ' '); $('.splide__arrows, .slide-control, [class^="ad-"], style').remove(); diff --git a/lib/routes/ncu/jwc.ts b/lib/routes/ncu/jwc.ts index 6f9c36ea2dff..45e477f195ee 100644 --- a/lib/routes/ncu/jwc.ts +++ b/lib/routes/ncu/jwc.ts @@ -48,10 +48,7 @@ async function handler() { const rawLink = linkEl.attr('href'); const link = rawLink ? new URL(rawLink, baseUrl).href : ''; - const dateText = el - .find(String.raw`.font-mono span.md\:inline`) - .text() - .trim(); + const dateText = el.find(String.raw`.font-mono span.md\:inline`).text(); return { title, @@ -69,19 +66,6 @@ async function handler() { const contentEl = $detail('.v_news_content'); - contentEl.find('a').each((_, el) => { - const href = $detail(el).attr('href'); - if (href && !href.startsWith('http')) { - $detail(el).attr('href', new URL(href, baseUrl).href); - } - }); - contentEl.find('img').each((_, el) => { - const src = $detail(el).attr('src'); - if (src && !src.startsWith('http')) { - $detail(el).attr('src', new URL(src, baseUrl).href); - } - }); - let description = contentEl.html() || ''; const attachments = $detail('a[href*="download.jsp"]'); diff --git a/lib/routes/neea/jlpt.ts b/lib/routes/neea/jlpt.ts index 5463710a1095..e676394d0a36 100644 --- a/lib/routes/neea/jlpt.ts +++ b/lib/routes/neea/jlpt.ts @@ -53,7 +53,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.dvTitle').text(); - const description: string = $$('div.dvContent').html() ?? ''; + const description = $$('div.dvContent').html(); const processedItem: DataItem = { title, diff --git a/lib/routes/netflix/research.ts b/lib/routes/netflix/research.ts index a398254178bc..721de82c474f 100644 --- a/lib/routes/netflix/research.ts +++ b/lib/routes/netflix/research.ts @@ -38,7 +38,7 @@ const resolveArticle = (data, store) => { return target === undefined ? null : resolveArticle(target, store); } - const out = Array.isArray(data) ? [] : {}; + const out = {}; for (const [k, v] of Object.entries(data)) { out[k] = resolveArticle(v, store); } diff --git a/lib/routes/nextjs/blog.ts b/lib/routes/nextjs/blog.ts index 2bf401cb30ad..8fb4281d5987 100644 --- a/lib/routes/nextjs/blog.ts +++ b/lib/routes/nextjs/blog.ts @@ -24,9 +24,9 @@ const handler: Route['handler'] = async () => { const $ = load(data); return { - title: $('h1').first().text().trim(), + title: $('h1').text(), link, - description: $('div.prose').html() ?? '', + description: $('div.prose').html(), pubDate: parseDate( $('p[data-version="v1"]') .first() diff --git a/lib/routes/nga/post.ts b/lib/routes/nga/post.ts index c80bd75a3e9b..b097c57dc4ab 100644 --- a/lib/routes/nga/post.ts +++ b/lib/routes/nga/post.ts @@ -99,7 +99,7 @@ async function handler(ctx) { const pageId = await getLastPageId(tid, authorId); const $ = await getPage(tid, authorId, pageId); - const title = $('title').text() || ''; + const title = $('title').text(); const posterMap = JSON.parse( $('script') .text() diff --git a/lib/routes/niaogebiji/cat.ts b/lib/routes/niaogebiji/cat.ts index a6e1e6e6940a..2577c9297798 100644 --- a/lib/routes/niaogebiji/cat.ts +++ b/lib/routes/niaogebiji/cat.ts @@ -43,9 +43,9 @@ async function handler(ctx) { .map((item) => { item = $(item); return { - title: item.find('.articleTitle').text().trim(), - description: item.find('.articleContentInner').text().trim(), - author: item.find('.author').text().trim(), + title: item.find('.articleTitle').text(), + description: item.find('.articleContentInner').text(), + author: item.find('.author').text(), link: new URL(item.find('a').first().attr('href'), link).href, category: [ ...item @@ -63,7 +63,7 @@ async function handler(ctx) { const response = await got(element.link); const $ = load(response.data); - element.pubDate = timezone(parseDate($('.writeTime3').text().trim()), 8); + element.pubDate = timezone(parseDate($('.writeTime3').text()), 8); element.description = $('.pc_content').html(); return element; diff --git a/lib/routes/nielsberglund/index.ts b/lib/routes/nielsberglund/index.ts index 0ffab274e51e..3e992251071c 100644 --- a/lib/routes/nielsberglund/index.ts +++ b/lib/routes/nielsberglund/index.ts @@ -34,7 +34,7 @@ async function handler() { const $item = $(item); const $link = $item.find('a').first(); const href = $link.attr('href'); - const title = $item.find('.post-title').first().text().trim(); + const title = $item.find('.post-title').text().trim(); const dateStr = $item.find('.post-meta').text().trim(); if (!href || !title) { @@ -60,7 +60,7 @@ async function handler() { const detailResponse = await got(item.link); const $detail = load(detailResponse.data); - item.description = $detail('.post-container').html() || ''; + item.description = $detail('.post-container').html(); return item; } catch { diff --git a/lib/routes/nikkei/asia/index.ts b/lib/routes/nikkei/asia/index.ts index 90209de9cec7..63e2fd043311 100644 --- a/lib/routes/nikkei/asia/index.ts +++ b/lib/routes/nikkei/asia/index.ts @@ -47,9 +47,9 @@ async function handler() { const response = await got(item.link); const $ = load(response.data); - const description = $('div[class^="NewsArticle_newsArticleContentContainerWrapper"]').html() || ''; + const description = $('div[class^="NewsArticle_newsArticleContentContainerWrapper"]').html(); - const author = $('div[class^="NewsArticleDetails_newsArticleDetailsByline"]').text() || ''; + const author = $('div[class^="NewsArticleDetails_newsArticleDetailsByline"]').text(); return { title, pubDate, diff --git a/lib/routes/njit/jwc.ts b/lib/routes/njit/jwc.ts index edb757279bbc..7d0bb9e5e107 100644 --- a/lib/routes/njit/jwc.ts +++ b/lib/routes/njit/jwc.ts @@ -20,7 +20,7 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, - name: '南京工程学院教务处', + name: '教务处', maintainers: ['zefengdaguo'], handler, description: `| 教学 | 考试 | 信息 | 实践 | diff --git a/lib/routes/njit/tzgg.ts b/lib/routes/njit/tzgg.ts index 81f3cf0cc3a4..fac095e33e18 100644 --- a/lib/routes/njit/tzgg.ts +++ b/lib/routes/njit/tzgg.ts @@ -26,7 +26,7 @@ export const route: Route = { source: ['www.njit.edu.cn/'], }, ], - name: '南京工程学院通知公告', + name: '通知公告', maintainers: ['zefengdaguo'], handler, url: 'www.njit.edu.cn/', diff --git a/lib/routes/nju/scit.ts b/lib/routes/nju/scit.ts index 6b5b8ab82608..d621db107384 100644 --- a/lib/routes/nju/scit.ts +++ b/lib/routes/nju/scit.ts @@ -50,7 +50,7 @@ async function handler(ctx) { return { title: item.find('a').attr('title'), link: 'https://scit.nju.edu.cn' + item.find('a').attr('href'), - pubDate: timezone(parseDate(item.find('.Article_PublishDate').first().text(), 'YYYY-MM-DD'), 8), + pubDate: timezone(parseDate(item.find('.Article_PublishDate').text(), 'YYYY-MM-DD'), 8), }; }), }; diff --git a/lib/routes/nju/zbb.ts b/lib/routes/nju/zbb.ts index 7cd9a779a5db..6b066578cf72 100644 --- a/lib/routes/nju/zbb.ts +++ b/lib/routes/nju/zbb.ts @@ -48,9 +48,9 @@ async function handler(ctx) { item = $(item); return { title: item.find('a').attr('title'), - description: item.find('a').first().text(), + description: item.find('a').text(), link: 'https://zbb.nju.edu.cn' + item.find('a').attr('href'), - pubDate: timezone(parseDate(item.find('span').first().text(), 'YYYY-MM-DD'), 8), + pubDate: timezone(parseDate(item.find('span').text(), 'YYYY-MM-DD'), 8), }; }), }; @@ -80,9 +80,9 @@ async function handler(ctx) { item = $(item); return { title: item.find('a').attr('title'), - description: item.find('a').first().text(), + description: item.find('a').text(), link: 'https://zbb.nju.edu.cn' + item.find('a').attr('href'), - pubDate: timezone(parseDate(item.find('span').first().text(), 'YYYY-MM-DD'), 8), + pubDate: timezone(parseDate(item.find('span').text(), 'YYYY-MM-DD'), 8), category: category_dict[c], }; }); diff --git a/lib/routes/njucm/utils/index.ts b/lib/routes/njucm/utils/index.ts index 1fff50cbd7a2..f91d670d1ca1 100644 --- a/lib/routes/njucm/utils/index.ts +++ b/lib/routes/njucm/utils/index.ts @@ -29,11 +29,7 @@ async function getNoticeList(ctx, url, host, listSelector, titleSelector, conten } else { const $ = load(response.data); item.title = $(contentSelector.title).text(); - item.description = $(contentSelector.content) - .html() - .replaceAll('src="/', () => `src="${new URL('.', host).href}`) - .replaceAll('href="/', () => `href="${new URL('.', host).href}`) - .trim(); + item.description = $(contentSelector.content).html().trim(); item.pubDate = timezone(parseDate($(contentSelector.date).text()), 8); } return item; diff --git a/lib/routes/njupt/jwc.ts b/lib/routes/njupt/jwc.ts index 50c269b911b5..d5db700e46c7 100644 --- a/lib/routes/njupt/jwc.ts +++ b/lib/routes/njupt/jwc.ts @@ -4,6 +4,8 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; +import { finishArticleItem } from '@/utils/wechat-mp'; const host = 'https://jwc.njupt.edu.cn'; @@ -39,75 +41,39 @@ async function handler(ctx) { const response = await got({ method: 'get', url: link, - headers: { - Referer: host, - }, }); const $ = load(response.data); - const urlList = $('.content') - .find('a') - .slice(0, 10) + const list = $('.news_list li') .toArray() - .map((e) => $(e).attr('href')); + .map((item) => { + const $item = $(item); - const titleList = $('.content') - .find('a') - .slice(0, 10) - .toArray() - .map((e) => $(e).attr('title')); - - const dateList = $('.content tr') - .find('div') - .slice(0, 10) - .toArray() - .map((e) => $(e).text().replace('发布时间:', '')); + return { + title: $item.find('.news_title').text(), + link: new URL($item.find('a').attr('href')!, host).href, + pubDate: timezone(parseDate($item.find('.news_meta').text()), 8), + }; + }); - const out = await Promise.all( - urlList.map((itemUrl, index) => { - itemUrl = new URL(itemUrl, host).href; - if (itemUrl.includes('.htm')) { - return cache.tryGet(itemUrl, async () => { - const response = await got.get(itemUrl); - if (response.redirectUrls.length !== 0) { - const single = { - title: titleList[index], - link: itemUrl, - description: '该通知无法直接预览, 请点击原文链接↑查看', - pubDate: parseDate(dateList[index]), - }; - return single; - } - const $ = load(response.data); - const single = { - title: $('.Article_Title').text(), - link: itemUrl, - description: $('.wp_articlecontent') - .html() - .replaceAll('src="/', () => `src="${new URL('.', host).href}`) - .replaceAll('href="/', () => `href="${new URL('.', host).href}`) - .trim(), - pubDate: parseDate($('.Article_PublishDate').text().replace('发布时间:', '')), - }; - return single; - }); + const items = await Promise.all( + list.map((item) => { + if (new URL(item.link).host === 'mp.weixin.qq.com') { + return finishArticleItem({ ...item, guid: item.link }); } - const single = { - title: titleList[index], - link: itemUrl, - description: '该通知为文件,请点击原文链接↑下载', - pubDate: parseDate(dateList[index]), - }; - return single; + + return cache.tryGet(item.link, async () => { + const detailResponse = await got(item.link); + const $ = load(detailResponse.data); + item.description = $('.wp_articlecontent').html() ?? '该通知无法直接预览,请点击原文链接查看'; + return item; + }); }) ); - let info = '通知公告'; - if (type === 'news') { - info = '教务快讯'; - } + return { - title: '南京邮电大学 -- ' + info, + title: `南京邮电大学 -- ${type === 'news' ? '教务快讯' : '通知公告'}`, link, - item: out, + item: items, }; } diff --git a/lib/routes/njxzc/home.ts b/lib/routes/njxzc/home.ts index ec1f0ffd3bda..0559db25bb78 100644 --- a/lib/routes/njxzc/home.ts +++ b/lib/routes/njxzc/home.ts @@ -45,7 +45,7 @@ async function handler() { return null; } return { - title: $link.attr('title') || $link.text().trim(), + title: $link.attr('title') || $link.text(), link: new URL(href, pageUrl).href, pubDate: parsePubDate($item.find('.news_meta').text()), }; diff --git a/lib/routes/njxzc/lib.ts b/lib/routes/njxzc/lib.ts index 04c2b45e79f1..42c439efcef2 100644 --- a/lib/routes/njxzc/lib.ts +++ b/lib/routes/njxzc/lib.ts @@ -43,8 +43,8 @@ async function handler() { if (!href) { return null; } - const day = $link.find('.tm-1').text().trim(); - const yearMonth = $link.find('.tm-2').text().trim(); + const day = $link.find('.tm-1').text(); + const yearMonth = $link.find('.tm-2').text(); return { title: $link.find('.btt-4').text().trim(), link: new URL(href, pageUrl).href, diff --git a/lib/routes/njxzc/utils/index.ts b/lib/routes/njxzc/utils/index.ts index da92a4f7a008..e958d11a879d 100644 --- a/lib/routes/njxzc/utils/index.ts +++ b/lib/routes/njxzc/utils/index.ts @@ -31,27 +31,12 @@ async function fetchArticle(item: NoticeItem): Promise<DataItem> { const $player = $(el); const pdfSrc = $player.attr('pdfsrc'); if (pdfSrc) { - $player.replaceWith(`<p><a href="${new URL(pdfSrc, item.link).href}">附件下载</a></p>`); + $player.replaceWith(`<p><a href="${pdfSrc}">附件下载</a></p>`); } else { $player.remove(); } }); - $content.find('a').each((_, el) => { - const $a = $(el); - const href = $a.attr('href'); - if (href) { - $a.attr('href', new URL(href, item.link).href); - } - }); - $content.find('img').each((_, el) => { - const $img = $(el); - const src = $img.attr('src'); - if (src) { - $img.attr('src', new URL(src, item.link).href); - } - }); - - const title = $('.arti_title').text().trim(); + const title = $('.arti_title').text(); const pubDate = parsePubDate($('.arti_update').text()); return { diff --git a/lib/routes/nmc/publish.ts b/lib/routes/nmc/publish.ts index 86ffff178416..e5f088d19515 100644 --- a/lib/routes/nmc/publish.ts +++ b/lib/routes/nmc/publish.ts @@ -34,7 +34,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const timeStrArray = $el .find('div.author b') .toArray() - .map((el) => $(el).text().trim()); + .map((el) => $(el).text()); const pubDateStr: string | undefined = `${timeStrArray.pop()}:00 ${timeStrArray.join('/')}`; const title = `${pubDateStr} - ${$el.find('div.title').text().replaceAll(/\s/g, '')}`; @@ -86,7 +86,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const title = `${$el.find('div').text().trim()} - ${$('div.nav1 a.actived, div#menuNavBar button.dropdown-toggle') .toArray() - .map((el) => $(el).text().trim()) + .map((el) => $(el).text()) .join(' - ')}`; const description: string | undefined = renderDescription({ images: image diff --git a/lib/routes/notion/release.ts b/lib/routes/notion/release.ts index 06324cb84eae..e74e6a950649 100644 --- a/lib/routes/notion/release.ts +++ b/lib/routes/notion/release.ts @@ -16,9 +16,9 @@ const handler: Route['handler'] = async () => { const $ = load(data); // the first post, do not cache - const title = $('h2').first().text() ?? ''; + const title = $('h2').first().text(); const pubDate = parseDate($('time').first().text()); - const description = $('article.release article').first().html() ?? ''; + const description = $('article.release article').first().html(); const link = `https://notion.so/releases/${day(pubDate).format('YYYY-MM-DD')}`; // archive @@ -39,9 +39,9 @@ const handler: Route['handler'] = async () => { const $ = load(data); return { - title: $('h2').first().text() ?? '', + title: $('h2').first().text(), pubDate: parseDate($('time').first().text()), - description: $('article.release article').first().html() ?? '', + description: $('article.release article').first().html(), link, }; }); diff --git a/lib/routes/nowcoder/schedule.ts b/lib/routes/nowcoder/schedule.ts index fa67bc84e814..24a5f694a9c6 100644 --- a/lib/routes/nowcoder/schedule.ts +++ b/lib/routes/nowcoder/schedule.ts @@ -43,7 +43,7 @@ async function handler(ctx) { link: 'https://www.nowcoder.com/school/schedule', description: '名企校招日程', item: data.map((item) => { - let desc = `<tr><td><img src="${item.logo}" referrerpolicy="no-referrer""></td></tr>`; + let desc = `<tr><td><img src="${item.logo}"></td></tr>`; for (const each of item.schedules) { desc += `<tr><td>${each.content}</td><td>${each.time}</td></tr>`; } diff --git a/lib/routes/npr/full.ts b/lib/routes/npr/full.ts index b56809541cdf..9ecdb7d78c48 100644 --- a/lib/routes/npr/full.ts +++ b/lib/routes/npr/full.ts @@ -13,10 +13,10 @@ const getArticleDetail = (link) => // Prefer tags to slug let categories = $('.tag') .toArray() - .map((el) => $(el).text().trim()); + .map((el) => $(el).text()); if (categories.length < 1) { - const slug = $('.slug a').contents().first().text().trim(); + const slug = $('.slug a').contents().first().text(); if (slug) { categories = [slug]; diff --git a/lib/routes/nuist/bulletin.ts b/lib/routes/nuist/bulletin.ts index 7dd30ef5e56d..392d172cb909 100644 --- a/lib/routes/nuist/bulletin.ts +++ b/lib/routes/nuist/bulletin.ts @@ -80,7 +80,7 @@ async function handler(ctx) { const item = $(element); // 从内部找 a 标签 - const a = item.find('.btt a').first(); + const a = item.find('.btt a'); const href = a.attr('href'); if (!href) { diff --git a/lib/routes/nuist/cas.ts b/lib/routes/nuist/cas.ts index 98629346ab8e..691cfff91ab6 100644 --- a/lib/routes/nuist/cas.ts +++ b/lib/routes/nuist/cas.ts @@ -42,7 +42,7 @@ async function handler(ctx) { .map((item) => { item = $(item); return { - title: item.find('.Title').text().trim(), + title: item.find('.Title').text(), link: new URL(item.find('.Title').attr('href'), baseUrl).href, pubDate: parseDate( item diff --git a/lib/routes/nyc/mayors-office-news.ts b/lib/routes/nyc/mayors-office-news.ts index d7168a2eac8a..bf817a5a8294 100644 --- a/lib/routes/nyc/mayors-office-news.ts +++ b/lib/routes/nyc/mayors-office-news.ts @@ -91,7 +91,7 @@ Categories // Remove `iframe` (e.g. YouTube embeds) $('iframe').remove(); - item.description = $('#body-text-section').first().html(); + item.description = $('#body-text-section').html(); return item; }) diff --git a/lib/routes/nycu/aa.ts b/lib/routes/nycu/aa.ts index 1c588541f7a0..442b6abf6127 100644 --- a/lib/routes/nycu/aa.ts +++ b/lib/routes/nycu/aa.ts @@ -36,7 +36,7 @@ async function handler(ctx: Context): Promise<Data> { const item = $('.newslist li') .toArray() .map((e) => ({ - title: $('a', e).attr('title')?.trim() || '', + title: $('a', e).attr('title') || '', link: $('a', e).attr('href') || '', pubDate: ROCDate($('div p:nth-child(1)', e).text().replace('更新日期:', '').trim()), category: [$('div p:nth-child(2)', e).text().replace('分類:', '')], diff --git a/lib/routes/nycu/announcement.ts b/lib/routes/nycu/announcement.ts index 9429172c4be9..3d674599ebf5 100644 --- a/lib/routes/nycu/announcement.ts +++ b/lib/routes/nycu/announcement.ts @@ -25,7 +25,7 @@ async function handler(ctx: Context): Promise<Data> { const item = $('.category-style tr .style2') .toArray() .map((titleEle) => { - const date = $(titleEle).parent().next().find('td').text().split('-', 1)[0]?.trim(); + const date = $(titleEle).parent().next().find('td').text().split('-', 1)[0]; return { title: $(titleEle).attr('title')?.trim() || '', diff --git a/lib/routes/nycu/osa.ts b/lib/routes/nycu/osa.ts index 73665c78c918..9e1e5c95ef32 100644 --- a/lib/routes/nycu/osa.ts +++ b/lib/routes/nycu/osa.ts @@ -70,9 +70,9 @@ async function handler(ctx: Context): Promise<Data> { const item = $('.newslist li') .toArray() .map((e) => ({ - title: $('a', e).attr('title')?.trim() || '', + title: $('a', e).attr('title') || '', link: $('a', e).attr('href') || '', - pubDate: ROCDate($('div p:nth-child(1)', e).text().replace('更新日期:', '').trim() || ''), + pubDate: ROCDate($('div p:nth-child(1)', e).text().replace('更新日期:', '').trim()), category: [$('div p:nth-child(2)', e).text().replace('分類:', '')], author: $('div p:nth-child(3)', e).text().replace('發布單位:', ''), })); diff --git a/lib/routes/nymity/censorbib.ts b/lib/routes/nymity/censorbib.ts index b48d4743fedf..f8eb0614702e 100644 --- a/lib/routes/nymity/censorbib.ts +++ b/lib/routes/nymity/censorbib.ts @@ -29,9 +29,9 @@ async function handler() { .map((item): DataItem => { const c = $(item); const id = c.attr('id')!; - const title = c.find('span.paper').text().trim(); - const author = c.find('span.author').text().trim(); - const other = c.find('span.other').text().trim(); + const title = c.find('span.paper').text(); + const author = c.find('span.author').text(); + const other = c.find('span.other').text(); const download = c.find("img.icon[title='Download paper']").parent().attr('href'); const downloadBibTex = c.find("img.icon[title='Download BibTeX']").parent().attr('href'); const linkToPaper = c.find("img.icon[title='Link to paper']").parent().attr('href'); diff --git a/lib/routes/ollama/blog.ts b/lib/routes/ollama/blog.ts index 7f393e1e00e8..2deed1a25488 100644 --- a/lib/routes/ollama/blog.ts +++ b/lib/routes/ollama/blog.ts @@ -27,10 +27,10 @@ async function handler() { const items = $('a.group.border-b.py-10') .toArray() .map((item) => ({ - title: $(item).children('h2').first().text(), + title: $(item).children('h2').text(), link: baseUrl + $(item).attr('href'), - pubDate: parseDate($(item).children('h3').first().text()), - description: $(item).children('p').first().text(), + pubDate: parseDate($(item).children('h3').text()), + description: $(item).children('p').text(), })); return { title: 'ollama blog', diff --git a/lib/routes/ollama/models.ts b/lib/routes/ollama/models.ts index f1638b997821..2045f814bc41 100644 --- a/lib/routes/ollama/models.ts +++ b/lib/routes/ollama/models.ts @@ -23,9 +23,9 @@ async function handler() { const items = $('#repo > ul > li > a') .toArray() .map((item) => { - const name = $(item).find('h2 span').first(); + const name = $(item).find('h2 span'); const link = $(item).attr('href'); - const description = $(item).find('div p.break-words').first(); + const description = $(item).find('div p.break-words'); const pubDate = $(item).find('span:contains("Updated")').first(); return { diff --git a/lib/routes/openrice/promos.ts b/lib/routes/openrice/promos.ts index 009ad1be5adb..098162bd81cc 100644 --- a/lib/routes/openrice/promos.ts +++ b/lib/routes/openrice/promos.ts @@ -39,13 +39,13 @@ async function handler(ctx) { const response = await ofetch(baseUrl + urlPath, {}); const $ = load(response); - const title = $('title').text() ?? "Openrice - What's Hot"; + const title = $('title').text(); const description = $('meta[name="description"]').attr('content') ?? "What's Hot from Openrice"; const data = $('.article-listing-content-cell-wrapper'); const resultList = data.toArray().map((item) => { const $item = $(item); - const title = $item.find('.title-name').text() ?? ''; + const title = $item.find('.title-name').text(); const link = $item.find('a.sr1-listing-content-cell').attr('href') ?? ''; const coverImg = $item @@ -53,7 +53,7 @@ async function handler(ctx) { .attr('style') ?.match(/url\(['"]?(.*?)['"]?\)/)?.[1] ?? null; const description = renderDescription({ - description: $item.find('.article-details .desc').text() ?? '', + description: $item.find('.article-details .desc').text(), image: coverImg, }); return { diff --git a/lib/routes/oreno3d/get-sec-page-data.ts b/lib/routes/oreno3d/get-sec-page-data.ts index d8529f82d07b..8bf45dc73066 100644 --- a/lib/routes/oreno3d/get-sec-page-data.ts +++ b/lib/routes/oreno3d/get-sec-page-data.ts @@ -33,28 +33,24 @@ export const sync_detail = async (link) => { .each((i, el) => { authors[i] = $(el).text(); authors[i].replace(' ', ''); // 去空格 - authors[i].trim(); // 去首尾空格 }); $(sec_page_selector) .find(origins_selector) .each((i, el) => { origins[i] = $(el).text(); origins[i].replace(' ', ''); - origins[i].trim(); }); $(sec_page_selector) .find(characters_selector) .each((i, el) => { characters[i] = $(el).text(); characters[i].replace(' ', ''); - characters[i].trim(); }); $(sec_page_selector) .find(tags_selector) .each((i, el) => { tags[i] = $(el).text(); tags[i].replace(' ', ''); - tags[i].trim(); }); // 筛选 const desc = $(sec_page_selector).find(desc_selector).text(); diff --git a/lib/routes/osu/beatmaps/packs.ts b/lib/routes/osu/beatmaps/packs.ts index afef25ec410f..689e6941de96 100644 --- a/lib/routes/osu/beatmaps/packs.ts +++ b/lib/routes/osu/beatmaps/packs.ts @@ -36,7 +36,7 @@ async function handler(ctx) { link, item: itemList.toArray().map((element) => { const item = $(element); - const title = item.find('.beatmap-pack__name').text().trim(); + const title = item.find('.beatmap-pack__name').text(); const link = item.find('.beatmap-pack__header').attr('href'); // Trying to get the description will return 429 (Too Many Requests). const description = item.find('.beatmap-pack__body').html(); diff --git a/lib/routes/p-articles/contributors.ts b/lib/routes/p-articles/contributors.ts index 8880096626f4..28a1b2693420 100644 --- a/lib/routes/p-articles/contributors.ts +++ b/lib/routes/p-articles/contributors.ts @@ -31,7 +31,7 @@ async function handler(ctx) { .toArray() .map((element) => { const info = { - title: $(element).find('h3').text().trim(), + title: $(element).find('h3').text(), link: new URL($(element).attr('href'), rootUrl).href, }; return info; diff --git a/lib/routes/p-articles/utils.ts b/lib/routes/p-articles/utils.ts index 3592d9024e5a..d34f09dded8f 100644 --- a/lib/routes/p-articles/utils.ts +++ b/lib/routes/p-articles/utils.ts @@ -8,10 +8,10 @@ const rootUrl = 'https://p-articles.com'; const ProcessFeed = (info, data) => { // const $ = cheerio.load(data); const $ = load(data); - const author = $('div.detail_title_02 > h4 > a:nth-child(2)').text().trim(); + const author = $('div.detail_title_02 > h4 > a:nth-child(2)').text(); info.author = author; - const dateValue = $('div.detail_title_02 > h4 ').text().trim(); + const dateValue = $('div.detail_title_02 > h4 ').text(); info.pubDate = timezone(parseDate(dateValue), 8); const description = $('div.detail_contect_01').html(); diff --git a/lib/routes/papers/category.ts b/lib/routes/papers/category.ts index 784791408f73..5f08828aa22c 100644 --- a/lib/routes/papers/category.ts +++ b/lib/routes/papers/category.ts @@ -34,7 +34,6 @@ export const handler = async (ctx: Context): Promise<Data> => { .contents() .last() .text() - ?.trim() ?.match(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})/)?.[1]; const linkUrl: string | undefined = $el.find('a.title-link').attr('href'); const categoryEls: Element[] = $el.find('p.subjects a').toArray(); @@ -66,7 +65,7 @@ export const handler = async (ctx: Context): Promise<Data> => { language, }; - const $enclosureEl: Cheerio<Element> = $el.find('a.title-pdf').first(); + const $enclosureEl: Cheerio<Element> = $el.find('a.title-pdf'); const enclosureUrl: string | undefined = $enclosureEl.attr('onclick')?.match(/togglePdf\('.*?',\s'(.*?)',\sthis\)/)?.[1]; if (enclosureUrl) { diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts index b03aead8d841..7ffb9cf213e3 100644 --- a/lib/routes/people/index.ts +++ b/lib/routes/people/index.ts @@ -55,7 +55,7 @@ async function handler(ctx) { .map((item) => { item = $(item); - const link = item.attr('href').trim(); + const link = item.attr('href'); return { title: item.text(), diff --git a/lib/routes/perplexity/blog.ts b/lib/routes/perplexity/blog.ts index edacc7dde33d..a014fcaf8aae 100644 --- a/lib/routes/perplexity/blog.ts +++ b/lib/routes/perplexity/blog.ts @@ -23,7 +23,7 @@ export const route: Route = { }, radar: [ { - source: ['www.perplexity.ai/hub'], + source: ['www.perplexity.ai/hub/blog'], target: '/blog', }, ], @@ -36,7 +36,7 @@ export const route: Route = { async function handler(ctx: Context) { const limit = Number(ctx.req.query('limit') ?? '20'); - const rootUrl = 'https://www.perplexity.ai/hub'; + const rootUrl = 'https://www.perplexity.ai/hub/blog'; const { page, destroy, context } = await getPlaywrightPage(rootUrl, { onBeforeLoad: async (page) => { @@ -56,7 +56,7 @@ async function handler(ctx: Context) { // Step 1: Extract featured article using data-framer-name attribute const featuredCard = $('[data-framer-name="Featured Card"]').first(); - const featuredHref = featuredCard.find('a[href^="./hub/blog/"]').first().attr('href'); + const featuredHref = featuredCard.find('a[href^="./blog/"]').attr('href'); const featuredTitle = featuredCard.find('h4').first().text().trim(); if (featuredHref && featuredTitle) { @@ -136,10 +136,10 @@ async function handler(ctx: Context) { } } - $content('script, style, noscript').remove(); + $content('style, noscript').remove(); const contentArea = $content('[data-framer-name="Content"]').first(); - const description = contentArea.length ? (contentArea.html() ?? undefined) : undefined; + const description = contentArea.length ? contentArea.html() : undefined; return { ...item, diff --git a/lib/routes/peterwunder/achievements.ts b/lib/routes/peterwunder/achievements.ts index 959c7826a74a..2a84e6f3eaaf 100644 --- a/lib/routes/peterwunder/achievements.ts +++ b/lib/routes/peterwunder/achievements.ts @@ -18,29 +18,16 @@ type BadgeItem = DataItem & { title: string; }; -function absolutizeImageSource($: CheerioAPI, itemUrl: string) { - $('article') - .first() - .find('[src]') - .each((_, element) => { - const value = $(element).attr('src'); - - if (value) { - $(element).attr('src', new URL(value, itemUrl).href); - } - }); -} - function extractBadgeDescription($: CheerioAPI) { - const article = $('article').first(); + const article = $('article'); if (!article.length) { return; } - article.find('h1, script, style, noscript').remove(); + article.find('h1, style, noscript').remove(); - return article.html() ?? undefined; + return article.html(); } function extractListItems($: CheerioAPI, limit: number): BadgeItem[] { @@ -50,7 +37,7 @@ function extractListItems($: CheerioAPI, limit: number): BadgeItem[] { .map((element) => { const badge = $(element); const href = badge.attr('href'); - const title = badge.find('.title').text().trim(); + const title = badge.find('.title').text(); if (!href || !title) { return null; @@ -74,10 +61,9 @@ function fetchBadge(item: BadgeItem) { const { data: response } = await got(item.link); const $: CheerioAPI = load(response); - const title = $('article h1').first().text().trim(); + const title = $('article h1').text(); const visibleStart = $('ul.metadata li').first().find('time.date').first().attr('datetime'); const image = $('meta[property="og:image"]').attr('content'); - absolutizeImageSource($, item.link); return { ...item, diff --git a/lib/routes/pincong/hot.ts b/lib/routes/pincong/hot.ts index ad77f0ac8151..a22943d20463 100644 --- a/lib/routes/pincong/hot.ts +++ b/lib/routes/pincong/hot.ts @@ -39,7 +39,7 @@ async function handler(ctx) { title: '品葱 - 精选', link: `${baseUrl}/hot/${category === '0' ? '' : `category-${category}`}`, item: list.toArray().map((item) => ({ - title: $(item).find('h2 a').text().trim(), + title: $(item).find('h2 a').text(), description: $(item).find('div.markitup-box').html(), link: baseUrl + $(item).find('div.mod-head h2 a').attr('href'), pubDate: parseDate($(item).find('div.mod-footer .aw-small-text').text()), diff --git a/lib/routes/pingwest/status.ts b/lib/routes/pingwest/status.ts index f9bf584f9b59..b03a239744bf 100644 --- a/lib/routes/pingwest/status.ts +++ b/lib/routes/pingwest/status.ts @@ -35,9 +35,6 @@ async function handler() { searchParams: { page: 1, }, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data.data.list); const items = $('section.item') diff --git a/lib/routes/pingwest/tag.ts b/lib/routes/pingwest/tag.ts index e2c1f55c29fa..776acfae9055 100644 --- a/lib/routes/pingwest/tag.ts +++ b/lib/routes/pingwest/tag.ts @@ -44,11 +44,7 @@ async function handler(ctx) { const baseUrl = 'https://www.pingwest.com'; const tagUrl = `${baseUrl}/tag/${tag}`; const { tagId, tagName } = await cache.tryGet(`pingwest:tag:${tag}`, async () => { - const res = await got(tagUrl, { - headers: { - Referer: baseUrl, - }, - }); + const res = await got(tagUrl); const $ = load(res.data); const tagId = $('.tag-detail').attr('data-id'); const tagName = $('.tag-detail .info .title').text(); @@ -61,9 +57,6 @@ async function handler(ctx) { id: tagId, type: type - 1, }, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data.data.list); diff --git a/lib/routes/pingwest/user.ts b/lib/routes/pingwest/user.ts index 5580fe5ebddf..25c9680815e7 100644 --- a/lib/routes/pingwest/user.ts +++ b/lib/routes/pingwest/user.ts @@ -44,11 +44,7 @@ async function handler(ctx) { const baseUrl = 'https://www.pingwest.com'; const aimUrl = `${baseUrl}/user/${uid}/${type}`; const { userName, realUid, userSign, userAvatar } = await cache.tryGet(`pingwest:user:info:${uid}`, async () => { - const res = await got(aimUrl, { - headers: { - Referer: baseUrl, - }, - }); + const res = await got(aimUrl); const $ = load(res.data); const userInfoNode = $('#J_userId'); return { @@ -65,9 +61,6 @@ async function handler(ctx) { user_id: realUid, tab: type, }, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data.data.list); diff --git a/lib/routes/pku/eecs.ts b/lib/routes/pku/eecs.ts index 3f7de9c4377a..49bd43c74135 100644 --- a/lib/routes/pku/eecs.ts +++ b/lib/routes/pku/eecs.ts @@ -5,34 +5,44 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { eecsMap } from './utils'; +const eecsMap = new Map([ + [0, 'tzgg.htm'], + [1, 'tzgg/xytz.htm'], + [2, 'tzgg/rstz.htm'], + [6, 'tzgg/jwtz.htm'], + [8, 'tzgg/xgtz.htm'], + [3, 'tzgg/ghtz.htm'], + [4, 'tzgg/yytz.htm'], +]); export const route: Route = { path: '/eecs/:type?', - name: 'Unknown', + name: '信科公告通知', + example: '/pku/eecs/0', maintainers: ['Ir1d'], handler, + description: `| 全部 | 学院通知 | 人事通知 | 教务通知 | 学工通知 | 工会通知 | 院友通知 | +| ---- | -------- | -------- | -------- | -------- | -------- | -------- | +| 0 | 1 | 2 | 6 | 8 | 3 | 4 |`, }; async function handler(ctx) { const host = 'https://eecs.pku.edu.cn'; - let type = ctx.params && Number.parseInt(ctx.req.param('type')); - if (type === undefined) { - type = 0; - } + const type = Number.parseInt(ctx.req.param('type')) || 0; + const listUrl = host + '/' + (eecsMap.get(type) ?? eecsMap.get(0)); - const response = await got(host + '/xygk1/ggtz/' + eecsMap.get(type)); + const response = await got(listUrl); const $ = load(response.data); - let items = $('.hvr-shutter-out-vertical') + let items = $('ul.list-text > li > a') .toArray() .map((item) => { item = $(item); return { - title: item.attr('title'), - link: new URL(item.attr('href'), host).href, - pubDate: parseDate(item.find('em').text()), + title: item.find('.tit').text(), + link: new URL(item.attr('href'), listUrl).href, + pubDate: parseDate(item.find('.date .mon').text() + '-' + item.find('.date .day').text()), }; }); @@ -40,26 +50,12 @@ async function handler(ctx) { items.map((item) => cache.tryGet(item.link, async () => { const detail = await got(item.link); - const content = load(detail.data); + const $ = load(detail.data); - content('input').remove(); - content('h1').remove(); - content('.con_xq').remove(); + const content = $('.Section1'); + content.find('[style]').removeAttr('style'); - content('form[name=_newscontent_fromname] img').each((_, i) => { - i = $(i); - if (i.attr('src').startsWith('/')) { - i.attr('src', new URL(i.attr('src'), host).href); - } - }); - content('form[name=_newscontent_fromname] ul li a').each((_, a) => { - a = $(a); - if (a.attr('href').startsWith('/')) { - a.attr('href', new URL(a.attr('href'), host).href); - } - }); - - item.description = content('form[name=_newscontent_fromname]').html(); + item.description = content.html(); return item; }) ) @@ -67,7 +63,7 @@ async function handler(ctx) { return { title: $('title').text(), - link: host + '/xygk1/ggtz/' + eecsMap.get(type), + link: listUrl, description: '北大信科 公告通知', item: items, }; diff --git a/lib/routes/pku/utils.ts b/lib/routes/pku/utils.ts deleted file mode 100644 index 351692a8f4c8..000000000000 --- a/lib/routes/pku/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -const eecsMap = new Map([ - [0, 'qb.htm'], - [1, 'xytz.htm'], - [2, 'rstz.htm'], - [6, 'jwtz.htm'], - [8, 'xgtz.htm'], - [7, 'kytz.htm'], - [5, 'cwtz.htm'], - [3, 'ghtz.htm'], - [4, 'yytz.htm'], -]); - -export { eecsMap }; diff --git a/lib/routes/pornhub/category-url.ts b/lib/routes/pornhub/category-url.ts index 87812372a731..e47c81e2c7b5 100644 --- a/lib/routes/pornhub/category-url.ts +++ b/lib/routes/pornhub/category-url.ts @@ -48,7 +48,7 @@ async function handler(ctx) { .map((e) => parseItems($(e), showImages)); return { - title: $('title').first().text(), + title: $('title').text(), link, language: $('html').attr('lang') as any, item: items, diff --git a/lib/routes/priconne-redive/news.ts b/lib/routes/priconne-redive/news.ts index fc02c85092c7..cf29b3e27eef 100644 --- a/lib/routes/priconne-redive/news.ts +++ b/lib/routes/priconne-redive/news.ts @@ -51,7 +51,7 @@ async function handler(ctx) { const parseContent = (htmlString) => { const $ = load(htmlString); $('.contents-body h3').remove(); - const time = $('.meta-info .time').text().trim(); + const time = $('.meta-info .time').text(); $('.meta-info').remove(); const content = $('.contents-body'); @@ -72,7 +72,7 @@ async function handler(ctx) { const out = await Promise.all( list.map((index, item) => { item = $(item); - const link = item.find('a').first().attr('href'); + const link = item.find('a').attr('href'); return cache.tryGet(link, async () => { const rssitem = { title: item.find('h4').text(), diff --git a/lib/routes/projectjav/utils.ts b/lib/routes/projectjav/utils.ts index d863ebc7bebd..40c76fbd8e5c 100644 --- a/lib/routes/projectjav/utils.ts +++ b/lib/routes/projectjav/utils.ts @@ -20,7 +20,7 @@ const processItems = async (currentUrl: string) => { const item = $(element); const link = item.find('a').attr('href'); return { - title: item.find('div.name span').text() || '', + title: item.find('div.name span').text(), link: link?.startsWith('http') ? link : `${rootUrl}${link}`, }; }) @@ -80,7 +80,7 @@ const processItems = async (currentUrl: string) => { } // Get description - item.description = mainContent.html() || ''; + item.description = mainContent.html(); return item; }) diff --git a/lib/routes/qingting/podcast.ts b/lib/routes/qingting/podcast.ts index 98ad908b8388..e675652b436a 100644 --- a/lib/routes/qingting/podcast.ts +++ b/lib/routes/qingting/podcast.ts @@ -77,11 +77,7 @@ async function handler(ctx) { const data = (await cache.tryGet(`qingting:podcast:${channelId}:${item.id}`, async () => { const link = `https://www.qingting.fm/channels/${channelId}/programs/${item.id}/`; - const detailRes = await ofetch(link, { - headers: { - Referer: 'https://www.qingting.fm/', - }, - }); + const detailRes = await ofetch(link); const detail = JSON.parse(detailRes.match(/\},"program":(.*?),"plist":/)[1]); diff --git a/lib/routes/qlu/notice.ts b/lib/routes/qlu/notice.ts index cc2f6c8e5c60..ed9566144efa 100644 --- a/lib/routes/qlu/notice.ts +++ b/lib/routes/qlu/notice.ts @@ -55,7 +55,7 @@ async function handler() { } else { const result = await got(itemUrl); const $ = load(result.data); - description = $('.read').html().trim(); + description = $('.read').html(); } return { title: itemTitle, diff --git a/lib/routes/quicker/qa.ts b/lib/routes/quicker/qa.ts index 796ac38a71ac..5bdc3f52135e 100644 --- a/lib/routes/quicker/qa.ts +++ b/lib/routes/quicker/qa.ts @@ -84,8 +84,7 @@ async function handler(ctx) { const pubDate = content('.info-text') .first() .text() - .replace(/创建于 /, '') - .trim(); + .replace(/创建于 /, ''); item.description = content('.topic-body').html(); item.author = content('.user-link').first().text(); diff --git a/lib/routes/quicker/share.ts b/lib/routes/quicker/share.ts index cfc78802c5a1..29bd9e508efe 100644 --- a/lib/routes/quicker/share.ts +++ b/lib/routes/quicker/share.ts @@ -74,7 +74,7 @@ async function handler(ctx) { content('section').last().remove(); content('#app').children().slice(0, 2).remove(); - const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); + const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); item.author = content('.user-link').first().text(); item.description = content('div[data-info="动作信息"]').html() ?? content('#app').html() ?? content('.row').eq(1).html(); diff --git a/lib/routes/quicker/user.ts b/lib/routes/quicker/user.ts index 4d4ffea5691d..ca398698ba2b 100644 --- a/lib/routes/quicker/user.ts +++ b/lib/routes/quicker/user.ts @@ -66,7 +66,7 @@ async function handler(ctx) { content('section').last().remove(); content('#app').children().slice(0, 2).remove(); - const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.trim().replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); + const pubDate = content('.text-secondary a').not('.text-secondary').first().text()?.replaceAll(/\s*/g, '') || content('div.note-text').find('span').eq(3).text(); item.author = content('.user-link').first().text(); item.description = content('div[data-info="动作信息"]').html() ?? content('#app').html() ?? content('.row').eq(1).html(); diff --git a/lib/routes/qztc/sjxy/index.ts b/lib/routes/qztc/sjxy/index.ts index 7868446272a6..9973a1a7efb4 100644 --- a/lib/routes/qztc/sjxy/index.ts +++ b/lib/routes/qztc/sjxy/index.ts @@ -101,7 +101,7 @@ async function handler(ctx) { } else { const response = await ofetch(item.link); const $ = load(response); - newItem.description = $('.wp_articlecontent').html() || ''; + newItem.description = $('.wp_articlecontent').html(); } } else { // 涉及到其他站点,不方便做统一的 html 解析,直接返回链接 diff --git a/lib/routes/railway/index.ts b/lib/routes/railway/index.ts index 4c2e89ff6148..967d4fce1cd0 100644 --- a/lib/routes/railway/index.ts +++ b/lib/routes/railway/index.ts @@ -68,6 +68,6 @@ export const fetchArticleDetails = async (url: string) => { const $article = $('article > section '); return { - content: $article.html() ?? undefined, + content: $article.html(), }; }; diff --git a/lib/routes/raycast/changelog.ts b/lib/routes/raycast/changelog.ts index 7e3f8cdba008..431baa994db2 100644 --- a/lib/routes/raycast/changelog.ts +++ b/lib/routes/raycast/changelog.ts @@ -17,8 +17,8 @@ const handler: Route['handler'] = async () => { const $ = load(item); const version = $('span[id]').attr('id'); - const html = $('div.markdown').html() ?? ''; - const date = $('span[class^=ChangelogEntry_changelogDate]').text().trim(); + const html = $('div.markdown').html(); + const date = $('span[class^=ChangelogEntry_changelogDate]').text(); return { title: `Version ${version}`, diff --git a/lib/routes/react/blog.ts b/lib/routes/react/blog.ts index 88e3bc0e9347..90a833a8d9df 100644 --- a/lib/routes/react/blog.ts +++ b/lib/routes/react/blog.ts @@ -23,9 +23,9 @@ const handler: Route['handler'] = async () => { const $ = load(data); return { - title: $('h1').first().text().trim(), + title: $('h1').first().text(), link, - description: $('article div:nth-child(2)').html() ?? '', + description: $('article div:nth-child(2)').html(), pubDate: parseDate($('p.whitespace-pre-wrap').first().text().split(/\s+by/, 1)[0]), }; }); diff --git a/lib/routes/resetera/thread.ts b/lib/routes/resetera/thread.ts index 551c63d50d46..d02698d20468 100644 --- a/lib/routes/resetera/thread.ts +++ b/lib/routes/resetera/thread.ts @@ -93,8 +93,8 @@ const handler: Route['handler'] = async (ctx) => { const dataTime = Number(timeEl.attr('data-time') || 0); const pubDate = timeEl.attr('datetime') || (dataTime ? new Date(dataTime * 1000).toUTCString() : undefined); - // 正文容器(clone 后处理) - const $body = $el.find('.message-body .bbWrapper, .message-content .bbWrapper, .bbWrapper').first().clone(); + // 正文容器(就地处理) + const $body = $el.find('.message-body .bbWrapper, .message-content .bbWrapper, .bbWrapper'); // 去掉引用块(回复别人的引用) $body.find('.bbCodeBlock--quote, blockquote.bbCodeBlock').remove(); @@ -115,16 +115,15 @@ const handler: Route['handler'] = async (ctx) => { const hasImage = imgs.length > 0; // 文字 HTML:移除图片再取 HTML - const $textOnly = $body.clone(); - $textOnly.find('img, picture').remove(); - const textHtml = ($textOnly.html() || '').trim(); + $body.find('img, picture').remove(); + const textHtml = ($body.html() || '').trim(); // 标题:作者 + 楼层号(若能取到) const floor = $el.find('.message-attribution-opposite a').last().text().trim(); const title = author ? `${author}${floor ? ' - ' + floor : ''}` : floor || 'New post'; // 描述:Source 链接 + 文字 + 图片 - const imagesHtml = hasImage ? imgs.map((u) => `<p><img src="${u}" referrerpolicy="no-referrer" /></p>`).join('') : ''; + const imagesHtml = hasImage ? imgs.map((u) => `<p><img src="${u}" /></p>`).join('') : ''; const description = ` <p><a href="${link}">🔗 Source post</a></p> ${textHtml}${imagesHtml} @@ -158,7 +157,6 @@ const handler: Route['handler'] = async (ctx) => { // 5) 标题取“最新页”的 <h1> const title = load(htmlList.at(-1) ?? '')('h1') - .first() .text() .trim() || `ResetEra Thread ${id}`; diff --git a/lib/routes/reuters/common.tsx b/lib/routes/reuters/common.tsx index 70dea8e8d79b..13c50d0b0592 100644 --- a/lib/routes/reuters/common.tsx +++ b/lib/routes/reuters/common.tsx @@ -197,7 +197,6 @@ async function handler(ctx) { const browserHeaders = { Accept: 'application/json, text/plain, */*', 'Accept-Language': 'en-US,en;q=0.9', - Referer: 'https://www.reuters.com/', }; try { diff --git a/lib/routes/rfi/news.ts b/lib/routes/rfi/news.ts index 0f96c906c79b..e60369f022c1 100644 --- a/lib/routes/rfi/news.ts +++ b/lib/routes/rfi/news.ts @@ -60,29 +60,15 @@ async function handler(ctx) { content('.m-interstitial, .m-em-quote svg, .o-self-promo').remove(); - // Reason: pages may have multiple ld+json script tags; iterating separately - // avoids concatenation that produces invalid JSON like "{...}{...}" - let ldJson; - for (const el of content('script[type="application/ld+json"]').toArray()) { - try { - const parsed = JSON.parse(content(el).text()); - const candidates = Array.isArray(parsed) ? parsed : [parsed]; - ldJson = candidates.find((x) => x['@type'] === 'NewsArticle'); - if (ldJson) { - break; - } - } catch { - // skip malformed ld+json blocks - } - } + const ldJson = JSON.parse(content('script[type="application/ld+json"]:contains("NewsArticle")').text()); item.description = content('.t-content__chapo').prop('outerHTML') + content('.t-content__main-media').prop('outerHTML') + content('.t-content__body').html(); - item.pubDate = ldJson?.datePublished ? parseDate(ldJson.datePublished) : undefined; - item.updated = ldJson?.dateModified ? parseDate(ldJson.dateModified) : undefined; - item.author = ldJson?.author?.map((author) => author.name).join(', '); - item.category = ldJson?.keywords; + item.pubDate = ldJson.datePublished ? parseDate(ldJson.datePublished) : undefined; + item.updated = ldJson.dateModified ? parseDate(ldJson.dateModified) : undefined; + item.author = ldJson.author?.map((author) => author.name).join(', '); + item.category = ldJson.keywords; - if (ldJson?.audio) { + if (ldJson.audio) { item.itunes_item_image = ldJson.audio.thumbnailUrl; // TODO: Use Temporal.Duration when https://tc39.es/proposal-temporal/ is GA const durationMatch = ldJson.audio.duration?.match(/P0DT(\d+)H(\d+)M(\d+)S/); diff --git a/lib/routes/rsshub/transform/sitemap.ts b/lib/routes/rsshub/transform/sitemap.ts index 94f63ee65f5e..d10d856a2a31 100644 --- a/lib/routes/rsshub/transform/sitemap.ts +++ b/lib/routes/rsshub/transform/sitemap.ts @@ -33,9 +33,9 @@ async function handler(ctx) { ? urls .map((item) => { try { - const title = $(item).find('loc').text() || ''; - const link = $(item).find('loc').text() || ''; - const description = $(item).find('loc').text() || ''; + const title = $(item).find('loc').text(); + const link = $(item).find('loc').text(); + const description = $(item).find('loc').text(); const pubDate = $(item).find('lastmod').text() || undefined; return { diff --git a/lib/routes/ruankao/news.ts b/lib/routes/ruankao/news.ts index 623ac39ad03a..0876aad1171b 100644 --- a/lib/routes/ruankao/news.ts +++ b/lib/routes/ruankao/news.ts @@ -34,7 +34,7 @@ const handler: Route['handler'] = async () => { // Map through each list item to extract details const contentLinkList = listItems.toArray().map((element) => { - const date = $(element).find('label.time').text().trim().slice(1, -1); + const date = $(element).find('label.time').text().slice(1, -1); const title = $(element).find('a').attr('title')!; const link = $(element).find('a').attr('href')!; diff --git a/lib/routes/rule34video/latest.ts b/lib/routes/rule34video/latest.ts index 1e852e6c8341..e40e7474a01b 100644 --- a/lib/routes/rule34video/latest.ts +++ b/lib/routes/rule34video/latest.ts @@ -46,9 +46,6 @@ async function handler() { const response = await got({ method: 'get', url: 'https://www.rule34video.com/latest-updates/', - headers: { - Referer: 'https://www.rule34video.com', - }, }); const $ = load(response.data); diff --git a/lib/routes/rustcc/jobs.ts b/lib/routes/rustcc/jobs.ts index d38040fdc08d..e994b0adb14a 100644 --- a/lib/routes/rustcc/jobs.ts +++ b/lib/routes/rustcc/jobs.ts @@ -36,9 +36,6 @@ async function handler() { const response = await got({ url: jobs_url, - headers: { - Referer: base_url, - }, }); const $ = load(response.data); diff --git a/lib/routes/rustcc/news.ts b/lib/routes/rustcc/news.ts index 8f508d133e87..d937223b2322 100644 --- a/lib/routes/rustcc/news.ts +++ b/lib/routes/rustcc/news.ts @@ -27,9 +27,6 @@ async function handler() { const response = await got({ url: newsUrl, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data); diff --git a/lib/routes/samrdprc/index.ts b/lib/routes/samrdprc/index.ts index acca0f806a06..37af1345653a 100644 --- a/lib/routes/samrdprc/index.ts +++ b/lib/routes/samrdprc/index.ts @@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise<Data> => { .toArray() .map((el): Element => { const $el: Cheerio<Element> = $(el); - const $aEl: Cheerio<Element> = $el.find('a').first(); + const $aEl: Cheerio<Element> = $el.find('a'); const title: string = $aEl.text(); const pubDateStr: string | undefined = $el.find('span').text(); @@ -54,7 +54,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.show_tit').text(); - const description: string | undefined = $$('div.TRS_Editor div.TRS_Editor').html() ?? undefined; + const description = $$('div.TRS_Editor div.TRS_Editor').html(); const pubDateStr: string | undefined = $$('div.show_tit2').text().split(/:/).pop()?.trim(); const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? []; const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/samrdprc/news.ts b/lib/routes/samrdprc/news.ts index 0009506f8f30..51e60588f670 100644 --- a/lib/routes/samrdprc/news.ts +++ b/lib/routes/samrdprc/news.ts @@ -57,7 +57,7 @@ export const route: Route = { const detail = await got(link); const $ = load(detail.body); - const articleHTML = $('div.main > div.box1 > div.box_main > div.boxl.fl > div.show_txt > div.TRS_Editor > div').html() || ''; + const articleHTML = $('div.main > div.box1 > div.box_main > div.boxl.fl > div.show_txt > div.TRS_Editor > div').html(); return { title, diff --git a/lib/routes/sankei/news.ts b/lib/routes/sankei/news.ts index 192f389747c6..6fe7422d75ca 100644 --- a/lib/routes/sankei/news.ts +++ b/lib/routes/sankei/news.ts @@ -45,7 +45,7 @@ async function handler(ctx: Context): Promise<Data> { const detail = await got(link); const $ = load(detail.body); $('.inline-gptAd, .figure_image_sizer').remove(); - const articleHTML = $('div.article-body').html() || ''; + const articleHTML = $('div.article-body').html(); return { title, diff --git a/lib/routes/sankei/topics.ts b/lib/routes/sankei/topics.ts index 6e18c714e796..55eff0d11ff0 100644 --- a/lib/routes/sankei/topics.ts +++ b/lib/routes/sankei/topics.ts @@ -48,7 +48,7 @@ async function handler(ctx: Context): Promise<Data> { const detail = await got(link); const $ = load(detail.body); $('.inline-gptAd, .figure_image_sizer').remove(); - const articleHTML = $('div.article-body').html() || ''; + const articleHTML = $('div.article-body').html(); return { title, diff --git a/lib/routes/saraba1st/thread.ts b/lib/routes/saraba1st/thread.ts index aef3238bfdf5..be2613bc1546 100644 --- a/lib/routes/saraba1st/thread.ts +++ b/lib/routes/saraba1st/thread.ts @@ -68,7 +68,6 @@ async function handler(ctx) { imgHtml.removeAttr('zoomfile'); imgHtml.removeAttr('file'); imgHtml.removeAttr('onmouseover'); - imgHtml.removeAttr('onclick'); } contentHtml.find('div.aimg_tip').remove(); return { diff --git a/lib/routes/sass/gs/index.ts b/lib/routes/sass/gs/index.ts index 4bda148e535d..52260de5a1e1 100644 --- a/lib/routes/sass/gs/index.ts +++ b/lib/routes/sass/gs/index.ts @@ -52,7 +52,7 @@ async function handler(ctx) { if (itemUrl) { const result = await got(itemUrl); const $ = load(result.data); - description = $('.read .wp_articlecontent').length ? $('.read .wp_articlecontent').html().trim() : itemTitle; + description = $('.read .wp_articlecontent').length ? $('.read .wp_articlecontent').html() : itemTitle; } else { description = itemTitle; } diff --git a/lib/routes/scmp/index.ts b/lib/routes/scmp/index.ts index 8fc7551f7d6a..0bc113875196 100644 --- a/lib/routes/scmp/index.ts +++ b/lib/routes/scmp/index.ts @@ -56,7 +56,7 @@ async function handler(ctx) { .toArray() .map((elem) => { const item = $(elem); - const enclosure = item.find('enclosure').first(); + const enclosure = item.find('enclosure'); const mediaContent = item.find(String.raw`media\:content`).toArray()[0]; const thumbnail = item.find(String.raw`media\:thumbnail`).toArray()[0]; return { diff --git a/lib/routes/scnu/cs/match.ts b/lib/routes/scnu/cs/match.ts index 4d56f42a377a..32b0d38aff0c 100644 --- a/lib/routes/scnu/cs/match.ts +++ b/lib/routes/scnu/cs/match.ts @@ -34,9 +34,6 @@ async function handler() { const res = await got({ method: 'get', url, - headers: { - Referer: baseUrl, - }, }); const $ = load(res.data); const list = $('.listshow li a'); diff --git a/lib/routes/scnu/jw.ts b/lib/routes/scnu/jw.ts index 591a5a9b0541..8e0a7a883c5b 100644 --- a/lib/routes/scnu/jw.ts +++ b/lib/routes/scnu/jw.ts @@ -34,15 +34,12 @@ async function handler() { const res = await got({ method: 'get', url, - headers: { - Referer: baseUrl, - }, }); const $ = load(res.data); const list = $('.notice_01').find('li'); return { - title: $('title').first().text(), + title: $('title').text(), link: url, description: '华南师范大学教务处 - 通知公告', item: diff --git a/lib/routes/scnu/library.ts b/lib/routes/scnu/library.ts index 9972b43eaff0..787f77d1912b 100644 --- a/lib/routes/scnu/library.ts +++ b/lib/routes/scnu/library.ts @@ -34,9 +34,6 @@ async function handler() { const res = await got({ method: 'get', url, - headers: { - Referer: baseUrl, - }, }); const $ = load(res.data); const list = $('.article-list').find('li'); diff --git a/lib/routes/scpta/news.ts b/lib/routes/scpta/news.ts index c4173a4091ff..8987f0959e0b 100644 --- a/lib/routes/scpta/news.ts +++ b/lib/routes/scpta/news.ts @@ -61,7 +61,7 @@ async function handler(ctx) { return { title: item.find('a').attr('title'), link: `${baseUrl}${item.find('a').attr('href')}`, - pubDate: parseDate(item.find('span').text().trim()), + pubDate: parseDate(item.find('span').text()), }; }); // 获取公告详情 diff --git a/lib/routes/scut/jwc/news.ts b/lib/routes/scut/jwc/news.ts index a5d5c68b5d60..70c9aff75479 100644 --- a/lib/routes/scut/jwc/news.ts +++ b/lib/routes/scut/jwc/news.ts @@ -3,6 +3,7 @@ import querystring from 'node:querystring'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import timezone from '@/utils/timezone'; const baseUrl = 'http://jw.scut.edu.cn'; const refererUrl = baseUrl + '/dist/'; @@ -14,13 +15,6 @@ const articleApiUrl = baseUrl + '/zhinan/jw/api/v2/getArticleInfo.do'; const getArticleUrlById = (id) => `${baseUrl}/zhinan/cms/article/view.do?type=posts&id=${id}`; const getArticleMobileUrlById = (id) => `${baseUrl}/dist/#/detail/index?id=${id}&type=news`; -const convertTimezoneToCST = (date) => { - const timeZone = 8; - const serverOffset = date.getTimezoneOffset() / 60; - - return new Date(date.getTime() - 60 * 60 * 1000 * (timeZone + serverOffset)); -}; - const generateArticlePubDate = (createDateStr) => { const date = new Date(createDateStr); date.setHours(8); @@ -28,7 +22,7 @@ const generateArticlePubDate = (createDateStr) => { date.setSeconds(0); date.setMilliseconds(0); - return convertTimezoneToCST(date); + return timezone(date, 8); }; const isRedirectPage = (data) => !!data.link; diff --git a/lib/routes/scut/jwc/notice.ts b/lib/routes/scut/jwc/notice.ts index 3d2404ea616c..64e140b14f30 100644 --- a/lib/routes/scut/jwc/notice.ts +++ b/lib/routes/scut/jwc/notice.ts @@ -3,6 +3,7 @@ import querystring from 'node:querystring'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import timezone from '@/utils/timezone'; const baseUrl = 'http://jw.scut.edu.cn'; const refererUrl = baseUrl + '/dist/'; @@ -24,13 +25,6 @@ const categoryMap = { info: { title: '信息', tag: '6' }, }; -const convertTimezoneToCST = (date) => { - const timeZone = 8; - const serverOffset = date.getTimezoneOffset() / 60; - - return new Date(date.getTime() - 60 * 60 * 1000 * (timeZone + serverOffset)); -}; - const generateArticlePubDate = (createDateStr) => { const date = new Date(createDateStr); date.setHours(8); @@ -38,7 +32,7 @@ const generateArticlePubDate = (createDateStr) => { date.setSeconds(0); date.setMilliseconds(0); - return convertTimezoneToCST(date); + return timezone(date, 8); }; const isRedirectPage = (data) => !!data.link; diff --git a/lib/routes/scut/jwc/school.ts b/lib/routes/scut/jwc/school.ts index 0d7823d38a7f..382db9816cea 100644 --- a/lib/routes/scut/jwc/school.ts +++ b/lib/routes/scut/jwc/school.ts @@ -3,6 +3,7 @@ import querystring from 'node:querystring'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import timezone from '@/utils/timezone'; const baseUrl = 'http://jw.scut.edu.cn'; const refererUrl = baseUrl + '/dist/'; @@ -21,13 +22,6 @@ const categoryMap = { info: { title: '信息', tag: '6' }, }; -const convertTimezoneToCST = (date) => { - const timeZone = 8; - const serverOffset = date.getTimezoneOffset() / 60; - - return new Date(date.getTime() - 60 * 60 * 1000 * (timeZone + serverOffset)); -}; - const generateArticlePubDate = (createDateStr) => { const date = new Date(createDateStr); date.setHours(8); @@ -35,7 +29,7 @@ const generateArticlePubDate = (createDateStr) => { date.setSeconds(0); date.setMilliseconds(0); - return convertTimezoneToCST(date); + return timezone(date, 8); }; const isRedirectPage = (data) => !!data.link; diff --git a/lib/routes/shanghaimuseum/offline-exhibit.tsx b/lib/routes/shanghaimuseum/offline-exhibit.tsx index d4ed23112f09..afb70035b142 100644 --- a/lib/routes/shanghaimuseum/offline-exhibit.tsx +++ b/lib/routes/shanghaimuseum/offline-exhibit.tsx @@ -77,7 +77,7 @@ export const route: Route = { const pubDate = timezone(parseDate(item.issueTime), 8); const fullDuration = item.exhibitDateRange || ''; - const [startDate, endDate] = fullDuration.includes(' - ') ? fullDuration.split(' - ').map((s) => s.trim()) : [fullDuration, '']; + const [startDate, endDate] = fullDuration.includes(' - ') ? fullDuration.split(' - ') : [fullDuration, '']; const description = renderToString( <div> diff --git a/lib/routes/shisu/en.ts b/lib/routes/shisu/en.ts index 5845a98a1fa3..8b51836f81c0 100644 --- a/lib/routes/shisu/en.ts +++ b/lib/routes/shisu/en.ts @@ -36,7 +36,7 @@ async function process(baseUrl: string, section: any) { const img = i.find('img').attr('src'); const link = `${baseUrl}${i.find('h3>a').attr('href')}`; return { - title: i.find('h3>a').text().trim(), + title: i.find('h3>a').text(), link, pubDate: parseDate(i.find('p.time').text()), itunes_item_image: `${baseUrl}${img}`, diff --git a/lib/routes/shisu/news.ts b/lib/routes/shisu/news.ts index b7f2c6b6c530..84a85e7b4439 100644 --- a/lib/routes/shisu/news.ts +++ b/lib/routes/shisu/news.ts @@ -61,7 +61,7 @@ async function handler(ctx) { .map((i0) => { const i = $(i0); return { - title: i.find('h3>a').attr('title')?.trim(), + title: i.find('h3>a').attr('title'), link: `${url}${i.find('h3>a').attr('href')}`, category: i.find('p>span:nth-child(1)').text(), }; diff --git a/lib/routes/shmeea/index.ts b/lib/routes/shmeea/index.ts index 79304183ad70..aca0036ecc3b 100644 --- a/lib/routes/shmeea/index.ts +++ b/lib/routes/shmeea/index.ts @@ -48,7 +48,7 @@ async function handler(ctx) { return { title: item.find('a').attr('title') || item.find('a').text(), link: new URL(item.find('a').attr('href'), baseURL).href, - pubDate: parseDate(item.find('.listTime').text().trim(), 'YYYY-MM-DD'), + pubDate: parseDate(item.find('.listTime').text(), 'YYYY-MM-DD'), }; }); @@ -63,7 +63,7 @@ async function handler(ctx) { const $ = load(result.data); const description = $('#ivs_content').html(); - const pbTimeText = $('#ivs_title .PBtime').text().trim(); + const pbTimeText = $('#ivs_title .PBtime').text(); item.description = description; item.pubDate = pbTimeText ? timezone(parseDate(pbTimeText, 'YYYY-MM-DD HH:mm:ss'), 8) : item.pubDate; diff --git a/lib/routes/shopify/apps/[handle].reviews.ts b/lib/routes/shopify/apps/[handle].reviews.ts index e124416eb171..94bfad83e22d 100644 --- a/lib/routes/shopify/apps/[handle].reviews.ts +++ b/lib/routes/shopify/apps/[handle].reviews.ts @@ -30,7 +30,6 @@ async function handler(ctx: Context): Promise<Data> { headers: { accept: 'text/html, application/xhtml+xml', 'accept-language': 'en-US;q=0.9', - referer: baseURL, dnt: '1', }, }); @@ -62,7 +61,7 @@ async function handler(ctx: Context): Promise<Data> { _extra: { ratting_value: Number($review1.find('div[role="img"]').attr('aria-label')?.slice(0, 1)), - location: $review2.find('div.tw-text-fg-primary + div').text().trim(), + location: $review2.find('div.tw-text-fg-primary + div').text(), author, }, }; diff --git a/lib/routes/shopify/apps/search.ts b/lib/routes/shopify/apps/search.ts index 0e03c08c7405..20a09822af0b 100644 --- a/lib/routes/shopify/apps/search.ts +++ b/lib/routes/shopify/apps/search.ts @@ -37,7 +37,6 @@ async function handler(ctx: Context): Promise<Data> { accept: 'text/html, application/xhtml+xml', 'accept-language': 'en-US;q=0.9', 'turbo-frame': 'search_page', - referer: baseURL, dnt: '1', }, }); @@ -50,7 +49,7 @@ async function handler(ctx: Context): Promise<Data> { .map((item) => { const handle = $(item).attr('data-app-card-handle-value'); - const appInfo = $(item).find('div.tw-self-stretch').clone(); + const appInfo = $(item).find('div.tw-self-stretch'); const rattingMatch = appInfo .find('span') diff --git a/lib/routes/shu/global.ts b/lib/routes/shu/global.ts index c1d1f7d3c765..b5bb8a498f70 100644 --- a/lib/routes/shu/global.ts +++ b/lib/routes/shu/global.ts @@ -61,10 +61,10 @@ async function handler(ctx) { .map((el) => { const item = $(el); // 使用Cheerio包装每个li元素 const rawLink = item.find('a').attr('href'); - const pubDate = item.find('span').text().trim(); // 提取日期 + const pubDate = item.find('span').text(); // 提取日期 return { - title: item.find('a').text().trim(), // 获取标题 + title: item.find('a').text(), // 获取标题 link: rawLink ? new URL(rawLink, rootUrl).href : rootUrl, // 生成完整链接 pubDate: timezone(parseDate(pubDate, 'YYYY年MM月DD日'), 8), // 解析并转换日期 description: '', // 没有提供简要描述,设为空字符串 diff --git a/lib/routes/shu/index.ts b/lib/routes/shu/index.ts index 1d86b049dc1e..cbfdce94a31e 100644 --- a/lib/routes/shu/index.ts +++ b/lib/routes/shu/index.ts @@ -63,10 +63,10 @@ async function handler(ctx) { const item = $(el); // Wrap `el` in a Cheerio object const rawLink = item.find('a').attr('href'); return { - title: item.find('p.bt').text().trim(), + title: item.find('p.bt').text(), link: rawLink ? new URL(rawLink, rootUrl).href : rootUrl, - pubDate: timezone(parseDate(item.find('p.sj').text().trim(), 'YYYY.MM.DD'), 8), - description: item.find('p.zy').text().trim(), + pubDate: timezone(parseDate(item.find('p.sj').text(), 'YYYY.MM.DD'), 8), + description: item.find('p.zy').text(), }; }); diff --git a/lib/routes/sjtu/cs/tzgg.tsx b/lib/routes/sjtu/cs/tzgg.tsx index bd6027f6e1aa..d3e2c9c5d85c 100644 --- a/lib/routes/sjtu/cs/tzgg.tsx +++ b/lib/routes/sjtu/cs/tzgg.tsx @@ -59,19 +59,6 @@ function enrichItem(item: ListItem): Promise<DataItem> { const $body = $('div.xw-cont'); const $txt = $body.find('.txt'); - $txt.find('img').each((_, e) => { - const src = $(e).attr('src') || $(e).attr('_src'); - if (src) { - $(e).attr('src', absolutize(src)); - } - }); - $txt.find('a').each((_, e) => { - const href = $(e).attr('href'); - if (href) { - $(e).attr('href', absolutize(href)); - } - }); - const publishedText = $body.find('.jj p').first().text(); const publishedMatch = publishedText.match(/(\d{4})-(\d{1,2})-(\d{1,2})/); let pubDate: Date | undefined; @@ -83,7 +70,7 @@ function enrichItem(item: ListItem): Promise<DataItem> { return { title: item.title, link: item.link, - description: $txt.html() ?? '', + description: $txt.html(), pubDate: pubDate ?? timezone(parseDate(item.date, 'YYYY-MM-DD'), 8), }; }) as Promise<DataItem>; diff --git a/lib/routes/sjtu/cs/xshd.tsx b/lib/routes/sjtu/cs/xshd.tsx index ec985124a78c..2860bf87499f 100644 --- a/lib/routes/sjtu/cs/xshd.tsx +++ b/lib/routes/sjtu/cs/xshd.tsx @@ -1,4 +1,3 @@ -import type { Cheerio, CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; @@ -76,21 +75,6 @@ function renderDescription(item: ListItem): string { ); } -function rewriteRelativeUrls($: CheerioAPI, item: Cheerio<any>): void { - item.find('img').each((_, e) => { - const src = $(e).attr('src') || $(e).attr('_src'); - if (src) { - $(e).attr('src', absolutize(src)); - } - }); - item.find('a').each((_, e) => { - const href = $(e).attr('href'); - if (href) { - $(e).attr('href', absolutize(href)); - } - }); -} - function extractWechatUrl(finalUrl: string): string { const url = new URL(finalUrl); return url.searchParams.get('target_url') || finalUrl; @@ -129,7 +113,6 @@ function enrichItem(item: ListItem): Promise<DataItem> { const $body = $('.xw-cont'); if ($body.length > 0) { const $txt = $body.find('.txt'); - rewriteRelativeUrls($, $txt); description += `<hr>${$txt.html() ?? ''}`; const publishedText = $body.find('.jj p').first().text(); diff --git a/lib/routes/sjtu/jwc.ts b/lib/routes/sjtu/jwc.ts index 743fadccfcd7..6fed251b72e2 100644 --- a/lib/routes/sjtu/jwc.ts +++ b/lib/routes/sjtu/jwc.ts @@ -17,17 +17,6 @@ async function getFullArticle(link) { if (content.length === 0) { return null; } - // resolve links of <img> and <a> - content.find('img').each((_, e) => { - const relativeLink = $(e).attr('src'); - const absLink = new URL(relativeLink, urlRoot).href; - $(e).attr('src', absLink); - }); - content.find('a').each((_, e) => { - const relativeLink = $(e).attr('href'); - const absLink = new URL(relativeLink, urlRoot).href; - $(e).attr('href', absLink); - }); return content.html() + ($('.Newslist2').length ? $('.Newslist2').html() : ''); } diff --git a/lib/routes/smashingmagazine/category.ts b/lib/routes/smashingmagazine/category.ts index bbe17e48589e..1a12f5a833af 100644 --- a/lib/routes/smashingmagazine/category.ts +++ b/lib/routes/smashingmagazine/category.ts @@ -77,7 +77,11 @@ async function handler(ctx) { .map((item) => { item = $(item); const a = item.find('h2.article--post__title a'); - const description = item.find('p.article--post__teaser').clone().children().remove().end().text(); + const description = item + .find('p.article--post__teaser') + .contents() + .filter((_, node) => node.type === 'text') + .text(); const author = item.find('span.article--post__author-name a').text(); const time = $('p.article--post__teaser time').attr('datetime'); const pubDate = parseDate(time, 'YYYY-MM-DD'); @@ -98,10 +102,9 @@ async function handler(ctx) { item.category = $('li.meta-box--tags a') .toArray() .map((item) => $(item).text()); - const header = $('div#article__content header.article-header').clone().children('ul').remove().end().html(); + const header = $('div#article__content header.article-header').children('ul').remove().end().html(); const summary = $('div#article__content section.article__summary').html(); const descr = $('div#article__content div.c-garfield-the-cat') - .clone() .children('div') .remove() .end() diff --git a/lib/routes/smzdm/haowen.ts b/lib/routes/smzdm/haowen.ts index 2b616a0e81d1..78672245eca1 100644 --- a/lib/routes/smzdm/haowen.ts +++ b/lib/routes/smzdm/haowen.ts @@ -84,7 +84,7 @@ async function handler(ctx) { const outItem: DataItem = { title: item.title, link: item.link, - description: content.html() || '', + description: content.html(), pubDate: releaseDate ? timezone(parseDate(releaseDate), 8) : item.pubDate, author: $('meta[property="og:author"]').attr('content') || '', }; diff --git a/lib/routes/snnu/ccs.ts b/lib/routes/snnu/ccs.ts index 7eb0f1cb8abe..cf9cfcdf306b 100644 --- a/lib/routes/snnu/ccs.ts +++ b/lib/routes/snnu/ccs.ts @@ -73,7 +73,7 @@ export const route: Route = { try { const detailResponse = await ofetch(link); const $$ = load(detailResponse); - const description = $$('.v_news_content').html() || $$('#vsb_content').html() || ''; + const description = $$('.v_news_content').html() || $$('#vsb_content').html(); return { title, diff --git a/lib/routes/snnu/index.ts b/lib/routes/snnu/index.ts index b50d6704ae93..5ce63aef0dbc 100644 --- a/lib/routes/snnu/index.ts +++ b/lib/routes/snnu/index.ts @@ -36,12 +36,12 @@ export const route: Route = { const items = await Promise.all( list.map((item) => { const $item = $(item); - const $link = $item.find('a').first(); + const $link = $item.find('a'); const link = new URL($link.attr('href') || '', url).href; - const pubDate = parseDate($item.find('.date.date2').first().text()); + const pubDate = parseDate($item.find('.date.date2').text()); - let title = $item.find('a .txt h3').first().text(); + let title = $item.find('a .txt h3').text(); if (!title) { title = $link.text(); } @@ -50,7 +50,7 @@ export const route: Route = { try { const detailResponse = await ofetch(link); const $$ = load(detailResponse); - const description = $$('.v_news_content').html() || $$('#vsb_content').html() || ''; + const description = $$('.v_news_content').html() || $$('#vsb_content').html(); return { title, diff --git a/lib/routes/snnu/yjs.ts b/lib/routes/snnu/yjs.ts index a19425414e54..76313269a52c 100644 --- a/lib/routes/snnu/yjs.ts +++ b/lib/routes/snnu/yjs.ts @@ -46,7 +46,7 @@ export const route: Route = { try { const detailResponse = await ofetch(link); const $$ = load(detailResponse); - const description = $$('.v_news_content').html() || $$('#vsb_content').html() || ''; + const description = $$('.v_news_content').html() || $$('#vsb_content').html(); return { title, diff --git a/lib/routes/solidot/_article.ts b/lib/routes/solidot/_article.ts index 39d28f8d54dd..b2cbf044c13c 100644 --- a/lib/routes/solidot/_article.ts +++ b/lib/routes/solidot/_article.ts @@ -17,7 +17,10 @@ export default async function get_article(url) { const data = response.data; const $ = load(data); - const date_raw = $('div.talk_time').clone().children().remove().end().text(); + const date_raw = $('div.talk_time') + .contents() + .filter((_, node) => node.type === 'text') + .text(); const date_str_zh = date_raw.replaceAll(/^[^`]*发表于(?=(.*分))\1[^`]*$/g, '$1'); // use [^`] to match \n const date_str = date_str_zh .replaceAll(/[年月]/g, '-') @@ -34,7 +37,6 @@ export default async function get_article(url) { const description = $('div.block_m') .html() .replaceAll(/(href.*?)<u>(.*?)<\/u>/g, '$1$2') - .replaceAll('href="/', () => 'href="' + domain + '/') // Preserve the not extremely disturbing donation ad // to support the site. .replaceAll(/(<img.*liiLIZF8Uh6yM.*?>)/g, '<br><br>$1'); diff --git a/lib/routes/stcn/index.ts b/lib/routes/stcn/index.ts index 3110ccf99c62..5ba62ff6f10d 100644 --- a/lib/routes/stcn/index.ts +++ b/lib/routes/stcn/index.ts @@ -31,7 +31,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const title: string = $aEl.text(); const description: string = $el.find('div.text').html(); - const pubDateStr: string | undefined = $el.find('div.info span').last().text().trim(); + const pubDateStr: string | undefined = $el.find('div.info span').last().text(); const linkUrl: string | undefined = $aEl.attr('href'); const categoryEls: Element[] = $el.find('div.tags span').toArray(); const categories: string[] = [...new Set(categoryEls.map((el) => $(el).text()).filter(Boolean))]; @@ -71,8 +71,8 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.detail-title').text(); - const description: string = $$('div.detail-content').html() ?? ''; - const pubDateStr: string | undefined = $$('div.detail-info span').last().text().trim(); + const description = $$('div.detail-content').html(); + const pubDateStr: string | undefined = $$('div.detail-info span').last().text(); const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? []; const authors: DataItem['author'] = $$('div.detail-info span').first().text().split(/:/).pop(); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/stcn/kx.ts b/lib/routes/stcn/kx.ts index d55bb8b6144d..18dbec08f1d4 100644 --- a/lib/routes/stcn/kx.ts +++ b/lib/routes/stcn/kx.ts @@ -72,8 +72,8 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.detail-title').text(); - const description: string = $$('div.detail-content').html() ?? ''; - const pubDateStr: string | undefined = $$('div.detail-info span').last().text().trim(); + const description = $$('div.detail-content').html(); + const pubDateStr: string | undefined = $$('div.detail-info span').last().text(); const categories: string[] = [...new Set([...(item.category as string[]), ...($$('meta[name="keywords"]').attr('content')?.split(/,/) ?? [])])]; const authors: DataItem['author'] = $$('div.detail-info span').first().text().split(/:/).pop(); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/stcn/rank.ts b/lib/routes/stcn/rank.ts index e50753f3fd41..27bfdb8790a8 100644 --- a/lib/routes/stcn/rank.ts +++ b/lib/routes/stcn/rank.ts @@ -55,8 +55,8 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('div.detail-title').text(); - const description: string = $$('div.detail-content').html() ?? ''; - const pubDateStr: string | undefined = $$('div.detail-info span').last().text().trim(); + const description = $$('div.detail-content').html(); + const pubDateStr: string | undefined = $$('div.detail-info span').last().text(); const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? []; const authors: DataItem['author'] = $$('div.detail-info span').first().text().split(/:/).pop(); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/steam/search.ts b/lib/routes/steam/search.ts index 19ea613ba19c..94ae17dc6926 100644 --- a/lib/routes/steam/search.ts +++ b/lib/routes/steam/search.ts @@ -46,11 +46,11 @@ async function handler(ctx) { desc += `Items count: ${bundle.m_rgItems.length}\n`; } if (isDiscounted) { - desc += `Discount: ${$el.find('.discount_pct').text().trim()}\n`; - desc += `Original price: ${$el.find('.discount_original_price').text().trim()}\n`; - desc += `Discounted price: ${$el.find('.discount_final_price').text().trim()}\n`; + desc += `Discount: ${$el.find('.discount_pct').text()}\n`; + desc += `Original price: ${$el.find('.discount_original_price').text()}\n`; + desc += `Discounted price: ${$el.find('.discount_final_price').text()}\n`; } else { - desc += `Price: ${$el.find('.discount_final_price').text().trim()}\n`; + desc += `Price: ${$el.find('.discount_final_price').text()}\n`; } if (hasReview) { desc += $el.find('.search_review_summary').attr('data-tooltip-html'); diff --git a/lib/routes/steam/sharefile-changelog.ts b/lib/routes/steam/sharefile-changelog.ts index ddf485264119..b8356d4392b7 100644 --- a/lib/routes/steam/sharefile-changelog.ts +++ b/lib/routes/steam/sharefile-changelog.ts @@ -34,18 +34,18 @@ Helpful route parameters: const response = await ofetch(url); const $ = load(response); - const appName = $('div.apphub_AppName').first().text(); + const appName = $('div.apphub_AppName').text(); const appIcon = $('div.apphub_AppIcon').children('img').attr('src'); - const itemTitle = $('div.workshopItemTitle').first().text(); + const itemTitle = $('div.workshopItemTitle').text(); const items = $('div.clearfix .changeLogCtn') .toArray() .map((item) => { item = $(item); // changelogHeadline is local time - const changelogHeadline = item.find('.headline').first().text(); - const changelogTimestamp = item.find('p').first().attr('id'); - const changeDetail = item.find('p').first().html(); + const changelogHeadline = item.find('.headline').text(); + const changelogTimestamp = item.find('p').attr('id'); + const changeDetail = item.find('p').html(); return { title: changelogHeadline, diff --git a/lib/routes/swjtu/scai.ts b/lib/routes/swjtu/scai.ts index 9adb79a62c2c..7241fb6892f4 100644 --- a/lib/routes/swjtu/scai.ts +++ b/lib/routes/swjtu/scai.ts @@ -74,8 +74,8 @@ const getItem = (item, cache) => { pubDate = parseDate(dateMatch[0]); } else { const dateItem = item.find('.calendar'); // 注意 .calendar 是 class - const day = dateItem.find('.day').text().trim(); // "31" (文本需 trim 去空格) - const ymd = dateItem.find('.date').text().trim(); // "2025/03" + const day = dateItem.find('.day').text(); // "31" + const ymd = dateItem.find('.date').text(); // "2025/03" const [year, month] = ymd.split('/', 2); // ["2025", "03"] const dateText = `${year}-${month}-${day.padStart(2, '0')}`; pubDate = new Date(dateText); diff --git a/lib/routes/sxhm/announcement.ts b/lib/routes/sxhm/announcement.ts index 09fa5d3e9142..412f1c595777 100644 --- a/lib/routes/sxhm/announcement.ts +++ b/lib/routes/sxhm/announcement.ts @@ -40,7 +40,7 @@ export const route: Route = { const title = $item.find('.ltit .t').text(); const link = new URL($a.attr('href')!, baseUrl).href; - const dateStr = $item.find('.lcont .date').text().trim(); + const dateStr = $item.find('.lcont .date').text(); return { title, diff --git a/lib/routes/syosetu/dev.ts b/lib/routes/syosetu/dev.ts index 61ea237809ba..dbd85e91f5e3 100644 --- a/lib/routes/syosetu/dev.ts +++ b/lib/routes/syosetu/dev.ts @@ -40,7 +40,7 @@ async function handler(): Promise<Data> { const dates = logContainer .find('dt') .toArray() - .map((element) => $(element).text().trim()); + .map((element) => $(element).text()); const contents = logContainer .find('dd') diff --git a/lib/routes/syosetu/utils.ts b/lib/routes/syosetu/utils.ts index f7738467e2a6..ee68dd502571 100644 --- a/lib/routes/syosetu/utils.ts +++ b/lib/routes/syosetu/utils.ts @@ -38,7 +38,7 @@ export async function fetchChapterContent(chapterUrl: string, chapter?: number): const $ = load(response); const title = `${chapter ? `#${chapter} ` : ''}${$('.p-novel__title').html() || ''}`; - const description = $('.p-novel__body').html() || ''; + const description = $('.p-novel__body').html(); const pubDate = $('meta[name=WWWC]').attr('content'); return { diff --git a/lib/routes/sysu/cse.ts b/lib/routes/sysu/cse.ts index ae61712ba545..39ca483dbc9f 100644 --- a/lib/routes/sysu/cse.ts +++ b/lib/routes/sysu/cse.ts @@ -31,9 +31,6 @@ async function handler() { const response = await got({ method: 'get', url: 'http://cse.sysu.edu.cn/', - headers: { - Referer: 'http://cse.sysu.edu.cn/', - }, }); const $ = load(response.data); diff --git a/lib/routes/szftedu/dongtai.ts b/lib/routes/szftedu/dongtai.ts index 320f4766c86f..75fc1c76966d 100644 --- a/lib/routes/szftedu/dongtai.ts +++ b/lib/routes/szftedu/dongtai.ts @@ -35,7 +35,7 @@ async function handler() { const lists = $('div.pagenews04 div ul li') .toArray() .map((el) => ({ - title: $('a', el).text().trim(), + title: $('a', el).text(), link: $('a', el).attr('href'), pubDate: timezone(parseDate($('span[class=canedit]', el).text()), 8), })); diff --git a/lib/routes/szftedu/gonggao.ts b/lib/routes/szftedu/gonggao.ts index d0f58191f8ca..3c15638add78 100644 --- a/lib/routes/szftedu/gonggao.ts +++ b/lib/routes/szftedu/gonggao.ts @@ -35,7 +35,7 @@ async function handler() { const lists = $('div.pagenews04 div ul li') .toArray() .map((el) => ({ - title: $('a', el).text().trim(), + title: $('a', el).text(), link: $('a', el).attr('href'), pubDate: timezone(parseDate($('span[class=canedit]', el).text()), 8), })); diff --git a/lib/routes/szmuseum/temporary.tsx b/lib/routes/szmuseum/temporary.tsx index ec33cf64d885..eaeec6ba26b4 100644 --- a/lib/routes/szmuseum/temporary.tsx +++ b/lib/routes/szmuseum/temporary.tsx @@ -66,10 +66,10 @@ export const route: Route = { const linkStr = new URL(a.attr('href') || '', baseUrl); linkStr.search = ''; // remove dynamic date stamp from the link const link = linkStr.href; - const imgUrl = new URL($item.find('.aleftimg img').attr('src') || '', baseUrl).href; + const imgUrl = $item.find('.aleftimg img').attr('src'); - const fullDuration = $item.find('.activity_r_detail p:nth-child(1) span:nth-child(2)').text().trim(); - const location = $item.find('.activity_r_detail p:nth-child(2) span:nth-child(2)').text().trim(); + const fullDuration = $item.find('.activity_r_detail p:nth-child(1) span:nth-child(2)').text(); + const location = $item.find('.activity_r_detail p:nth-child(2) span:nth-child(2)').text(); let startDate; let endDate; diff --git a/lib/routes/szse/notice.ts b/lib/routes/szse/notice.ts index dc3091a1747c..f3d49cf3b2ed 100644 --- a/lib/routes/szse/notice.ts +++ b/lib/routes/szse/notice.ts @@ -31,9 +31,7 @@ export const route: Route = { async function handler() { const link = 'http://www.szse.cn/disclosure/notice/company/index.html'; - const response = await got.get(link, { - Referer: host, - }); + const response = await got.get(link); const $ = load(response.data); // 正则表达式匹配Script标签的url和title变量 function getData(jscontent, option) { @@ -75,9 +73,7 @@ async function handler() { if (cacheIn) { return JSON.parse(cacheIn); } - const response = await got.get(itemUrl, { - Referer: host, - }); + const response = await got.get(itemUrl); const $ = load(response.data); const description = $('#desContent').html(); const single = { diff --git a/lib/routes/szu/yz/utils.ts b/lib/routes/szu/yz/utils.ts index a479966b3fde..620e4513dc09 100644 --- a/lib/routes/szu/yz/utils.ts +++ b/lib/routes/szu/yz/utils.ts @@ -25,43 +25,21 @@ const ProcessFeed = (list, cache, current) => const data = response.data; $ = load(data); // 使用 cheerio 加载返回的 HTML - // 还原图片地址 - $(`${current.selector.content} img`).each((index, elem) => { - const $elem = $(elem); - const src = $elem.attr('src'); - if (src) { - $elem.attr('src', new URL(src, current.url).href); - } - }); - - // 还原链接地址 - $(`${current.selector.content} a, ul[style]`).each((index, elem) => { - const $elem = $(elem); - const src = $elem.attr('href'); - if (src) { - $elem.attr('href', new URL(src, current.url).href); - } - }); - // 去除样式 $('img, div, span, p, table, td, tr, a').removeAttr('style'); - $('style, script').remove(); + $('style').remove(); const title = $('h2').text(); + const dateMatch = $('div.ny_fbt') + .text() + .match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2})/); + const single = { title, description: $(current.selector.content).html() + ($('ul[style]').length ? $('ul[style]').html() : ''), link: $url, - pubDate: timezone( - parseDate( - $('div.ny_fbt') - .text() - .match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2})/)[0], - 'YYYY-MM-DD HH:mm' - ), - 8 - ), // 混有发表时间和点击量,取出时间 + pubDate: dateMatch ? timezone(parseDate(dateMatch[0], 'YYYY-MM-DD HH:mm'), 8) : undefined, // 混有发表时间和点击量,取出时间 author: '深圳大学研究生招生网', }; // 返回列表上提取到的信息 diff --git a/lib/routes/taobao/mysql.ts b/lib/routes/taobao/mysql.ts index e031a070df71..08e8cadd2e3d 100644 --- a/lib/routes/taobao/mysql.ts +++ b/lib/routes/taobao/mysql.ts @@ -79,7 +79,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $$: CheerioAPI = load(detailResponse); const title: string = $$('h2').first().text()?.trim() || item.title; - const description: string | undefined = $$('div.content').html() ?? undefined; + const description = $$('div.content').html(); const pubDateStr: string | undefined = item.link.split(/monthly\//).pop(); const authorEls: Element[] = $$('div.block p').toArray(); const authors: DataItem['author'] = authorEls.map((authorEl) => { diff --git a/lib/routes/techpowerup/review.ts b/lib/routes/techpowerup/review.ts index 870523559d4f..150a926f1bc3 100644 --- a/lib/routes/techpowerup/review.ts +++ b/lib/routes/techpowerup/review.ts @@ -60,14 +60,12 @@ async function handler(ctx) { .find('.author') .contents() .filter((_, c) => c.type === 'text') - .text() - .trim(), + .text(), category: $item .find('.category') .contents() .filter((_, c) => c.type === 'text') - .text() - .trim(), + .text(), }; }); diff --git a/lib/routes/the/index.ts b/lib/routes/the/index.ts index 5daaf445d9b7..5b06a0fe332c 100644 --- a/lib/routes/the/index.ts +++ b/lib/routes/the/index.ts @@ -28,7 +28,7 @@ export const handler = async (ctx): Promise<Data> => { return { title: `${title} - 江河日下`, - description: $('meta[property="og:description"]').attr('content') || $('meta[name="description"]').attr('content') || '', + description: $('meta[property="og:description"]').attr('content') || $('meta[name="description"]').attr('content'), link: targetUrl, allowEmpty: true, image: $('meta[property="og:image"]').attr('content') || '', @@ -99,14 +99,14 @@ function findArticleLink($: CheerioAPI, block: Cheerio<Element>): string | undef function extractFromMaterialBlocks($: CheerioAPI, pageUrl: string): DataItem[] { return $('[class^="material5"], [class^="material3"], [class^="material2"]') .toArray() - .map((el): DataItem | null => { + .map((el): DataItem => { const block = $(el); const title = block.find('h3').text().trim(); const summary = block.find('.ui-summary, h4.ui-summary, p.ui-summary').text().trim(); const dateStr = block.find('time').attr('datetime'); - const tag = block.find('.TagName').attr('title') || ''; + const tag = block.find('.TagName').attr('title'); const author = block.find('.postMetaInline--author').text().trim(); const image = block.find('img.graf-image--src').attr('data-srcset'); @@ -129,7 +129,7 @@ function extractFromMaterialBlocks($: CheerioAPI, pageUrl: string): DataItem[] { author, guid, }; - }) as DataItem[]; + }); } /** @@ -140,7 +140,7 @@ function extractFromMaterialBlocks($: CheerioAPI, pageUrl: string): DataItem[] { function extractFromPostPreviews($: CheerioAPI, pageUrl: string): DataItem[] { return $('.streamItem--postPreview') .toArray() - .map((el): DataItem | null => { + .map((el): DataItem => { const block = $(el); const title = block.find('h3.graf--title').text().trim(); @@ -148,7 +148,7 @@ function extractFromPostPreviews($: CheerioAPI, pageUrl: string): DataItem[] { const summary = block.find('h4.ui-summary, .graf--subtitle').text().trim(); const dateStr = block.find('time').attr('datetime'); const author = block.find('.postMetaInline--author').text().trim(); - const tag = block.find('.TagName').attr('title') || ''; + const tag = block.find('.TagName').attr('title'); const image = block.find('img.graf-image--src').not('.avatar-image img').attr('data-srcset'); // Reason: these blocks typically have #Unauthorized links; use page URL as fallback @@ -170,7 +170,7 @@ function extractFromPostPreviews($: CheerioAPI, pageUrl: string): DataItem[] { author, guid, }; - }) as DataItem[]; + }); } export const route: Route = { diff --git a/lib/routes/thegradient/index.ts b/lib/routes/thegradient/index.ts index 9eece5aaf837..806f0104d55c 100644 --- a/lib/routes/thegradient/index.ts +++ b/lib/routes/thegradient/index.ts @@ -32,7 +32,7 @@ async function handler() { .toArray() .map((item) => { const $item = $(item); - const $link = $item.find('.c-post-card__title-link').first(); + const $link = $item.find('.c-post-card__title-link'); const $meta = $item.find('.c-post-card__meta'); const href = $link.attr('href'); @@ -62,7 +62,7 @@ async function handler() { const detailResponse = await got(item.link); const $detail = load(detailResponse.data); - item.description = $detail('.c-content').html() || ''; + item.description = $detail('.c-content').html(); return item as DataItem; } catch { diff --git a/lib/routes/thepaper/839studio/category.ts b/lib/routes/thepaper/839studio/category.ts index d14b46f8e6d5..f58ec4409121 100644 --- a/lib/routes/thepaper/839studio/category.ts +++ b/lib/routes/thepaper/839studio/category.ts @@ -42,7 +42,7 @@ async function handler(ctx) { item: list.toArray().map((item) => { item = $(item); return { - title: item.find('.archive_up a').first().text(), + title: item.find('.archive_up a').text(), description: `描述:${item.find('.imgdown p').text()}`, link: item.find('.archive_up a').attr('href'), }; diff --git a/lib/routes/thepaper/839studio/studio.ts b/lib/routes/thepaper/839studio/studio.ts index 81c86ecdd676..76500f97868d 100644 --- a/lib/routes/thepaper/839studio/studio.ts +++ b/lib/routes/thepaper/839studio/studio.ts @@ -29,7 +29,7 @@ async function handler() { item: list.toArray().map((item) => { item = $(item); return { - title: item.find('.imgup a').first().text(), + title: item.find('.imgup a').text(), description: `描述:${item.find('.imgdown p').text()}`, link: item.find('.imgup a').attr('href'), }; diff --git a/lib/routes/theverge/index.ts b/lib/routes/theverge/index.ts index c2201c43aa82..bcb6abebf092 100644 --- a/lib/routes/theverge/index.ts +++ b/lib/routes/theverge/index.ts @@ -9,7 +9,7 @@ import { renderHeader } from './templates/header'; const excludeTypes = new Set(['NewsletterBlockType', 'RelatedPostsBlockType', 'ProductsTableBlockType', 'TableOfContentsBlockType']); -const shouldKeep = (b: any) => !excludeTypes.has(b.__typename.trim()); +const shouldKeep = (b: any) => !excludeTypes.has(b.__typename); export const route: Route = { path: '/:hub?', diff --git a/lib/routes/thinkingmachines/news.ts b/lib/routes/thinkingmachines/news.ts index f9b7ae6f2eee..ab5674023205 100644 --- a/lib/routes/thinkingmachines/news.ts +++ b/lib/routes/thinkingmachines/news.ts @@ -37,8 +37,8 @@ async function handler() { .toArray() .map((el) => { const $el = $(el); - const title = $el.find('.post-title').text().trim(); - const dateStr = $el.find('time.desktop-time').text().trim(); + const title = $el.find('.post-title').text(); + const dateStr = $el.find('time.desktop-time').text(); const href = $el.attr('href') || ''; const link = href.startsWith('http') ? href : `${baseUrl}${href}`; diff --git a/lib/routes/thoughtworks/index.ts b/lib/routes/thoughtworks/index.ts index 1df1b2d76d10..d3aa181a0758 100644 --- a/lib/routes/thoughtworks/index.ts +++ b/lib/routes/thoughtworks/index.ts @@ -21,7 +21,6 @@ async function handler() { headers: { 'content-type': 'application/json', origin: 'https://www.thoughtworks.com', - referer: 'https://www.thoughtworks.com/', }, }); diff --git a/lib/routes/thzt/index.ts b/lib/routes/thzt/index.ts index 8102e6ef7ad6..4df503965f27 100644 --- a/lib/routes/thzt/index.ts +++ b/lib/routes/thzt/index.ts @@ -34,7 +34,7 @@ async function handler() { const link = element.attr('href') || ''; const span = element.find('span').first(); - const title = span.text() || ''; + const title = span.text(); return { title, @@ -56,7 +56,7 @@ async function handler() { item.author = author; item.pubDate = parseDate(articlePubDate); - item.description = $('div.post-body').first().html() || ''; + item.description = $('div.post-body').html(); item.category = [$('span.post-category>span>a>span').first().text()]; return item; diff --git a/lib/routes/tidb/blog.ts b/lib/routes/tidb/blog.ts index c7beb35f22f9..c143c1ddfb82 100644 --- a/lib/routes/tidb/blog.ts +++ b/lib/routes/tidb/blog.ts @@ -142,7 +142,7 @@ export const handler = async (ctx: Context): Promise<Data> => { }); const title: string = detailResponse.title; - const description: string | undefined = detailResponse.content ? parseContentToHtml(JSON.parse(detailResponse.content)) : item.description; + const description: string | null | undefined = detailResponse.content ? parseContentToHtml(JSON.parse(detailResponse.content)) : item.description; const pubDate: number | string = detailResponse.publishedAt; const linkUrl: string | undefined = `blog/${detailResponse.slug}`; const categories: string[] = [...new Set([detailResponse.category?.name, ...(detailResponse.tags ?? []).map((c) => c.name)].filter(Boolean))]; diff --git a/lib/routes/tjbwg/exhibition.tsx b/lib/routes/tjbwg/exhibition.tsx index b26c66614382..8a027cf1bbd0 100644 --- a/lib/routes/tjbwg/exhibition.tsx +++ b/lib/routes/tjbwg/exhibition.tsx @@ -67,8 +67,7 @@ export const route: Route = { const href = a.attr('href') ?? ''; const link = new URL(href, `${baseUrl}/cn/`).href; const title = a.find('.text h3').text(); - const rawImgSrc = a.find('.img img').attr('src') ?? ''; - const imgUrl = new URL(rawImgSrc, baseUrl).href ?? ''; + const imgUrl = a.find('.img img').attr('src') ?? ''; const location = a .find('p') .first() diff --git a/lib/routes/tju/cic/index.ts b/lib/routes/tju/cic/index.ts index 4c5f87516b90..3f7cc405344f 100644 --- a/lib/routes/tju/cic/index.ts +++ b/lib/routes/tju/cic/index.ts @@ -61,11 +61,7 @@ async function handler(ctx) { } let response = null; try { - response = await got(cic_base_url + path, { - headers: { - Referer: cic_base_url, - }, - }); + response = await got(cic_base_url + path); } catch { // ignore error handler // console.log(e); diff --git a/lib/routes/tju/yzb/index.ts b/lib/routes/tju/yzb/index.ts index 6bd7d6b8a3cc..536bd2f5f137 100644 --- a/lib/routes/tju/yzb/index.ts +++ b/lib/routes/tju/yzb/index.ts @@ -70,9 +70,6 @@ async function handler(ctx) { let response = null; try { response = await got(yzb_base_url + path, { - headers: { - Referer: yzb_base_url, - }, responseType: 'buffer', }); } catch { diff --git a/lib/routes/tongji/sem/_utils.ts b/lib/routes/tongji/sem/_utils.ts index f1b84b3da414..426e256ccdf5 100644 --- a/lib/routes/tongji/sem/_utils.ts +++ b/lib/routes/tongji/sem/_utils.ts @@ -25,7 +25,7 @@ export async function getNotifByPage(url): Promise<Array<{ title: string; link: const title = aTagFirst.attr('title'); const href = aTagFirst.attr('href'); - const time = aTagSecond.text().trim(); + const time = aTagSecond.text(); return { title, diff --git a/lib/routes/tongji/sse/_article.ts b/lib/routes/tongji/sse/_article.ts index d976a79f6ef7..4d0bf4effbf2 100644 --- a/lib/routes/tongji/sse/_article.ts +++ b/lib/routes/tongji/sse/_article.ts @@ -12,12 +12,6 @@ export default async function getArticle(item) { const $ = load(data); const title = $('div.view-title').text(); const content = $('#vsb_content').html(); - $('[name="_newscontent_fromname"] ul a').each((_, e) => { - const href = $(e).attr('href'); - if (href.startsWith('/')) { - $(e).attr('href', new URL(href, item.link).href); - } - }); item.title = title; item.description = content + ($('ul[style]').length ? $('ul[style]').html() : ''); diff --git a/lib/routes/tophub/index.ts b/lib/routes/tophub/index.ts index c25b0d359516..14b29086b816 100644 --- a/lib/routes/tophub/index.ts +++ b/lib/routes/tophub/index.ts @@ -39,7 +39,6 @@ async function handler(ctx) { const link = `https://tophub.today/n/${id}`; const response = await ofetch(link, { headers: { - Referer: 'https://tophub.today', Cookie: config.tophub?.cookie ?? '', }, }); diff --git a/lib/routes/tophub/list.tsx b/lib/routes/tophub/list.tsx index 8d6975e05d34..f1eca06dd331 100644 --- a/lib/routes/tophub/list.tsx +++ b/lib/routes/tophub/list.tsx @@ -45,7 +45,6 @@ async function handler(ctx) { const link = `https://tophub.today/n/${id}`; const response = await ofetch(link, { headers: { - Referer: 'https://tophub.today', Cookie: config.tophub?.cookie ?? '', }, }); diff --git a/lib/routes/toranoana/news.ts b/lib/routes/toranoana/news.ts index 4c27a435f1d9..799ffae31900 100644 --- a/lib/routes/toranoana/news.ts +++ b/lib/routes/toranoana/news.ts @@ -78,7 +78,7 @@ async function handler(ctx): Promise<Data> { const $ = load(post.content.rendered); // remove unnecessary title - $('h1').first().remove(); + $('h1').remove(); $('h2').first().remove(); let thumbnail = ''; diff --git a/lib/routes/trendforce/news-cn.ts b/lib/routes/trendforce/news-cn.ts index b10979cea88f..7f4061b7f447 100644 --- a/lib/routes/trendforce/news-cn.ts +++ b/lib/routes/trendforce/news-cn.ts @@ -33,10 +33,10 @@ async function handler() { const $item = $(item); const a = $item.find('h3 a.title-link'); return { - title: a.find('strong').text().trim(), + title: a.find('strong').text(), link: new URL(a.attr('href')!, baseUrl).href, description: $item.find('p').text()?.trim(), - pubDate: parseDate($item.find('h4').text().trim()), + pubDate: parseDate($item.find('h4').text()), }; }); @@ -54,8 +54,8 @@ async function handler() { .parent() .find('a') .toArray() - .map((a) => $(a).text().trim()); - item.author = tagRow.find('.fa-user').parent().find('a').text().trim(); + .map((a) => $(a).text()); + item.author = tagRow.find('.fa-user').parent().find('a').text(); pressCenter.find('h1, .tag-row, .press-choose-post, hr').remove(); @@ -67,7 +67,7 @@ async function handler() { ); return { - title: $('head title').text().trim(), + title: $('head title').text(), description: $('meta[name="description"]').attr('content'), link, language: $('html').attr('lang'), diff --git a/lib/routes/tsinghua/lib/tzgg.ts b/lib/routes/tsinghua/lib/tzgg.ts index b78f63a78423..57c7f4a04077 100644 --- a/lib/routes/tsinghua/lib/tzgg.ts +++ b/lib/routes/tsinghua/lib/tzgg.ts @@ -39,9 +39,9 @@ async function handler(ctx) { .toArray() .map((item) => { item = $(item); - const title = item.find('a').first().text(); - const time = item.find('.notice-date').first().text(); - const a = item.find('a').first().attr('href'); + const title = item.find('a').text(); + const time = item.find('.notice-date').text(); + const a = item.find('a').attr('href'); const fullUrl = new URL(a, host).href; @@ -58,7 +58,7 @@ async function handler(ctx) { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.v_news_content').first().html(); + item.description = $('.v_news_content').html(); return item; }) diff --git a/lib/routes/udn/global/index.ts b/lib/routes/udn/global/index.ts index cdf67185e61c..09798ea1909e 100644 --- a/lib/routes/udn/global/index.ts +++ b/lib/routes/udn/global/index.ts @@ -48,15 +48,15 @@ async function handler(ctx) { const categoriesConf = { hot: { articleSelector: '.carousel__list .carousel__item', - titleExtractor: (e) => e.attr('title').trim(), + titleExtractor: (e) => e.attr('title'), }, editor: { articleSelector: '.list-container--featured .list-vertical__item', - titleExtractor: (e) => e.find('.list-vertical__title').text().trim(), + titleExtractor: (e) => e.find('.list-vertical__title').text(), }, default: { articleSelector: '.list-container--index .list-vertical__item', - titleExtractor: (e) => e.find('.list-vertical__title').text().trim(), + titleExtractor: (e) => e.find('.list-vertical__title').text(), }, }; const getItems = (config) => @@ -93,7 +93,7 @@ async function handler(ctx) { const content = load(detailResponse.data); - item.author = content('.article-content__authors-name').first().text().trim(); + item.author = content('.article-content__authors .article-content__authors-name').text(); item.pubDate = timezone(parseDate(content('meta[property="article:published_time"]').attr('content')), 8); const mainImage = content('.article-content__focus').html(); diff --git a/lib/routes/uestc/gr.ts b/lib/routes/uestc/gr.ts index 4aae2298f853..2d0a34723967 100644 --- a/lib/routes/uestc/gr.ts +++ b/lib/routes/uestc/gr.ts @@ -71,7 +71,7 @@ async function handler(ctx: Context): Promise<Data> { const items = entries.map(async (entry) => { const element = $(entry); - const newsTitle = element.find('a').text() ?? ''; + const newsTitle = element.find('a').text(); const newsLink = detailUrl + element.find('a').attr('href'); const newsDetail = await cache.tryGet(newsLink, async () => { diff --git a/lib/routes/uestc/scse.ts b/lib/routes/uestc/scse.ts index ef65aaa03ead..aabc632f2e03 100644 --- a/lib/routes/uestc/scse.ts +++ b/lib/routes/uestc/scse.ts @@ -87,8 +87,7 @@ async function handler() { .find('a[href]') .contents() .filter((index, element) => element.nodeType === 3) - .text() - .trim(); + .text(); const newsLink = host + item.find('a[href]').attr('href'); const newsPubDate = parseDate(date); diff --git a/lib/routes/unipd/ilbolive/news.ts b/lib/routes/unipd/ilbolive/news.ts index dfbc1ad1047c..14810f9432e1 100644 --- a/lib/routes/unipd/ilbolive/news.ts +++ b/lib/routes/unipd/ilbolive/news.ts @@ -71,24 +71,20 @@ async function handler() { // Picture article.find('img').each((_, el) => { const img = $(el); - const src = img.attr('src'); - if (src && src.startsWith('/')) { - img.attr('src', baseUrl + src); - } img.attr('style', 'max-width: 100%; height: auto;'); }); const datetime = article.find('time.date').attr('datetime'); const pubDate = datetime ? timezone(parseDate(datetime), 0) : undefined; - const author = article.find('.author a').text().trim(); + const author = article.find('.author a').text(); // Delete header article.find('.header').remove(); return { ...item, - description: article.html() ?? '', + description: article.html(), pubDate, author, }; diff --git a/lib/routes/usenix/usenix.ts b/lib/routes/usenix/usenix.ts index e994b8a4b9d5..1f0011890e26 100644 --- a/lib/routes/usenix/usenix.ts +++ b/lib/routes/usenix/usenix.ts @@ -52,7 +52,7 @@ async function handler() { return { title: item.find('h2.node-title > a').text().trim(), link: `${url}${item.find('h2.node-title > a').attr('href')}`, - author: item.find('div.field.field-name-field-paper-people-text.field-type-text-long.field-label-hidden p').text().trim(), + author: item.find('div.field.field-name-field-paper-people-text.field-type-text-long.field-label-hidden p').text(), pubDate, }; }); diff --git a/lib/routes/ustb/yjsy/news.ts b/lib/routes/ustb/yjsy/news.ts index 93496d5f9f15..8ef011d04298 100644 --- a/lib/routes/ustb/yjsy/news.ts +++ b/lib/routes/ustb/yjsy/news.ts @@ -456,7 +456,11 @@ async function handler(ctx) { // logger.info("link:" + link); // title - let title = item.find($(struct[type].titleSelector.list)).clone().children().remove().end().text(); + let title = item + .find($(struct[type].titleSelector.list)) + .contents() + .filter((_, node) => node.type === 'text') + .text(); if (title === '') { title = item.find($(struct[type].titleSelector.list)).text(); } diff --git a/lib/routes/ustc/gs.ts b/lib/routes/ustc/gs.ts index b96cbbf63f63..51d4e8480be3 100644 --- a/lib/routes/ustc/gs.ts +++ b/lib/routes/ustc/gs.ts @@ -57,7 +57,7 @@ async function handler(ctx) { .toArray() .map((item) => { item = $(item); - const title = item.find('a').text().trim(); + const title = item.find('a').text(); const link = item.find('a').attr('href').startsWith('/article') ? host + item.find('a').attr('href') : item.find('a').attr('href'); const pubDate = timezone(parseDate(item.find('time').text(), 'YYYY-MM-DD'), 8); return { diff --git a/lib/routes/ustc/math.ts b/lib/routes/ustc/math.ts index f92dc3de641a..e98f28f5c7f6 100644 --- a/lib/routes/ustc/math.ts +++ b/lib/routes/ustc/math.ts @@ -59,7 +59,7 @@ async function handler(ctx) { .toArray() .map((item) => { const elem = $(item); - const title = elem.find('.Article_Title > a').attr('title').trim(); + const title = elem.find('.Article_Title > a').attr('title'); let link = elem.find('.Article_Title > a').attr('href'); link = link.startsWith('/') ? host + link : link; // Assume that the articles are published at 12:00 UTC+8 diff --git a/lib/routes/ustc/sist.ts b/lib/routes/ustc/sist.ts index 759ce91d19fc..a7db1e581e85 100644 --- a/lib/routes/ustc/sist.ts +++ b/lib/routes/ustc/sist.ts @@ -57,7 +57,7 @@ async function handler(ctx) { .toArray() .map((item) => { item = $(item); - const title = item.find('.card-title > a').attr('title').trim(); + const title = item.find('.card-title > a').attr('title'); let link = item.find('.card-title > a').attr('href'); link = link.startsWith('/') ? host + link : link; const pubDate = timezone(parseDate(item.find('time').text().replace('发布时间:', ''), 'YYYY-MM-DD'), 8); diff --git a/lib/routes/verfghbw/press.ts b/lib/routes/verfghbw/press.ts index 3e68e5f02142..f69c8b0e9420 100644 --- a/lib/routes/verfghbw/press.ts +++ b/lib/routes/verfghbw/press.ts @@ -1,14 +1,17 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +const rootUrl = 'https://verfgh.baden-wuerttemberg.de'; +const listUrl = `${rootUrl}/presse-und-service/pressemitteilungen/`; + export const route: Route = { - path: '/press/:keyword?', + path: '/press', categories: ['government'], example: '/verfghbw/press', - parameters: { keyword: 'Keyword' }, features: { requireConfig: false, requirePuppeteer: false, @@ -19,67 +22,50 @@ export const route: Route = { }, radar: [ { - source: ['verfgh.baden-wuerttemberg.de/de/presse-und-service/pressemitteilungen/'], + source: ['verfgh.baden-wuerttemberg.de/presse-und-service/pressemitteilungen/'], target: '/press', }, ], name: 'Press releases', maintainers: ['quinn-dev'], handler, - url: 'verfgh.baden-wuerttemberg.de/de/presse-und-service/pressemitteilungen/', + url: 'verfgh.baden-wuerttemberg.de/presse-und-service/pressemitteilungen/', }; -async function handler(ctx) { - const keyword = ctx.req.param('keyword'); - const rootUrl = 'https://verfgh.baden-wuerttemberg.de'; - - let request = { - url: `${rootUrl}/de/presse-und-service/pressemitteilungen/`, - headers: { - Referer: `${rootUrl}/de/presse-und-service/pressemitteilungen/`, - }, - }; - - request = keyword - ? { - method: 'post', - form: { - 'tx_bwlistheader_list[search][keywords]': keyword, - }, - ...request, - } - : { - method: 'get', - ...request, - }; +async function handler() { + const response = await got(listUrl); + const $ = load(response.data); - const response = await got(request); - - const data = response.data; - const $ = load(data); - - const list = $('.pressListItem') + const list = $('.news-singel') .toArray() .map((item) => { - item = $(item); - - const title = item.find('.pressListItemTeaser > h3').text().trim(); - const link = rootUrl + '/' + item.find('.link-download').attr('href'); - item.find('.pressListItemTeaser > h3').replaceWith((_, e) => `<p>${$(e).html()}</p>`); - item.find('a').each((_, e) => $(e).attr('href', rootUrl + '/' + $(e).attr('href'))); + const $item = $(item); return { - title, - link, - description: item.find('.pressListItemTeaser').html(), - pubDate: parseDate(item.find('.pressListItemDate > span').text(), 'DD.MM.YYYY'), + title: $item.find('.news-header').text(), + link: new URL($item.attr('href')!, rootUrl).href, }; }); + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link, async () => { + const detail = await got(item.link); + const $$ = load(detail.data); + + return { + ...item, + description: $$('.news-text-wrap').html(), + pubDate: parseDate($$('.news-single time').attr('datetime')!), + }; + }) + ) + ); + return { title: 'Verfassungsgerichtshof Baden-Württemberg - Pressemitteilungen', - link: request.url, + link: listUrl, description: 'Pressemitteilungen des Verfassungsgerichtshof für das Land Baden-Württemberg', - item: list, + item: items, }; } diff --git a/lib/routes/visionias/daily-news-summary.ts b/lib/routes/visionias/daily-news-summary.ts index 6f749e25d833..c3a701102903 100644 --- a/lib/routes/visionias/daily-news-summary.ts +++ b/lib/routes/visionias/daily-news-summary.ts @@ -52,7 +52,7 @@ function processNews(page) { .toArray() .map((item) => { const title = $(item).find('a>h5').text().trim(); - const content = $(item).find('a>div').html() ?? ''; + const content = $(item).find('a>div').html(); const link = $(item).find('div>p>a').attr('href') || ''; return { title, diff --git a/lib/routes/visionias/news-today.ts b/lib/routes/visionias/news-today.ts index 41a3f667cc9e..15ab3725979b 100644 --- a/lib/routes/visionias/news-today.ts +++ b/lib/routes/visionias/news-today.ts @@ -79,7 +79,7 @@ async function processCurrentNews(currentUrl) { .toArray() .map((item) => { const link = $(item).attr('href'); - const title = $(item).clone().children('span').remove().end().text().trim(); + const title = $(item).children('span').remove().end().text().trim(); return { title, link: title === 'Also in News' ? link : `${baseUrl}${link}`, diff --git a/lib/routes/visualstudio/code-blog.ts b/lib/routes/visualstudio/code-blog.ts index 0d7a4f6a2233..3f7e678c18f9 100644 --- a/lib/routes/visualstudio/code-blog.ts +++ b/lib/routes/visualstudio/code-blog.ts @@ -43,7 +43,7 @@ async function handler() { const $ = load(data); // remove title and time - $('main h1').first().remove(); + $('main h1').remove(); $('main p').first().remove(); item.content = $('main').html() as string; diff --git a/lib/routes/warp/blog.ts b/lib/routes/warp/blog.ts index 15e5002f2e5b..0111c16314b3 100644 --- a/lib/routes/warp/blog.ts +++ b/lib/routes/warp/blog.ts @@ -34,7 +34,7 @@ export const route: Route = { }; async function handler() { - const feed = await parser.parseURL('https://www.warp.dev/blog/rss.xml'); + const feed = await parser.parseURL('https://www.warp.dev/blog/feed.xml'); const items = await Promise.all( feed.items.map((item) => @@ -50,7 +50,6 @@ async function handler() { main.find('[class]').removeAttr('class'); main.find('[id]').removeAttr('id'); main.find('[preload]').removeAttr('preload'); - main.find('script').remove(); main.find('figcaption').remove(); // remove title, time and button diff --git a/lib/routes/wechat/sogou.ts b/lib/routes/wechat/sogou.ts index 211d8943b056..ea01b3ffb841 100644 --- a/lib/routes/wechat/sogou.ts +++ b/lib/routes/wechat/sogou.ts @@ -30,7 +30,6 @@ async function fetchAndParsePage(wechatId: string): Promise<SogouItemInternal[]> page: '1', }, headers: { - Referer: host, Cookie: hardcodedCookie, }, }); diff --git a/lib/routes/weibo/oasis/user.ts b/lib/routes/weibo/oasis/user.ts index 44f7b110df56..078c3346b932 100644 --- a/lib/routes/weibo/oasis/user.ts +++ b/lib/routes/weibo/oasis/user.ts @@ -38,8 +38,8 @@ async function handler(ctx) { description: `$('.desc').text().trim()`, item: { item: '.container .status-item', - title: `$('.status-item-title').clone().children().remove().end().text()`, - description: `$('.status-item-title').clone().children().remove().end().text() + '<br>' + $('.status-img').html()`, + title: `$('.status-item-title').contents().filter((_, node) => node.type === 'text').text()`, + description: `$('.status-item-title').contents().filter((_, node) => node.type === 'text').text() + '<br>' + $('.status-img').html()`, link: `'https://oasis.weibo.cn/v1/h5/share?sid=' + $('.status-item-title').parent().data('id')`, }, }) diff --git a/lib/routes/wfu/news.ts b/lib/routes/wfu/news.ts index 7fe7e61317e1..601d012ceb54 100644 --- a/lib/routes/wfu/news.ts +++ b/lib/routes/wfu/news.ts @@ -84,9 +84,6 @@ async function handler(ctx) { const response = await got({ method: 'get', url: listPageUrl, - headers: { - Referer: baseUrl, - }, }); const $ = load(response.data); diff --git a/lib/routes/whu/cs.ts b/lib/routes/whu/cs.ts index ece68e074698..b7901647b0ba 100644 --- a/lib/routes/whu/cs.ts +++ b/lib/routes/whu/cs.ts @@ -111,7 +111,7 @@ async function handler(ctx) { items = items.filter((item) => item !== null); return { - title: $('title').first().text(), + title: $('title').text(), link, item: items, }; diff --git a/lib/routes/whu/rsgis.ts b/lib/routes/whu/rsgis.ts index c9ce6eb71255..247a866c5bcc 100644 --- a/lib/routes/whu/rsgis.ts +++ b/lib/routes/whu/rsgis.ts @@ -105,7 +105,7 @@ function checkExternal(link: string): boolean { * @returns A list of RSS meta node. */ function parseListLinkDateItem(element: Cheerio<Element>, currentUrl: string) { - const linkElement = element.find('a').first(); + const linkElement = element.find('a'); const title = linkElement.text(); const href = linkElement.attr('href'); if (href === undefined) { @@ -113,7 +113,7 @@ function parseListLinkDateItem(element: Cheerio<Element>, currentUrl: string) { } const external = checkExternal(href); const link = external ? href : new URL(href, currentUrl).href; - const pubDate = element.find('div.date1').first().text(); + const pubDate = element.find('div.date1').text(); return { title, link, @@ -132,8 +132,8 @@ async function getDetail(item: Post): Promise<DataItem | any> { } else { const response = await ofetch(link); const $ = load(response); - const title = $('div.content div.content_title h1').first().text(); - const content = $('div.content div.v_news_content').first().html(); + const title = $('div.content div.content_title h1').text(); + const content = $('div.content div.v_news_content').html(); item.title = title; item.description = content || ''; } diff --git a/lib/routes/whu/swrh.ts b/lib/routes/whu/swrh.ts index 8946615f0a1f..81a31260d3e6 100644 --- a/lib/routes/whu/swrh.ts +++ b/lib/routes/whu/swrh.ts @@ -69,7 +69,7 @@ async function handler(ctx) { item = $(item); return { title: item.find('a b.am-text-truncate').text().trim(), - pubDate: item.find('a i').text().trim(), + pubDate: item.find('a i').text(), link: new URL(item.find('a').attr('href'), baseUrl).href, }; }) @@ -79,7 +79,7 @@ async function handler(ctx) { item = $(item); return { title: item.find('a span').text().trim(), - pubDate: item.find('a i').text().trim(), + pubDate: item.find('a i').text(), link: new URL(item.find('a').attr('href'), baseUrl).href, }; }); @@ -109,7 +109,7 @@ async function handler(ctx) { items = items.filter((item) => item !== null); return { - title: $('title').first().text(), + title: $('title').text(), link, item: items, }; diff --git a/lib/routes/wiensued/index.ts b/lib/routes/wiensued/index.ts index c0e35b20af9f..c29b25015d57 100644 --- a/lib/routes/wiensued/index.ts +++ b/lib/routes/wiensued/index.ts @@ -45,8 +45,8 @@ leading up to the listing (e.g. \`wohnen/sofort-verfuegbar\`)`, const $image = $el.find('.image img'); const link = $el.find('.link a').attr('href'); const image = $image.attr('data-lazy-src') || $image.attr('src'); - const title = $el.find('.address h4').first().text().trim(); - const subtitle = $el.find('.address p').first().text().trim(); + const title = $el.find('.address h4').first().text(); + const subtitle = $el.find('.address p').first().text(); const $text = $el.find('.text'); const description = $text .find('.labtxtline') @@ -55,7 +55,7 @@ leading up to the listing (e.g. \`wohnen/sofort-verfuegbar\`)`, $(el) .children() .toArray() - .map((c) => $(c).text().trim()) + .map((c) => $(c).text()) .join(': ') ) .join(', '); diff --git a/lib/routes/windsurf/changelog.ts b/lib/routes/windsurf/changelog.ts index ac605f89b2a2..9fc6942693b6 100644 --- a/lib/routes/windsurf/changelog.ts +++ b/lib/routes/windsurf/changelog.ts @@ -18,7 +18,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $: CheerioAPI = load(response); const language = $('html').attr('lang') ?? 'en'; - const title: string = $('title').first().text(); + const title: string = $('title').text(); const author: string | undefined = title.split(/\|/).pop()?.trim(); const items: DataItem[] = $('div[aria-label="changelog-layout"]') @@ -31,7 +31,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const h1: string | undefined = $el.find('article h1').text()?.trim(); const title: string = [version, h1].filter(Boolean).join(' '); - const description: string | undefined = $el.find('article div').first()?.html() ?? undefined; + const description = $el.find('article div').first()?.html(); const pubDateStr: string | undefined = $el.find('header div').last().text()?.trim(); const guid: string = version ? `windsurf-${version}` : ''; const image: string | undefined = $el.find('article img').attr('src'); diff --git a/lib/routes/wohnnet/index.ts b/lib/routes/wohnnet/index.ts index 9cbed1877886..f591860f3fe1 100644 --- a/lib/routes/wohnnet/index.ts +++ b/lib/routes/wohnnet/index.ts @@ -98,7 +98,7 @@ Examples: const badges = $el .find('.realty-detail-badges .badge') .toArray() - .map((b) => $(b).text().trim()); + .map((b) => $(b).text()); const agency = $el.find('.realty-detail-agency').text(); const imgSrc = $el.find('.realty-image img').attr('src'); diff --git a/lib/routes/wufazhuce/one.ts b/lib/routes/wufazhuce/one.ts index 3b78b83dbc80..29b8368d17ae 100644 --- a/lib/routes/wufazhuce/one.ts +++ b/lib/routes/wufazhuce/one.ts @@ -37,7 +37,7 @@ async function handler(): Promise<Data> { ...$('#carousel-one div.item') .toArray() .map((item) => { - const a = $(item).find('.fp-one-cita a').first(); + const a = $(item).find('.fp-one-cita a'); return { title: a.text(), link: a.attr('href'), @@ -75,7 +75,7 @@ async function handler(): Promise<Data> { cache.tryGet(item.link, async () => { const rsp = await got(item.link); const content = load(rsp.body); - item.description = content('.tab-content').html() || ''; + item.description = content('.tab-content').html(); return item; }) ) diff --git a/lib/routes/xaut/index.ts b/lib/routes/xaut/index.ts index 92a87662fb0b..6182d2a5f9ce 100644 --- a/lib/routes/xaut/index.ts +++ b/lib/routes/xaut/index.ts @@ -50,7 +50,7 @@ async function handler(ctx) { item = $(item); // link原来长这样:'../info/1196/13990.htm' const link = item.find('a').attr('href').replace(/^\.\./, 'http://www.xaut.edu.cn'); - const pubDate = timezone(parseDate(item.find('div.time').text().trim()), 8); + const pubDate = timezone(parseDate(item.find('div.time').text()), 8); const title = item.find('h5').text(); return { diff --git a/lib/routes/xbmu/academic.ts b/lib/routes/xbmu/academic.ts index d824e55b44e1..28732ea83013 100644 --- a/lib/routes/xbmu/academic.ts +++ b/lib/routes/xbmu/academic.ts @@ -20,7 +20,7 @@ const handler: Route['handler'] = async () => { // Map through each list item to extract details const academicLinkList = await Promise.all( listItems.toArray().map((element) => { - const rawDate = $(element).find('span').text().trim(); + const rawDate = $(element).find('span').text(); const [day, yearMonth] = rawDate.split('/').map((s) => s.trim()); const formattedDate = parseDate(`${yearMonth}-${day}`).toUTCString(); diff --git a/lib/routes/xbookcn/blog.ts b/lib/routes/xbookcn/blog.ts index 9b6f98c70f1c..16620fc969b8 100644 --- a/lib/routes/xbookcn/blog.ts +++ b/lib/routes/xbookcn/blog.ts @@ -31,7 +31,7 @@ export const route: Route = { const list = articles.toArray().map((elem) => { const a = $(elem).find('.post-title a'); // 获取标题链接 return { - title: a.text().trim(), // 标题 + title: a.text(), // 标题 link: a.attr('href'), // 链接 category: [], // 分类 }; @@ -51,7 +51,7 @@ export const route: Route = { // 获取分类信息 const categories = $('.post-labels a') .toArray() - .map((el) => $(el).text().trim()); + .map((el) => $(el).text()); item.category = categories; // 添加多个分类信息 return item; // 返回带有描述和分类的文章对象 diff --git a/lib/routes/xidian/cs.ts b/lib/routes/xidian/cs.ts index ddffd258e8e9..3d21c06097f0 100644 --- a/lib/routes/xidian/cs.ts +++ b/lib/routes/xidian/cs.ts @@ -95,11 +95,7 @@ export const route: Route = { async function handler(ctx) { const { category = 'xyxw' } = ctx.req.param(); const url = `${baseUrl}/${struct[category].path}.htm`; - const response = await got(url, { - headers: { - referer: baseUrl, - }, - }); + const response = await got(url); const $ = load(response.data); diff --git a/lib/routes/xidian/gr.ts b/lib/routes/xidian/gr.ts index 18503e50ad70..ed3ef2c57b2c 100644 --- a/lib/routes/xidian/gr.ts +++ b/lib/routes/xidian/gr.ts @@ -227,11 +227,7 @@ export const route: Route = { async function handler(ctx) { const { category = 'home_tzgg1' } = ctx.req.param(); const url = `${baseUrl}/${struct[category].path}.htm`; - const response = await got(url, { - headers: { - referer: baseUrl, - }, - }); + const response = await got(url); const $ = load(response.data); diff --git a/lib/routes/xidian/jwc.ts b/lib/routes/xidian/jwc.ts index fb10f8d57d0c..e634b62ee575 100644 --- a/lib/routes/xidian/jwc.ts +++ b/lib/routes/xidian/jwc.ts @@ -32,11 +32,7 @@ export const route: Route = { async function handler(ctx) { const { category = 'tzgg' } = ctx.req.param(); const url = `${baseUrl}/${category}.htm`; - const response = await got(url, { - headers: { - referer: baseUrl, - }, - }); + const response = await got(url); const $ = load(response.data); let items = $('.list ul li') diff --git a/lib/routes/xjtlu/news.ts b/lib/routes/xjtlu/news.ts index 8a60e8be9b79..14bc84be9bb3 100644 --- a/lib/routes/xjtlu/news.ts +++ b/lib/routes/xjtlu/news.ts @@ -97,7 +97,7 @@ const handler = async (ctx) => { const $article = load(articleResponse); const fullContent = $article('.post_content').html(); - const articleDate = $article('.edited-view .date, p.date').text().trim(); + const articleDate = $article('.edited-view .date, p.date').text(); // Parse date based on language // English: "21 Jan 2026" with 'en' locale for month name recognition diff --git a/lib/routes/xjtu/ee-jzxx.ts b/lib/routes/xjtu/ee-jzxx.ts index fb180ba3ba41..b6f9e9fe5cce 100644 --- a/lib/routes/xjtu/ee-jzxx.ts +++ b/lib/routes/xjtu/ee-jzxx.ts @@ -74,7 +74,6 @@ async function handler(ctx) { const pubDate = timezone(parseDate(dateText), 8); const description = content('div.art-body.wow.fadeInUp') - .clone() // 创建副本防止修改原始内容 .find('ul li') // 定位到附件列表项 .each((_, el) => { const $li = $(el); diff --git a/lib/routes/xmanhua/index.ts b/lib/routes/xmanhua/index.ts index 179bc9ff82a5..822d3f860993 100644 --- a/lib/routes/xmanhua/index.ts +++ b/lib/routes/xmanhua/index.ts @@ -35,9 +35,6 @@ async function handler(ctx) { const response = await got({ method: 'get', url, - headers: { - Referer: host, - }, }); const data = response.data; @@ -46,7 +43,10 @@ async function handler(ctx) { // 作者 const autherName = $('body > div.detail-info-1 > div > div > p.detail-info-tip > span:nth-child(1)').text().split(':', 2)[1]; // 检查漫画是否已经完结 - const finished_text = $('div.detail-list-form-title').clone().children().remove().end().text(); + const finished_text = $('div.detail-list-form-title') + .contents() + .filter((_, node) => node.type === 'text') + .text(); let finished = false; let newOneDate = finished_text.split(',', 2)[1]; if (newOneDate.includes('月') && newOneDate.includes('號')) { diff --git a/lib/routes/xmu/kydt.ts b/lib/routes/xmu/kydt.ts index c7ee2d8fcf7b..4306f21b9cb3 100644 --- a/lib/routes/xmu/kydt.ts +++ b/lib/routes/xmu/kydt.ts @@ -35,9 +35,9 @@ async function handler() { .toArray() .map((item) => { item = $(item); - const title = item.find('h4').first().text(); - const time = item.find('h6').first().text(); - const a = item.find('a').first().attr('href'); + const title = item.find('h4').text(); + const time = item.find('h6').text(); + const a = item.find('a').attr('href'); const fullUrl = new URL(a, host).href; return { @@ -53,7 +53,7 @@ async function handler() { const response = await ofetch(item.link); const $ = load(response); - item.description = $('.v_news_content').first().html(); + item.description = $('.v_news_content').html(); return item; }) diff --git a/lib/routes/xmut/jwc/bkjw.ts b/lib/routes/xmut/jwc/bkjw.ts index 5d95dd29b9dd..7e17f0a6468e 100644 --- a/lib/routes/xmut/jwc/bkjw.ts +++ b/lib/routes/xmut/jwc/bkjw.ts @@ -17,11 +17,7 @@ export const route: Route = { async function handler(ctx) { const { category = 'jwxt' } = ctx.req.param(); const url = `${xmut}/index/tzgg/${category}.htm`; - const res = await got(url, { - headers: { - referer: xmut, - }, - }); + const res = await got(url); const $ = load(res.data); const itemsArray = $('#result_list table tbody tr') .toArray() @@ -37,7 +33,7 @@ async function handler(ctx) { link = resLink; } const title = $('a', res).attr('title'); - const pubDate = parseDate(resDate.text().trim()); + const pubDate = parseDate(resDate.text()); return { title, link, diff --git a/lib/routes/xueqiu/hots.ts b/lib/routes/xueqiu/hots.ts index 77c8fd3bcb6e..0866c3f9d00d 100644 --- a/lib/routes/xueqiu/hots.ts +++ b/lib/routes/xueqiu/hots.ts @@ -45,7 +45,6 @@ async function handler() { }), headers: { Cookie: token, - Referer: 'https://xueqiu.com/', }, }); const data = res2.data; diff --git a/lib/routes/xueqiu/today.ts b/lib/routes/xueqiu/today.ts index fe57fac6bb24..4463c286a055 100644 --- a/lib/routes/xueqiu/today.ts +++ b/lib/routes/xueqiu/today.ts @@ -65,7 +65,6 @@ async function handler(ctx) { method: 'get', url: item.link, headers: { - Referer: rootUrl, Cookie: token, }, }); diff --git a/lib/routes/xyu/library.ts b/lib/routes/xyu/library.ts index 33dd88d5c267..1344e7ed561f 100644 --- a/lib/routes/xyu/library.ts +++ b/lib/routes/xyu/library.ts @@ -40,7 +40,7 @@ async function handler() { .map((item) => { const $item = $(item); const $link = $item.find('a'); - const title = $link.attr('title') || $link.text().trim(); + const title = $link.attr('title') || $link.text(); const relativeUrl = $link.attr('href'); const link = relativeUrl ? new URL(relativeUrl, baseUrl).href : ''; // 提取日期 diff --git a/lib/routes/xyu/notices.ts b/lib/routes/xyu/notices.ts index 5943391d78b5..20b01e439e0f 100644 --- a/lib/routes/xyu/notices.ts +++ b/lib/routes/xyu/notices.ts @@ -55,8 +55,8 @@ async function handler() { const title = currentItem.find('.list-tx h3').text().trim(); const description = currentItem.find('.list-tx p').text().trim(); - const day = currentItem.find('.date p').text().trim(); - const yearMonth = currentItem.find('.date span').text().trim(); + const day = currentItem.find('.date p').text(); + const yearMonth = currentItem.find('.date span').text(); const dateText = `${yearMonth}-${day.padStart(2, '0')}`; return { @@ -88,13 +88,6 @@ async function handler() { if (content) { const $content = load(content); - $content('a').each((_, el) => { - const a = $(el); - const href = a.attr('href'); - if (href && !href.startsWith('http')) { - a.attr('href', new URL(href, baseUrl).href); - } - }); item.description = $content.html(); } diff --git a/lib/routes/ynet/list.ts b/lib/routes/ynet/list.ts index 199a556e7543..62875ea302fc 100644 --- a/lib/routes/ynet/list.ts +++ b/lib/routes/ynet/list.ts @@ -98,7 +98,7 @@ export const handler = async (ctx: Context): Promise<Data> => { item.link = finalUrl; const title: string = $$('div.articleTitle h1').text(); - const description: string | undefined = $$('div#articleBox').html() ?? undefined; + const description = $$('div#articleBox').html(); const pubDateStr: string | undefined = $$('span.yearMsg').text() && $$('span.timeMsg').text() ? `${$$('span.yearMsg').text()} ${$$('span.timeMsg').text()}` : undefined; const authors: DataItem['author'] = $$('spna.sourceMsg').text(); const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/zaimanhua/comic.ts b/lib/routes/zaimanhua/comic.ts index 752525fab48e..0f9d92b07647 100644 --- a/lib/routes/zaimanhua/comic.ts +++ b/lib/routes/zaimanhua/comic.ts @@ -50,7 +50,6 @@ async function handler(ctx) { const headers: Record<string, string> = { 'user-agent': config.trueUA, - referer: baseUrl, }; const token = config.zaimanhua.token; diff --git a/lib/routes/zaimanhua/update.ts b/lib/routes/zaimanhua/update.ts index fd166a1f1108..987eed2ee539 100644 --- a/lib/routes/zaimanhua/update.ts +++ b/lib/routes/zaimanhua/update.ts @@ -43,7 +43,6 @@ export const route: Route = { const currentUrl = `${baseUrl}/api/v1/comic2/update_list?status&theme&zone&cate&firstLetter&sortType&page=1&size=20`; const headers: Record<string, string> = { 'user-agent': config.trueUA, - referer: baseUrl, }; const token = config.zaimanhua.token; if (token) { @@ -75,7 +74,7 @@ export const route: Route = { return { title: `[${item.status}] | ${item.name} - ${item.last_update_chapter_name}`, author: item.authors, - category: [item.status, ...item.types.split('/').map((type) => type.trim())], + category: [item.status, ...item.types.split('/')], image: item.cover, link: `${baseUrl}/view/${comicPy}/${comicId}/${lastUpdateChapterId}`, pubDate: parseDate(item.last_updatetime * 1000), diff --git a/lib/routes/zcmu/yxy/index.ts b/lib/routes/zcmu/yxy/index.ts index f65c61279529..084722a70a20 100644 --- a/lib/routes/zcmu/yxy/index.ts +++ b/lib/routes/zcmu/yxy/index.ts @@ -53,7 +53,7 @@ async function handler(ctx) { return { title: item.find('a').text(), link: `https://yxy.zcmu.edu.cn/${item.find('a').attr('href')}`, - pubDate: parseDate(item.find('span').text().trim()), + pubDate: parseDate(item.find('span').text()), }; }); diff --git a/lib/routes/zed/blog.ts b/lib/routes/zed/blog.ts index 30f3aa64ad4a..b10fd6a49d64 100644 --- a/lib/routes/zed/blog.ts +++ b/lib/routes/zed/blog.ts @@ -50,7 +50,6 @@ async function handler() { article.find('[class]').removeAttr('class'); article.find('[id]').removeAttr('id'); article.find('[preload]').removeAttr('preload'); - article.find('script').remove(); article.find('figcaption').remove(); article.find('aside').remove(); // remove Looking and hiring part diff --git a/lib/routes/zhizhuan100/report.ts b/lib/routes/zhizhuan100/report.ts index 26ea826f6770..8de002907c4a 100644 --- a/lib/routes/zhizhuan100/report.ts +++ b/lib/routes/zhizhuan100/report.ts @@ -54,8 +54,8 @@ async function handler() { const linkElement = $item.find('.w-list-link'); const imgElement = $item.find('.w-listpic-in'); - const title = titleElement.text().trim() || ''; - const dateText = dateElement.text().trim() || ''; + const title = titleElement.text() || ''; + const dateText = dateElement.text() || ''; const href = linkElement.attr('href') || ''; const imgSrc = imgElement.attr('src') || ''; diff --git a/lib/routes/zju/math/index.ts b/lib/routes/zju/math/index.ts index 9cc43bc85b6e..b682d7cc446e 100644 --- a/lib/routes/zju/math/index.ts +++ b/lib/routes/zju/math/index.ts @@ -35,13 +35,13 @@ async function fetchNewsItemsByCategory(categoryId: string): Promise<NewsItem[]> .toArray() .map((item): NewsItem | null => { const element = $(item); - const link = element.find('a[href]').first().attr('href'); + const link = element.find('a[href]').attr('href'); const titleNode = element.find('.title'); // if the title node contains an image with src "/_images/news/icon/unopen.gif", // it indicates that the news item is only accessible from the intranet const intranetOnly = titleNode.find('img[src="/_images/news/icon/unopen.gif"]').length > 0; - const title = titleNode.text().trim() || element.find('a[href]').first().attr('title'); - const dateText = `${element.find('.date .y').text().trim()}-${element.find('.date .d').text().trim()}`; + const title = titleNode.text() || element.find('a[href]').attr('title'); + const dateText = `${element.find('.date .y').text()}-${element.find('.date .d').text()}`; // If the title or link is missing, we skip this item as it is likely not a valid news entry. if (!(title && link)) { @@ -82,13 +82,8 @@ async function enrichNewsItemWithDetails(item: NewsItem, refererUrl: string): Pr const infoText = $('.item_info').text(); const [, author, pubDate] = infoText.match(/来源:([\s\S]*?)发布时间:(\d{4}-\d{2}-\d{2})/) ?? []; - if (description) { - dataItem.description = description; - } - - if (author) { - dataItem.author = author; - } + dataItem.description = description; + dataItem.author = author; if (pubDate) { dataItem.pubDate = timezone(parseDate(pubDate), 8); diff --git a/lib/routes/zju/sis/index.ts b/lib/routes/zju/sis/index.ts index 57cdf0e3cfc9..038d553e2e25 100644 --- a/lib/routes/zju/sis/index.ts +++ b/lib/routes/zju/sis/index.ts @@ -87,9 +87,7 @@ async function enrichNewsItemWithDetails(item: DataItem, refererUrl: string): Pr // Extract and set the full article content as description const description = $('.wp_articlecontent').html(); - if (description) { - item.description = description; - } + item.description = description; // Extract and clean the author information let author = $('.arti_metas').find('.arti_publisher').text(); diff --git a/lib/routes/zjut/cs/index.ts b/lib/routes/zjut/cs/index.ts index ae027a4330c8..cc2cc059624b 100644 --- a/lib/routes/zjut/cs/index.ts +++ b/lib/routes/zjut/cs/index.ts @@ -49,7 +49,7 @@ async function handler(ctx) { const a = cheerioItem.find('a'); try { - const title = a.text() || ''; + const title = a.text(); let link = a.attr('href'); if (!link) { link = ''; @@ -87,7 +87,7 @@ async function handler(ctx) { } else { const response = await ofetch(item.link); const $ = load(response); - newItem.description = $('div.news1content').html() || ''; + newItem.description = $('div.news1content').html(); } } else { // 涉及到其他站点,不方便做统一的 html 解析,直接返回链接 diff --git a/lib/routes/zjut/jwc/index.ts b/lib/routes/zjut/jwc/index.ts index 9e6c86b7586e..f6417c7795ee 100644 --- a/lib/routes/zjut/jwc/index.ts +++ b/lib/routes/zjut/jwc/index.ts @@ -99,7 +99,7 @@ async function handler(ctx) { } else { const response = await ofetch(item.link); const $ = load(response); - newItem.description = $('.wp_articlecontent').html() || ''; + newItem.description = $('.wp_articlecontent').html(); } } else { // 涉及到其他站点,不方便做统一的 html 解析,直接返回链接 diff --git a/lib/routes/zjut/www/index.ts b/lib/routes/zjut/www/index.ts index 07c4c5cb862d..3219231f5eda 100644 --- a/lib/routes/zjut/www/index.ts +++ b/lib/routes/zjut/www/index.ts @@ -55,7 +55,7 @@ async function handler(ctx) { const a = cheerioItem.find('a'); try { - const title = a.text() || ''; + const title = a.text(); let link = a.attr('href'); if (!link) { link = ''; @@ -102,7 +102,7 @@ async function handler(ctx) { } else { const response = await ofetch(item.link); const $ = load(response); - newItem.description = $('div.wp_articlecontent').html() || ''; + newItem.description = $('div.wp_articlecontent').html(); } } else { // 涉及到其他站点,不方便做统一的 html 解析,直接返回链接 diff --git a/lib/routes/zongheng/detail.ts b/lib/routes/zongheng/detail.ts index 91bba2bddb46..b58b76072c48 100644 --- a/lib/routes/zongheng/detail.ts +++ b/lib/routes/zongheng/detail.ts @@ -69,7 +69,7 @@ async function handler(ctx) { return { title: `${$('.book-info--title span').text()}(${author})- 纵横中文网`, - description: `${$('.book-info--nums').text().trim()} ${description}`, + description: `${$('.book-info--nums').text()} ${description}`, link, allowEmpty: true, image: $('.book-info--coverImage-img').attr('src'), diff --git a/lib/routes/zotero/versions.ts b/lib/routes/zotero/versions.ts index cba8375168a9..7ab2948ec9e8 100644 --- a/lib/routes/zotero/versions.ts +++ b/lib/routes/zotero/versions.ts @@ -44,8 +44,12 @@ async function handler() { .match(/\((.*)\)/); date = Array.isArray(date) ? date[1] : null; return { - title: item.text().trim(), - description: $('<div/>').append(item.nextUntil('h2').clone()).html(), + title: item.text(), + description: item + .nextUntil('h2') + .toArray() + .map((element) => $.html(element)) + .join(''), pubDate: date, link: url + '#' + item.attr('id'), }; diff --git a/lib/routes/zxcs/novel.ts b/lib/routes/zxcs/novel.ts index 5541810820f1..4a25f14e01aa 100644 --- a/lib/routes/zxcs/novel.ts +++ b/lib/routes/zxcs/novel.ts @@ -80,7 +80,7 @@ async function handler(ctx) { const $ = load(response); const links = String(item.link).split('/'); item.category = [types[String(links.at(-2))]]; - item.description = String($('.intro').first().html()); + item.description = String($('.intro').html()); item.image = baseUrl + String($('.book-cover img').attr('src')); item.author = $('.author').text(); return item; diff --git a/lib/routes/zzu/dzb.ts b/lib/routes/zzu/dzb.ts index 63522019db5e..459b20992537 100644 --- a/lib/routes/zzu/dzb.ts +++ b/lib/routes/zzu/dzb.ts @@ -46,12 +46,12 @@ async function handler(ctx) { .slice(0, 20) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); // 尝试获取发布时间 - const pubDateText = $element.find('span.fr.gray').text().trim(); + const pubDateText = $element.find('span.fr.gray').text(); return { title, diff --git a/lib/routes/zzu/kjc.ts b/lib/routes/zzu/kjc.ts index 55a339f72c62..c0abb79b23d9 100644 --- a/lib/routes/zzu/kjc.ts +++ b/lib/routes/zzu/kjc.ts @@ -46,12 +46,12 @@ async function handler(ctx) { .slice(0, 14) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); // 获取发布时间 - const pubDateText = $element.find('span').text().trim(); + const pubDateText = $element.find('span').text(); return { title, diff --git a/lib/routes/zzu/math.ts b/lib/routes/zzu/math.ts index 10c0167ec707..6b77a11f8e70 100644 --- a/lib/routes/zzu/math.ts +++ b/lib/routes/zzu/math.ts @@ -48,12 +48,12 @@ async function handler(ctx) { .slice(0, 16) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); // 获取发布时间 - const pubDateText = $element.find('span').text().trim(); + const pubDateText = $element.find('span').text(); return { title, diff --git a/lib/routes/zzu/news.ts b/lib/routes/zzu/news.ts index b0456ad46dd1..bd66484cb6b4 100644 --- a/lib/routes/zzu/news.ts +++ b/lib/routes/zzu/news.ts @@ -51,12 +51,12 @@ async function handler(ctx) { .slice(0, 15) .map((element) => { const $element = $(element); - const $link = $element.find('h3 a').first(); + const $link = $element.find('h3 a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); // 尝试获取发布时间 - const pubDateText = $element.find('.new-date').text().trim(); + const pubDateText = $element.find('.new-date').text(); // 尝试获取描述 const description = $element.find('p a').text().trim() || ''; diff --git a/lib/routes/zzu/rsc.ts b/lib/routes/zzu/rsc.ts index c870c9a3e987..e9fb7ab5c332 100644 --- a/lib/routes/zzu/rsc.ts +++ b/lib/routes/zzu/rsc.ts @@ -50,12 +50,12 @@ async function handler(ctx) { .slice(0, 14) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; - const title = $link.attr('title') || $link.text().trim(); + const title = $link.attr('title') || $link.text(); // 获取发布时间 - const pubDateText = $element.find('span').text().trim(); + const pubDateText = $element.find('span').text(); return { title, diff --git a/lib/routes/zzu/ss.ts b/lib/routes/zzu/ss.ts index 86089e1b29ba..5a32469f137d 100644 --- a/lib/routes/zzu/ss.ts +++ b/lib/routes/zzu/ss.ts @@ -52,7 +52,7 @@ async function handler(ctx) { // 获取发布时间 // xwzx: 格式为 MM-DD,需要补全年份 // tzgg: 格式为 yyyy-mm-dd,直接使用 - const pubDateText = $element.find('time').text().trim(); + const pubDateText = $element.find('time').text(); let pubDate = null; if (pubDateText) { diff --git a/lib/routes/zzu/sxy.ts b/lib/routes/zzu/sxy.ts index 09d55b110c47..e8c5886a9f43 100644 --- a/lib/routes/zzu/sxy.ts +++ b/lib/routes/zzu/sxy.ts @@ -59,13 +59,13 @@ function parseXyxwList($, typeDict, type) { .slice(0, 6) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); const description = $element.find('.right .con p').text().trim(); - const monthDay = $element.find('.time_con h3').text().trim(); - const year = $element.find('.time_con h6').text().trim(); + const monthDay = $element.find('.time_con h3').text(); + const year = $element.find('.time_con h6').text(); const pubDateText = `${year}-${monthDay}`; return { @@ -83,11 +83,11 @@ function parseOtherList($, typeDict, type) { .slice(0, 16) .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title'); - const pubDateText = $element.find('span.span01').text().trim(); + const pubDateText = $element.find('span.span01').text(); return { title, diff --git a/lib/routes/zzu/zcycwb.ts b/lib/routes/zzu/zcycwb.ts index 9141dc1f2916..606ab96e61e9 100644 --- a/lib/routes/zzu/zcycwb.ts +++ b/lib/routes/zzu/zcycwb.ts @@ -49,12 +49,12 @@ async function handler(ctx) { .toArray() .map((element) => { const $element = $(element); - const $link = $element.find('a').first(); + const $link = $element.find('a'); const link = new URL($link.attr('href'), typeDict[type][1]).href; const title = $link.attr('title') || $link.text().trim(); // 获取发布时间 (格式: [yyyy-mm-dd]) - const pubDateText = $element.find('i').text().trim().replaceAll(/[[\]]/g, ''); + const pubDateText = $element.find('i').text().replaceAll(/[[\]]/g, ''); return { title, diff --git a/lib/types.ts b/lib/types.ts index a565a21be7dd..ada8e506e58c 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -32,7 +32,7 @@ export type Category = // rss export type DataItem = { title: string; - description?: string; + description?: string | null; pubDate?: number | string | Date; link?: string; category?: string[]; @@ -47,8 +47,8 @@ export type DataItem = { guid?: string; id?: string; content?: { - html: string; - text: string; + html?: string | null; + text?: string | null; }; summary?: string; image?: string; @@ -81,7 +81,7 @@ export type DataItem = { export type Data = { title: string; - description?: string; + description?: string | null; link?: string; item?: DataItem[]; allowEmpty?: boolean; diff --git a/lib/views/json.ts b/lib/views/json.ts index 5dfff9a83479..19019d9586c3 100644 --- a/lib/views/json.ts +++ b/lib/views/json.ts @@ -21,7 +21,7 @@ const json = (data: Data) => { title: item.title, // content_html and content_text are each optional strings — but one or both must be present content_html: (item.content && item.content.html) || item.description || item.title, - content_text: item.content && item.content.text, + content_text: item.content?.text ?? undefined, summary: item.summary, image: item.image || item.itunes_item_image, banner_image: item.banner, From 175cf612fde88dab29f5aeb7b918c198e22177f7 Mon Sep 17 00:00:00 2001 From: TonyRL <TonyRL@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:26:16 +0800 Subject: [PATCH 0438/1190] chore: group bbob/* in dependabot config --- .github/dependabot.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 52b2a10afbdd..6442df3fa594 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,6 +22,9 @@ updates: - dependency-name: art-template versions: ['>=4.13.3'] groups: + bbob: + patterns: + - '@bbob/*' cloudflare: patterns: - '@cloudflare/*' From f8764e37aaf59715d989f455b7af1859acda141b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:32:35 +0000 Subject: [PATCH 0439/1190] chore(deps): bump the bbob group with 4 updates (#22850) Bumps the bbob group with 4 updates: [@bbob/html](https://github.com/JiLiZART/bbob), [@bbob/plugin-helper](https://github.com/JiLiZART/bbob), [@bbob/preset-html5](https://github.com/JiLiZART/bbob) and [@bbob/types](https://github.com/JiLiZART/bbob). Updates `@bbob/html` from 4.4.0 to 4.4.1 - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/html@4.4.0...@bbob/html@4.4.1) Updates `@bbob/plugin-helper` from 4.4.0 to 4.4.1 - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/plugin-helper@4.4.0...@bbob/plugin-helper@4.4.1) Updates `@bbob/preset-html5` from 4.4.0 to 4.4.1 - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/preset-html5@4.4.0...@bbob/preset-html5@4.4.1) Updates `@bbob/types` from 4.4.0 to 4.4.1 - [Release notes](https://github.com/JiLiZART/bbob/releases) - [Changelog](https://github.com/JiLiZART/BBob/blob/master/CHANGELOG.md) - [Commits](https://github.com/JiLiZART/bbob/compare/@bbob/types@4.4.0...@bbob/types@4.4.1) --- updated-dependencies: - dependency-name: "@bbob/html" dependency-version: 4.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: bbob - dependency-name: "@bbob/plugin-helper" dependency-version: 4.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: bbob - dependency-name: "@bbob/preset-html5" dependency-version: 4.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: bbob - dependency-name: "@bbob/types" dependency-version: 4.4.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: bbob ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 8 ++--- pnpm-lock.yaml | 88 +++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 7a6a11266dd5..32db4724b36b 100644 --- a/package.json +++ b/package.json @@ -59,9 +59,9 @@ "worker-test": "npm run worker-build && vitest run lib/worker.test.ts" }, "dependencies": { - "@bbob/html": "4.4.0", - "@bbob/plugin-helper": "4.4.0", - "@bbob/preset-html5": "4.4.0", + "@bbob/html": "4.4.1", + "@bbob/plugin-helper": "4.4.1", + "@bbob/preset-html5": "4.4.1", "@googleapis/youtube": "33.0.0", "@honeybadger-io/js": "6.15.3", "@hono/node-server": "2.0.12", @@ -145,7 +145,7 @@ "devDependencies": { "@actions/core": "3.0.1", "@actions/github": "9.1.1", - "@bbob/types": "4.4.0", + "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.0", "@cloudflare/vitest-pool-workers": "0.18.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5329c40c576..21b2b5c23575 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,14 +32,14 @@ importers: .: dependencies: '@bbob/html': - specifier: 4.4.0 - version: 4.4.0 + specifier: 4.4.1 + version: 4.4.1 '@bbob/plugin-helper': - specifier: 4.4.0 - version: 4.4.0 + specifier: 4.4.1 + version: 4.4.1 '@bbob/preset-html5': - specifier: 4.4.0 - version: 4.4.0 + specifier: 4.4.1 + version: 4.4.1 '@googleapis/youtube': specifier: 33.0.0 version: 33.0.0 @@ -285,8 +285,8 @@ importers: specifier: 9.1.1 version: 9.1.1 '@bbob/types': - specifier: 4.4.0 - version: 4.4.0 + specifier: 4.4.1 + version: 4.4.1 '@cloudflare/containers': specifier: 0.3.7 version: 0.3.7 @@ -546,26 +546,26 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@bbob/core@4.4.0': - resolution: {integrity: sha512-4rLEjFsbMECLsUsz8SCwP/W2LomPAT1Pj5KBfIF3RDBdQAaU0nSLOauIDhnU17zxUZBlvXsncZmZTWTFNqXCNQ==} + '@bbob/core@4.4.1': + resolution: {integrity: sha512-+/p5zQryGVbviM6MUCtHZL3TBtsPFLynCClgiWpQgonYunzIifVaPq8wkA3tnYRrz8rTVC2CMEaiGr16/KV+zw==} - '@bbob/html@4.4.0': - resolution: {integrity: sha512-cPNPMOq5akCexag3XerJLQ7A9TGdUdPPACzlZniMj2JGGgBsgGq9kIHm+6huQswvveQDd39dIkKUlcFpK1hQZg==} + '@bbob/html@4.4.1': + resolution: {integrity: sha512-pR2SruL2LmQEDDjZ+HrkW1wFtUkyTJN4tdUWQelLiRggAzR1XOH5ZtVSGfPlWzX5HvdjGjITxkvtCBuGaESegw==} - '@bbob/parser@4.4.0': - resolution: {integrity: sha512-5w0VnGCSY3JYGs1m4AfR5a3soBoc8HB3EUCYneXQqP1KPUvFuBKDc5XWGQ5EeXl6QuM03oS39HO19fhY9GgzXA==} + '@bbob/parser@4.4.1': + resolution: {integrity: sha512-Hs6caDSsY5zJdZZpj7ohTz4hQZGKPL+ZgZy+qbgDtfD9WV2YGxFGtXQf7PBu/aIRKOrkGgbCQxDYWlCEyONi9w==} - '@bbob/plugin-helper@4.4.0': - resolution: {integrity: sha512-1X9El64z/g+4vMKx+CnLc2O1No/KqRRU4Ku1PLYN1CD6OmJk/T1sQ3dlLAzSjV6gzZ2fSa8/endhZpf2ULKBLA==} + '@bbob/plugin-helper@4.4.1': + resolution: {integrity: sha512-vTjwNiiIHhihyD41DuxrUFVCuGPr1EB1fXvy5+HHnmcvWmOH5BzPSgSEs+KCdpWF+hIDMAho2toAHiDmYurCJQ==} - '@bbob/preset-html5@4.4.0': - resolution: {integrity: sha512-hvYpHCRxo2lhfGXgI8TT4BWpVCJM5Bxtgc2XL1Pr77lmTb6yTVG+nNNuNxBJ3Mz3K0XPzaOhlS1YkTLpf1vYfA==} + '@bbob/preset-html5@4.4.1': + resolution: {integrity: sha512-rk8T9NnaVQRciHklMY1s5FefswT777XKZ+uQISGToUzrkTPQh2B3E5tLlJN4Z0Cb0n9IKCuDxy6sqaCVfPwxOg==} - '@bbob/preset@4.4.0': - resolution: {integrity: sha512-N0k1pS9ywv6qbOiLAoM6RH1yOJtIHDSM7rHWmtfe+FyWjHnP7Icyn4McLIra2ZHlutJIVY+05UGAf375ByRzGw==} + '@bbob/preset@4.4.1': + resolution: {integrity: sha512-QX68ZKgmX8oLzB7TRaoUlXXX+CN/Mf/TtczH29QIHzwWNTXze04/4njYNulzmIJC7/VcABkK7feEbw8M96rU8Q==} - '@bbob/types@4.4.0': - resolution: {integrity: sha512-d79ov/IQFW5gEAllrK48xqI/IDs6y31F4gXRUX3d7KYN+r6EaRsChjMgv0wPppZovUL2/eECFOe82sqAa0HF7g==} + '@bbob/types@4.4.1': + resolution: {integrity: sha512-IDu7H3J9yMQG0U//0X4Akbbm3SOIKfeLmgPHO4s3s+WPsl+AeGb6dy4XBFLEDcDIN/N3f8WPibhIjLr0gFQ7Fg==} '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} @@ -3697,7 +3697,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.51: @@ -6603,39 +6603,39 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@bbob/core@4.4.0': + '@bbob/core@4.4.1': dependencies: - '@bbob/parser': 4.4.0 - '@bbob/plugin-helper': 4.4.0 - '@bbob/types': 4.4.0 + '@bbob/parser': 4.4.1 + '@bbob/plugin-helper': 4.4.1 + '@bbob/types': 4.4.1 - '@bbob/html@4.4.0': + '@bbob/html@4.4.1': dependencies: - '@bbob/core': 4.4.0 - '@bbob/plugin-helper': 4.4.0 - '@bbob/types': 4.4.0 + '@bbob/core': 4.4.1 + '@bbob/plugin-helper': 4.4.1 + '@bbob/types': 4.4.1 - '@bbob/parser@4.4.0': + '@bbob/parser@4.4.1': dependencies: - '@bbob/plugin-helper': 4.4.0 - '@bbob/types': 4.4.0 + '@bbob/plugin-helper': 4.4.1 + '@bbob/types': 4.4.1 - '@bbob/plugin-helper@4.4.0': + '@bbob/plugin-helper@4.4.1': dependencies: - '@bbob/types': 4.4.0 + '@bbob/types': 4.4.1 - '@bbob/preset-html5@4.4.0': + '@bbob/preset-html5@4.4.1': dependencies: - '@bbob/plugin-helper': 4.4.0 - '@bbob/preset': 4.4.0 - '@bbob/types': 4.4.0 + '@bbob/plugin-helper': 4.4.1 + '@bbob/preset': 4.4.1 + '@bbob/types': 4.4.1 - '@bbob/preset@4.4.0': + '@bbob/preset@4.4.1': dependencies: - '@bbob/plugin-helper': 4.4.0 - '@bbob/types': 4.4.0 + '@bbob/plugin-helper': 4.4.1 + '@bbob/types': 4.4.1 - '@bbob/types@4.4.0': {} + '@bbob/types@4.4.1': {} '@bcoe/v8-coverage@1.0.2': {} From a1a21b59de9e75ff0b53b98ba1f2f805c54ee6a1 Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Tue, 28 Jul 2026 00:44:30 +0800 Subject: [PATCH 0440/1190] fix(route): housekeeping (#22851) * fix(route): agirls * fix(route): hakkatv * fix(route): meteor * fix(route): dcfever * fix(route): netflix * fix(route): immich * fix(route): 0818tuan * fix(route): mhlw * fix(route): indienova * fix(route): anthropic * fix(route): tingtingfm * fix(route): pubscholar * fix(route): byteclicks * fix: correct path format in GameDB route * fix: add 'game' category to GameDB route * fix: reduce concurrency in item parsing for Byteclicks route --- lib/routes/0818tuan/index.ts | 2 +- lib/routes/agirls/topic-list.ts | 6 +-- lib/routes/agirls/topic.ts | 6 +-- lib/routes/agirls/utils.ts | 13 ++--- lib/routes/agirls/z-index.ts | 8 +-- lib/routes/anthropic/engineering.ts | 11 ++-- lib/routes/byteclicks/index.ts | 32 ++++++------ lib/routes/byteclicks/tag.ts | 43 +++++---------- lib/routes/byteclicks/utils.ts | 43 ++++++++++++--- lib/routes/dcfever/reviews.ts | 3 +- lib/routes/dcfever/utils.tsx | 12 ++--- lib/routes/hakkatv/{type.ts => news.ts} | 37 +++++++------ lib/routes/immich/cursed-knowledge.ts | 66 ++++++++++++++++-------- lib/routes/indienova/gamedb.ts | 29 ++++++++--- lib/routes/meteor/index.ts | 27 +++++----- lib/routes/meteor/utils.ts | 39 +++++++++----- lib/routes/mhlw/monthly-labour-survey.ts | 14 +++-- lib/routes/netflix/research.ts | 2 + lib/routes/pubscholar/explore.ts | 19 ++++--- lib/routes/pubscholar/types.ts | 14 ++--- lib/routes/tingtingfm/program.tsx | 2 +- lib/routes/tingtingfm/utils.ts | 22 ++++---- 22 files changed, 267 insertions(+), 183 deletions(-) rename lib/routes/hakkatv/{type.ts => news.ts} (74%) diff --git a/lib/routes/0818tuan/index.ts b/lib/routes/0818tuan/index.ts index 39fcc7f56fa6..6605b42ef156 100644 --- a/lib/routes/0818tuan/index.ts +++ b/lib/routes/0818tuan/index.ts @@ -44,7 +44,7 @@ async function handler(ctx) { link: item.attr('href').startsWith('http') ? item.attr('href') : `${baseUrl}${item.attr('href')}`, }; }) - .filter((i) => !i.link.includes('m.0818tuan.com/tb1111.php')); + .filter((i) => !i.link.includes('m.0818tuan.com/tb1111.php') && !i.link.includes('www.0818tuan.com/pdd/zudui.php')); const items = await Promise.all( list.map((item) => diff --git a/lib/routes/agirls/topic-list.ts b/lib/routes/agirls/topic-list.ts index 5d4c48f5e1e3..feeaac7988ce 100644 --- a/lib/routes/agirls/topic-list.ts +++ b/lib/routes/agirls/topic-list.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { baseUrl } from './utils'; @@ -33,9 +33,9 @@ async function handler() { const category = 'topic'; const link = `${baseUrl}/${category}`; - const response = await got(`${baseUrl}/${category}`); + const response = await ofetch(`${baseUrl}/${category}`); - const $ = load(response.data); + const $ = load(response); const items = $('.ag-topic') .toArray() diff --git a/lib/routes/agirls/topic.ts b/lib/routes/agirls/topic.ts index ff67a670c170..d8c9ae56cf59 100644 --- a/lib/routes/agirls/topic.ts +++ b/lib/routes/agirls/topic.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { baseUrl, parseArticle } from './utils'; @@ -32,9 +32,9 @@ export const route: Route = { async function handler(ctx) { const topic = ctx.req.param('topic'); const link = `${baseUrl}/topic/${topic}`; - const response = await got(link); + const response = await ofetch(link); - const $ = load(response.data); + const $ = load(response); const ldJson = JSON.parse($('script[type="application/ld+json"]').text()); const list = $('.ag-post-item__link') .toArray() diff --git a/lib/routes/agirls/utils.ts b/lib/routes/agirls/utils.ts index 31d2d05a90a1..b1cafcc48c23 100644 --- a/lib/routes/agirls/utils.ts +++ b/lib/routes/agirls/utils.ts @@ -1,13 +1,13 @@ import { load } from 'cheerio'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://agirls.aotter.net'; const parseArticle = async (item) => { - const detailResponse = await got(item.link); - const content = load(detailResponse.data); + const detailResponse = await ofetch(item.link); + const content = load(detailResponse); item.category = [ ...new Set( @@ -17,11 +17,12 @@ const parseArticle = async (item) => { ), ]; const ldJson = JSON.parse(content('script[type="application/ld+json"]').text()); + const newsArticle = ldJson['@graph'].find((g) => g['@type'] === 'NewsArticle'); item.description = content('.ag-article__content').html(); - item.pubDate = parseDate(ldJson['@graph'][0].datePublished); // 2023-07-05T12:11:36+08:00 - item.updated = parseDate(ldJson['@graph'][0].dateModified); // 2023-07-05T12:11:36+08:00 - item.author = ldJson['@graph'][0].author.map((a) => a.name).join(', '); + item.pubDate = parseDate(newsArticle.datePublished); // 2023-07-05T12:11:36+08:00 + item.updated = parseDate(newsArticle.dateModified); // 2023-07-05T12:11:36+08:00 + item.author = newsArticle.author.map((a) => a.name).join(', '); return item; }; diff --git a/lib/routes/agirls/z-index.ts b/lib/routes/agirls/z-index.ts index cfab56f9d2f4..c1efd1a38163 100644 --- a/lib/routes/agirls/z-index.ts +++ b/lib/routes/agirls/z-index.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { baseUrl, parseArticle } from './utils'; @@ -36,11 +36,11 @@ export const route: Route = { async function handler(ctx) { const { category = '' } = ctx.req.param(); const link = `${baseUrl}/posts${category ? `/${category}` : ''}`; - const response = await got(link); + const response = await ofetch(link); - const $ = load(response.data); + const $ = load(response); - const list = $('.ag-post-item__link') + const list = $('.ag-post-list .ag-post-item__link') .toArray() .map((item) => { item = $(item); diff --git a/lib/routes/anthropic/engineering.ts b/lib/routes/anthropic/engineering.ts index 4fc6cdd5630b..c9528885fb0f 100644 --- a/lib/routes/anthropic/engineering.ts +++ b/lib/routes/anthropic/engineering.ts @@ -4,6 +4,7 @@ import pMap from 'p-map'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/engineering', @@ -28,17 +29,17 @@ async function handler(ctx) { const $ = load(response); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; - const list: DataItem[] = $('a[class*="cardLink"]') + const list: DataItem[] = $('a[class$="cardLink"]') .toArray() .map((element) => { const $e = $(element); const href = $e.attr('href') ?? ''; const fullLink = href.startsWith('http') ? href : `${baseUrl}${href}`; - const pubDate = $e.find('div[class*="date"]').text(); + const dateText = $e.find('div[class$="date"]').text(); return { title: $e.find('h2, h3').text(), link: fullLink, - pubDate, + pubDate: dateText ? parseDate(dateText, 'MMM D, YYYY') : undefined, }; }) .filter((item) => item.title && item.link) @@ -51,7 +52,7 @@ async function handler(ctx) { const response = await ofetch(item.link!); const $ = load(response); - const content = $('article > div > div[class*="__body"]'); + const content = $('article > div > div[class$="__body"]'); content.find('img').each((_, e) => { const $e = $(e); @@ -65,6 +66,8 @@ async function handler(ctx) { }); item.description = content.html(); + const dateText = $('p[class$="date"]').text().replace('Published', '').trim(); + item.pubDate ||= dateText ? parseDate(dateText, 'MMM D, YYYY') : undefined; return item; }), diff --git a/lib/routes/byteclicks/index.ts b/lib/routes/byteclicks/index.ts index 67865b163b7c..741758e67617 100644 --- a/lib/routes/byteclicks/index.ts +++ b/lib/routes/byteclicks/index.ts @@ -1,38 +1,38 @@ -import type { Route } from '@/types'; -import got from '@/utils/got'; +import { load } from 'cheerio'; +import pMap from 'p-map'; -import { parseItem } from './utils'; +import type { Route } from '@/types'; +import { PRESETS } from '@/utils/header-generator'; +import ofetch from '@/utils/ofetch'; -const baseUrl = 'https://byteclicks.com'; +import { baseUrl, parseItem, parseList } from './utils'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/byteclicks', radar: [ { source: ['byteclicks.com/'], - target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['TonyRL'], handler, url: 'byteclicks.com/', }; async function handler(ctx) { - const { data } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { - searchParams: { - per_page: ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100, - }, - }); + const response = await ofetch(baseUrl, { headerGeneratorOptions: PRESETS.MODERN_WINDOWS_CHROME }); + const $ = load(response); - const items = parseItem(data); + const list = parseList($).slice(0, ctx.req.query('limit') ? Number(ctx.req.query('limit')) : undefined); + const items = await pMap(list, (item) => parseItem(item), { concurrency: 5 }); return { - title: '字节点击 - 聚合全球优质资源,跟踪世界前沿科技', - description: - 'byteclicks.com 最专业的前沿科技网站。聚合全球优质资源,跟踪世界前沿科技,精选推荐一些很棒的互联网好资源好工具好产品。寻找有前景好项目、找论文、找报告、找数据、找课程、找电子书上byteclicks!byteclicks.com是投资人、科研学者、学生每天必看的网站。', - image: 'https://byteclicks.com/wp-content/themes/RK-Blogger/images/wbolt.ico', + title: $('head title').text(), + description: $('head meta[name="description"]').attr('content'), + image: $('head link[rel="shortcut icon"]').attr('href'), link: baseUrl, item: items, }; diff --git a/lib/routes/byteclicks/tag.ts b/lib/routes/byteclicks/tag.ts index 8a15daa89ed7..0364e3458970 100644 --- a/lib/routes/byteclicks/tag.ts +++ b/lib/routes/byteclicks/tag.ts @@ -1,23 +1,17 @@ -import type { Route } from '@/types'; -import got from '@/utils/got'; +import { load } from 'cheerio'; +import pMap from 'p-map'; -import { parseItem } from './utils'; +import type { Route } from '@/types'; +import { PRESETS } from '@/utils/header-generator'; +import ofetch from '@/utils/ofetch'; -const baseUrl = 'https://byteclicks.com'; +import { baseUrl, parseItem, parseList } from './utils'; export const route: Route = { path: '/tag/:tag', categories: ['new-media'], example: '/byteclicks/tag/人工智能', parameters: { tag: '标签,可在URL中找到' }, - features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, - supportBT: false, - supportPodcast: false, - supportScihub: false, - }, radar: [ { source: ['byteclicks.com/tag/:tag'], @@ -31,27 +25,18 @@ export const route: Route = { async function handler(ctx) { const tag = ctx.req.param('tag'); - const { data: search } = await got(`${baseUrl}/wp-json/wp/v2/tags`, { - searchParams: { - search: tag, - per_page: 100, - }, - }); - const tagData = search.find((item) => item.name === tag); + const link = `${baseUrl}/tag/${tag}`; - const { data } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { - searchParams: { - per_page: ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100, - tags: tagData.id, - }, - }); + const response = await ofetch(link, { headerGeneratorOptions: PRESETS.MODERN_WINDOWS_CHROME }); + const $ = load(response); - const items = parseItem(data); + const list = parseList($).slice(0, ctx.req.query('limit') ? Number(ctx.req.query('limit')) : undefined); + const items = await pMap(list, (item) => parseItem(item), { concurrency: 5 }); return { - title: `${tagData.name} - 字节点击`, - image: 'https://byteclicks.com/wp-content/themes/RK-Blogger/images/wbolt.ico', - link: tagData.link, + title: $('head title').text(), + image: $('head link[rel="shortcut icon"]').attr('href'), + link, item: items, }; } diff --git a/lib/routes/byteclicks/utils.ts b/lib/routes/byteclicks/utils.ts index d8073f39eb01..11a48930ee8b 100644 --- a/lib/routes/byteclicks/utils.ts +++ b/lib/routes/byteclicks/utils.ts @@ -1,11 +1,38 @@ +import type { CheerioAPI } from 'cheerio'; +import { load } from 'cheerio'; + +import type { DataItem } from '@/types'; +import cache from '@/utils/cache'; +import { PRESETS } from '@/utils/header-generator'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +export const baseUrl = 'https://byteclicks.com'; + +export const parseList = ($: CheerioAPI) => + $('article.post') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('a.post-title'); + return { + title: a.text(), + link: a.attr('href'), + pubDate: timezone(parseDate($item.find('.meta-item.primary em').text(), 'YYYY.MM.DD'), 8), + category: $item.find('.cate-tag').text(), + }; + }); + +export const parseItem = (item: DataItem) => + cache.tryGet(item.link!, async () => { + const response = await ofetch(item.link!, { headerGeneratorOptions: PRESETS.MODERN_WINDOWS_CHROME }); + const $ = load(response); -const parseItem = (data) => - data.map((item) => ({ - title: item.title.rendered, - description: item.content.rendered, - pubDate: parseDate(item.date_gmt), - link: item.link, - })); + const content = $('.article-detail'); + content.find('.erphp-wppay, .copyright-message').remove(); + item.author = $('.meta-author em').text().trim(); + item.description = content.html()?.trim(); -export { parseItem }; + return item; + }); diff --git a/lib/routes/dcfever/reviews.ts b/lib/routes/dcfever/reviews.ts index 84e49498fd7a..9baa959fb48d 100644 --- a/lib/routes/dcfever/reviews.ts +++ b/lib/routes/dcfever/reviews.ts @@ -39,7 +39,8 @@ async function handler(ctx) { title: item.text(), link: new URL(item.attr('href'), link).href, }; - }); + }) + .filter((item, index, arr) => arr.findIndex((i) => i.link === item.link) === index); const items = await Promise.all(list.map((item) => parseItem(item))); diff --git a/lib/routes/dcfever/utils.tsx b/lib/routes/dcfever/utils.tsx index 7045d8fa49d5..070606487a31 100644 --- a/lib/routes/dcfever/utils.tsx +++ b/lib/routes/dcfever/utils.tsx @@ -81,16 +81,16 @@ const parseItem = (item) => }); content.find('p a').each((_, e) => { - e = $(e); - if (e.text().startsWith('下一頁為')) { - e.remove(); + const $e = $(e); + if ($e.text().startsWith('下一頁為')) { + $e.remove(); } }); content.find('iframe').each((_, e) => { - e = $(e); - if (e.attr('src').startsWith('https://www.facebook.com/plugins/like.php')) { - e.remove(); + const $e = $(e); + if ($e.attr('src')?.startsWith('https://www.facebook.com/plugins/like.php')) { + $e.remove(); } }); diff --git a/lib/routes/hakkatv/type.ts b/lib/routes/hakkatv/news.ts similarity index 74% rename from lib/routes/hakkatv/type.ts rename to lib/routes/hakkatv/news.ts index f386fbb4871c..680d668161f2 100644 --- a/lib/routes/hakkatv/type.ts +++ b/lib/routes/hakkatv/news.ts @@ -1,6 +1,6 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -41,41 +41,44 @@ async function handler(ctx) { const allData = type ? ( - await got(`${apiUrl}/api/news/index`, { - searchParams: { - per: 4, + await ofetch(`${apiUrl}/api/news/index`, { + query: { + per: 8, 'sort[created_at]': 'desc', type, keywords: '', }, }) - ).data.data + ).data : await Promise.all( typeMap.map(async (t) => { - const { data } = await got(`${apiUrl}/api/news/index`, { - searchParams: { - per: 4, + const { data } = await ofetch(`${apiUrl}/api/news/index`, { + query: { + per: 8, 'sort[created_at]': 'desc', type: t, keywords: '', }, }); - return data.data; + return data; }) ); - const list = allData.flat().map((item) => ({ - title: item.title, - pubDate: timezone(parseDate(item.created_at), 8), - author: item.author, - link: `${baseUrl}/news-detail/${item.id}`, - id: item.id, - })); + const list = allData + .flat() + .filter((item, index, arr) => arr.findIndex((i) => i.id === item.id) === index) + .map((item) => ({ + title: item.title, + pubDate: timezone(parseDate(item.created_at), 8), + author: item.author, + link: `${baseUrl}/news-detail/${item.id}`, + id: item.id, + })); const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const { data } = await got(`${apiUrl}/api/news/read/${item.id}`); + const data = await ofetch(`${apiUrl}/api/news/read/${item.id}`); item.category = data.tag.map((t) => t.tag); item.description = data.content.replaceAll('\n', '<br>'); delete item.id; diff --git a/lib/routes/immich/cursed-knowledge.ts b/lib/routes/immich/cursed-knowledge.ts index 93eaffe43dfa..ec3e6e2d984e 100644 --- a/lib/routes/immich/cursed-knowledge.ts +++ b/lib/routes/immich/cursed-knowledge.ts @@ -1,5 +1,3 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; @@ -19,31 +17,57 @@ export const route: Route = { handler, }; +const ghType = { + pr: 'pull', + issue: 'issues', + discussion: 'discussions', +}; + +const parseGithubLink = (arg: string) => { + const number = arg.match(/(?:number: )?([\d_]+)/)?.[1].replaceAll('_', ''); + const type = arg.match(/type: '(\w+)'/)?.[1] ?? 'pr'; + return `https://github.com/immich-app/immich/${ghType[type]}/${number}`; +}; + +const matchString = (entry: string, key: string) => { + const m = entry.match(new RegExp(`${key}:\\s*(?:'((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)")`)); + return (m?.[1] ?? m?.[2])?.replaceAll(/\\(.)/g, '$1'); +}; + async function handler() { const baseUrl = 'https://immich.app'; const link = `${baseUrl}/cursed-knowledge/`; - const response = await ofetch(link); - const $ = load(response); - - const items = $('div.justify-around ul li') - .toArray() - .map((item) => { - const $item = $(item); - const href = $item.find('a').attr('href'); - const title = $item.find('section p').first().text(); - return { - title, - description: $item.find('section p').last().text(), - link: href ?? `${link}#${title}`, - pubDate: parseDate($item.find('div.justify-start').text()), - }; - }); + const [source, feed] = await Promise.all([ + ofetch('https://raw.githubusercontent.com/immich-app/static-pages/main/apps/root.immich.app/src/routes/cursed-knowledge/+page.svelte'), + ofetch(`${baseUrl}/blog/feed.json`, { responseType: 'json' }), + ]); + + const entries = source + .slice(source.indexOf('const items')) + .split(/\n {4}(?:withBlog\()?\{\n/) + .slice(1); + + const items = entries.map((entry) => { + const blogId = entry.match(/id: '([^']+)'/)?.[1]; + const blogPost = blogId && feed.items.find((post) => post.id.endsWith(blogId)); + const gh = entry.match(/link: asGithubLink\(([^)]*)\)/); + const date = entry.match(/new Date\((\d+), (\d+), (\d+)\)/); + const title = matchString(entry, 'title'); + + return { + title, + description: matchString(entry, 'description'), + link: (gh ? parseGithubLink(gh[1]) : undefined) ?? entry.match(/href: '([^']+)'/)?.[1] ?? blogPost?.url, + guid: `${link}#${title}`, + pubDate: date ? parseDate(`${date[1]}-${Number(date[2]) + 1}-${date[3]}`, 'YYYY-M-D') : blogPost ? parseDate(blogPost.date_published) : undefined, + }; + }); return { - title: $('head title').text(), - description: $('p.text-center').text(), - image: `${baseUrl}${$('head link[rel="icon"]').attr('href')}`, + title: 'Cursed Knowledge | Immich', + description: 'Cursed knowledge we have learned as a result of building Immich that we wish we never knew.', + image: `${baseUrl}/favicon.ico`, link, item: items, }; diff --git a/lib/routes/indienova/gamedb.ts b/lib/routes/indienova/gamedb.ts index 34efc5a193e5..69a664b206d5 100644 --- a/lib/routes/indienova/gamedb.ts +++ b/lib/routes/indienova/gamedb.ts @@ -3,12 +3,26 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; +import { parseDate, parseRelativeDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/gamedb/recent', - name: 'Unknown', + name: 'GameDB 游戏库', + path: '/gamedb/recent/:platform?', + example: '/indienova/gamedb/recent', + categories: ['game'], + parameters: { + platform: { + description: '平台,留空为 `all`', + options: [ + { value: 'all', label: '全部' }, + { value: 'ps4', label: 'PS4' }, + { value: 'xboxone', label: 'XBOX One' }, + { value: 'nintendo-switch', label: 'Nintendo Switch' }, + ], + default: 'all', + }, + }, maintainers: ['TonyRL'], handler, }; @@ -23,14 +37,15 @@ async function handler(ctx) { const list = $('.related-game') .toArray() .map((item) => { - item = $(item); + const $item = $(item); return { - title: item + title: $item .find('span') .contents() .filter((_, el) => el.nodeType === 3) .text(), - link: new URL(item.find('a').attr('href'), baseUrl).href, + link: new URL($item.find('a').attr('href')!, baseUrl).href, + pubDate: parseRelativeDate($item.find('small').first().text()), }; }); @@ -50,7 +65,7 @@ async function handler(ctx) { article.find('#showHiddenText').remove(); item.description = $('.cover-image').prop('outerHTML') + $('.tab-container').html() + article.html(); - item.pubDate = $('.gamedb-release').length ? timezone(parseDate($('.gamedb-release').text().replaceAll(/[()]/g, '')), 8) : null; + item.pubDate = $('.gamedb-release').length ? timezone(parseDate($('.gamedb-release').text().replaceAll(/[()]/g, '')), 8) : item.pubDate; return item; }) diff --git a/lib/routes/meteor/index.ts b/lib/routes/meteor/index.ts index 4ef1310201a4..4243141cd7d3 100644 --- a/lib/routes/meteor/index.ts +++ b/lib/routes/meteor/index.ts @@ -1,6 +1,5 @@ import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, getBoards, renderDesc } from './utils'; @@ -33,8 +32,9 @@ async function handler(ctx) { board = boardInfo.id; } - const { data: response } = await got.post(`${baseUrl}/article/get_new_articles`, { - json: { + const response = await ofetch(`${baseUrl}/article/get_new_articles`, { + method: 'POST', + body: { boardId: board, isCollege: false, page: 0, @@ -44,17 +44,14 @@ async function handler(ctx) { const result = JSON.parse(decodeURIComponent(response.result)); - const items = await Promise.all( - result.map((item) => - cache.tryGet(`meteor:${item.id}`, () => ({ - title: item.title, - description: renderDesc(item.content), - link: `${baseUrl}/article/${item.shortId}`, - author: item.authorAlias, - pubDate: parseDate(item.createdAt), - })) - ) - ); + const items = result.map((item) => ({ + title: item.title, + description: renderDesc(item.content), + link: `${baseUrl}/article/${item.shortId}`, + author: item.authorAlias, + pubDate: parseDate(item.createdAt), + category: item.tagNameList, + })); return { title: `${board === 'all' ? '全部看板' : boardInfo.title} | Meteor 學生社群`, diff --git a/lib/routes/meteor/utils.ts b/lib/routes/meteor/utils.ts index 15930b9814a6..acee692bee1b 100644 --- a/lib/routes/meteor/utils.ts +++ b/lib/routes/meteor/utils.ts @@ -1,5 +1,5 @@ import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { renderMedia } from './templates/desc'; @@ -7,28 +7,32 @@ const baseUrl = 'https://meteor.today'; const getBoards = () => cache.tryGet('meteor:boards', async () => { - const { data: response } = await got.post(`${baseUrl}/board/get_boards`, { - json: { + const response = await ofetch(`${baseUrl}/board/get_boards`, { + method: 'POST', + body: { isCollege: 'false', }, }); - return JSON.parse(decodeURIComponent(response.result)).map((item) => ({ - title: `${item.category ? `${item.category} - ` : ''}${item.name}`, - description: item.id, - feedDescription: item.description, - category: item.articleCategory, - link: `${baseUrl}/board/${item.alias ?? item.name}`, - alias: item.alias, - imgUrl: item.imageUrl, - id: item.id, - })); + return JSON.parse(decodeURIComponent(response.result)) + .map((item) => ({ + title: `${item.category ? `${item.category} - ` : ''}${item.name}`, + description: item.id, + feedDescription: item.description, + category: item.articleCategory, + link: `${baseUrl}/board/${item.alias ?? item.name}`, + alias: item.alias, + imgUrl: item.imageUrl, + id: item.id, + })) + .filter((item, index, arr) => arr.findIndex((i) => i.link === item.link) === index); }); const renderDesc = (desc) => { const youTube = /(?:https?:\/\/)?(?:www\.)?youtu\.?be.*(?:v=|v\/|\/)([\w-]+)&?/g; const matchYouTube = desc.match(youTube); const matchImgur = desc.match(/https:\/\/i.imgur.com\/\w*.(jpg|png|gif|jpeg)/g); + const matchImage = desc.match(/https:\/\/storage\.meteor\.today\/image\/[\da-f]{24}\.(jpg|png)/g); const matchVideo = desc.match(/(https:\/\/storage\.meteor\.today\/video\/[\da-f]{24}\.)(mp4|mov|avi|flv|wmv|mpeg|mkv)/gi); const matchSticker = desc.match(/assets\/images\/stickers\/(duck|ep2|ep1)\/\w*.(jpg|png|gif|jpeg)/g); const matchEmoji = desc.match(/assets\/images\/emoji\/\w*.(jpg|png|gif|jpeg)/g); @@ -49,6 +53,15 @@ const renderDesc = (desc) => { ); } } + if (matchImage) { + for (const img of matchImage) { + desc = desc.replace(img, () => + renderMedia({ + img, + }) + ); + } + } if (matchVideo) { for (const video of matchVideo) { desc = desc.replace(video, () => diff --git a/lib/routes/mhlw/monthly-labour-survey.ts b/lib/routes/mhlw/monthly-labour-survey.ts index b994e062e8fa..4f7b0d9eaa82 100644 --- a/lib/routes/mhlw/monthly-labour-survey.ts +++ b/lib/routes/mhlw/monthly-labour-survey.ts @@ -22,6 +22,15 @@ export const route: Route = { url: 'www.mhlw.go.jp/toukei/list/30-1a.html', }; +const parseJapaneseDate = (text: string) => { + const normalized = text + .replaceAll(/([^)]+)/g, '') + // oxlint-disable-next-line regexp/no-obscure-range + .replaceAll(/[0-9]/g, (c) => String.fromCodePoint(c.codePointAt(0)! - 0xfee0)) + .replace(/^令和(\d+)年/, (_, year) => `${Number(year) + 2018}年`); + return parseDate(normalized, 'YYYY年M月D日'); +}; + async function fetchPage(url: string) { const raw = await ofetch(url, { responseType: 'arrayBuffer' }); const decoder = new TextDecoder('shift-jis'); @@ -62,12 +71,11 @@ async function handler(ctx: Context) { const $ = load(response); const dateText = $('.prt-topContents .al-right').text(); - const cleanedDate = dateText.replaceAll(/([^)]+)/g, ''); const content = $('#contentsInner'); content.find('.prt-topContents, .prt-linkNavi, .prt-plugin').remove(); - item.title = $('h1#pageTitle').text() || item.title; - item.pubDate = timezone(parseDate(cleanedDate, 'YYYY年M月D日'), 9); + item.title = $('h1#pageTitle').text().trim() || item.title; + item.pubDate = timezone(parseJapaneseDate(dateText), 9); item.description = content.html()?.trim(); return item; diff --git a/lib/routes/netflix/research.ts b/lib/routes/netflix/research.ts index 721de82c474f..eba4a2970b13 100644 --- a/lib/routes/netflix/research.ts +++ b/lib/routes/netflix/research.ts @@ -1,6 +1,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { collapseWhitespace } from '@/utils/common-utils'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; @@ -72,6 +73,7 @@ async function handler() { title: item.title, description: item.description, link: item.link, + guid: collapseWhitespace(item.title) ?? undefined, pubDate: (item.date ?? item.startDate) ? parseDate(item.date ?? item.startDate) : undefined, category: item.tags?.json, image: item.image?.url, diff --git a/lib/routes/pubscholar/explore.ts b/lib/routes/pubscholar/explore.ts index cee31ba776e5..576ff80432aa 100644 --- a/lib/routes/pubscholar/explore.ts +++ b/lib/routes/pubscholar/explore.ts @@ -47,14 +47,17 @@ async function handler(ctx) { }, }); - const list = response.content.map((item) => ({ - title: (item.is_free || item.links.some((l) => l.is_open_access) ? '「Open Access」' : '') + sanitizeHtml(item.title, { allowedTags: [], allowedAttributes: {} }), - description: item.abstracts + `<br>${item.links.map((link) => `<a href="${link.url}">${link.is_open_access ? '「Open Access」' : ''}${link.name}</a>`).join('<br>')}`, - author: item.author.join('; '), - pubDate: parseDate(item.date), - category: item.keywords.map((keyword) => sanitizeHtml(keyword, { allowedTags: [], allowedAttributes: {} })), - link: `${baseUrl}/${category}/${getArticleLink(item.id)}`, - })); + const list = response.content.map((item) => { + const date = item.date ?? item.issue_date ?? item.year; + return { + title: (item.is_free || item.links?.some((l) => l.is_open_access) ? '「Open Access」' : '') + sanitizeHtml(item.title, { allowedTags: [], allowedAttributes: {} }), + description: item.abstracts + `<br>${(item.links ?? []).map((link) => `<a href="${link.url}">${link.is_open_access ? '「Open Access」' : ''}${link.name}</a>`).join('<br>')}`, + author: (item.author ?? item.inventors)?.join('; '), + pubDate: date ? parseDate(String(date), ['YYYY-MM-DD', 'YYYYMMDD', 'YYYY']) : undefined, + category: item.keywords?.map((keyword) => sanitizeHtml(keyword, { allowedTags: [], allowedAttributes: {} })), + link: `${baseUrl}/${category}/${getArticleLink(item.id)}`, + }; + }); return { title: 'PubScholar 公益学术平台', diff --git a/lib/routes/pubscholar/types.ts b/lib/routes/pubscholar/types.ts index 8d51adfbe17c..497ecb6fbc98 100644 --- a/lib/routes/pubscholar/types.ts +++ b/lib/routes/pubscholar/types.ts @@ -5,10 +5,10 @@ interface Link { } interface Content { - date: string; + date?: string; attachments: any[]; - keywords: string[]; - year: number; + keywords?: string[]; + year?: number | string; source: string; title: string; type: string; @@ -17,21 +17,23 @@ interface Content { school: any[]; first_page: string; local_links: any[]; - links: Link[]; + links?: Link[]; id: string; graduation_institution: any[]; cn_type: string; article_type: string; issue: string; abstracts: string; - author: string[]; + author?: string[]; + inventors?: string[]; + issue_date?: string; last_page: string; degree: string; tutor: any[]; semantic_entities: object; volume: string; source_list: string[]; - is_free: boolean; + is_free?: boolean; } export interface Resource { diff --git a/lib/routes/tingtingfm/program.tsx b/lib/routes/tingtingfm/program.tsx index 0ff2db14bf6f..330b04159c3e 100644 --- a/lib/routes/tingtingfm/program.tsx +++ b/lib/routes/tingtingfm/program.tsx @@ -50,7 +50,7 @@ async function handler(ctx) { const mobileBaseUrl = 'https://mobile.tingtingfm.com'; const params = { - version: 'h5_5.16', + version: 'h5_6.3.2', client: getClientVal(30), h_program_id: programId, }; diff --git a/lib/routes/tingtingfm/utils.ts b/lib/routes/tingtingfm/utils.ts index 02d8ec5b7774..899d559993f3 100644 --- a/lib/routes/tingtingfm/utils.ts +++ b/lib/routes/tingtingfm/utils.ts @@ -1,20 +1,20 @@ -/* eslint-disable unicorn/prefer-code-point */ import md5 from '@/utils/md5'; const SALT = '1Ftjv0bfpVmqbE38'; +const randomChar = () => { + const random = Math.floor(62 * Math.random()); + if (random < 10) { + return random; + } + if (random < 36) { + return String.fromCodePoint(random + 55); + } + return String.fromCodePoint(random + 61); +}; + const getClientVal = (length) => { let result = ''; - const randomChar = () => { - const random = Math.floor(62 * Math.random()); - if (random < 10) { - return random; - } - if (random < 36) { - return String.fromCharCode(random + 55); - } - return String.fromCharCode(random + 61); - }; while (result.length < length) { result += randomChar(); } From 9b6585e29bcb270c47bc0dde3b56883a6abc4dbe Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Tue, 28 Jul 2026 03:03:56 +0800 Subject: [PATCH 0441/1190] fix(route): update link to adapt new website html (#22853) --- lib/routes/hebeimuseum/list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/hebeimuseum/list.tsx b/lib/routes/hebeimuseum/list.tsx index d4e11f967196..3f4eec9f0d19 100644 --- a/lib/routes/hebeimuseum/list.tsx +++ b/lib/routes/hebeimuseum/list.tsx @@ -94,7 +94,7 @@ export const route: Route = { return { title: listTitle, - itemLink: link, + itemLink: `${baseUrl}${link}`, imgUrl: `${baseUrl}${imgUrlRaw}`, }; }); From 1950616f04c9203797e253fff2ff091669b35c7b Mon Sep 17 00:00:00 2001 From: AiraNadih <128119996+AiraNadih@users.noreply.github.com> Date: Tue, 28 Jul 2026 07:51:03 +0800 Subject: [PATCH 0442/1190] fix(cma): fetch full bulletin content from NMC (#22830) * fix(cma): handle SafeLine challenge * fix(cma): refine channel route handling --- lib/routes/cma/channel.tsx | 105 ++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/lib/routes/cma/channel.tsx b/lib/routes/cma/channel.tsx index abdcf0c96f0b..f8920a830df9 100644 --- a/lib/routes/cma/channel.tsx +++ b/lib/routes/cma/channel.tsx @@ -1,12 +1,51 @@ +import { createHash } from 'node:crypto'; + import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +const solveChallenge = (prefix: string, leadingZeroBits: number) => { + for (let count = 0; count < 100_000_000; count++) { + const suffix = count.toString(16); + const hash = createHash('sha1') + .update(prefix + suffix) + .digest(); + if (hash.readUInt32BE(0) >>> (32 - leadingZeroBits) === 0) { + return suffix; + } + } + throw new Error('Failed to solve the SafeLine challenge of weather.cma.cn'); +}; + +const fetchPageWithChallenge = async (url: string) => { + const response = await ofetch.raw<string>(url); + const html = response._data ?? ''; + const challenge = response.headers + .getSetCookie() + .find((cookie) => cookie.startsWith('safeline_bot_challenge=')) + ?.split(';', 1)[0]; + const prefix = html.match(/var prefix = '(\w+)';/)?.[1]; + const leadingZeroBits = Number(html.match(/var leading_zero_bit = (\d+);/)?.[1]); + + if (!challenge || !prefix || !leadingZeroBits) { + return html; + } + + const answer = challenge.replace('safeline_bot_challenge=', 'safeline_bot_challenge_ans=') + solveChallenge(prefix, leadingZeroBits); + + return ofetch<string>(url, { + headers: { + Cookie: `${challenge}; ${answer}`, + }, + }); +}; + export const route: Route = { path: '/channel/:id?', categories: ['forecast'], @@ -15,7 +54,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: false, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, @@ -65,11 +104,11 @@ async function handler(ctx) { }, }); - const data = response?.data?.pop() ?? {}; + const article = response?.data?.pop() ?? {}; - data.image = data.image?.replace(/\?.*$/, '') ?? undefined; + article.image = article.image?.replace(/\?.*$/, ''); - const { data: currentResponse } = await got(currentUrl); + const currentResponse = await fetchPageWithChallenge(currentUrl); const $ = load(currentResponse); @@ -84,38 +123,32 @@ async function handler(ctx) { const descriptionHtml = $('div.xml').html(); const image = new URL($('li.active a img').prop('src'), rootUrl).href; const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; - - const items = data - ? [ - { - title: `${data.title} ${data.releaseTime}`, - link: new URL(data.link, rootUrl).href, - description: renderToString( - <> - {data.image ? ( - <figure> - <img src={new URL(data.image, rootUrl).href} alt={data.title} /> - </figure> - ) : null} - {descriptionHtml ? raw(descriptionHtml) : null} - </> - ), - author: - $( - $('div.col-xs-8 span') - .toArray() - .findLast((a) => $(a).text().startsWith('来源')) - ) - ?.text() - ?.split(/:/) - ?.pop() || author, - guid: `cma${data.link}#${data.releaseTime.replaceAll(/\s/g, '-')}`, - pubDate: timezone(parseDate(data.releaseTime), 8), - enclosure_url: new URL(data.image, rootUrl).href, - enclosure_type: data.image ? `image/${data.image.split(/\./).pop()}` : undefined, - }, - ] - : []; + const sourceElement = $('div.col-xs-8 span') + .toArray() + .findLast((element) => $(element).text().startsWith('来源')); + const itemAuthor = $(sourceElement).text().split(':').pop() || author; + + const items = [ + { + title: `${article.title} ${article.releaseTime}`, + link: new URL(article.link, rootUrl).href, + description: renderToString( + <> + {article.image ? ( + <figure> + <img src={new URL(article.image, rootUrl).href} alt={article.title} /> + </figure> + ) : null} + {descriptionHtml ? raw(descriptionHtml) : null} + </> + ), + author: itemAuthor, + guid: `cma${article.link}#${article.releaseTime.replaceAll(/\s/g, '-')}`, + pubDate: timezone(parseDate(article.releaseTime), 8), + enclosure_url: new URL(article.image, rootUrl).href, + enclosure_type: article.image ? `image/${article.image.split(/\./).pop()}` : undefined, + }, + ]; return { item: items, From 5a01d63f9dd0bb34c1e11ccb71c02d4fb752e733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 08:13:06 +0000 Subject: [PATCH 0443/1190] chore(deps): bump docker/login-action from 4.5.1 to 4.5.2 (#22855) Bumps [docker/login-action](https://github.com/docker/login-action) from 4.5.1 to 4.5.2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/abd2ef45e78c5afb21d64d4ca52ee8550d9572c7...371161bbe7024a29a25c5e19bfcbc0804fe9ad2c) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.5.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index acfec3b54d01..d8fae1294d3d 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -74,13 +74,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 + uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 + uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 with: registry: ghcr.io username: ${{ github.actor }} @@ -207,13 +207,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 + uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 + uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 with: registry: ghcr.io username: ${{ github.actor }} From 4b9655846d23e841a3b5ba08b49cab6b18c576b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 08:16:18 +0000 Subject: [PATCH 0444/1190] chore(deps-dev): bump @types/node from 26.1.1 to 26.1.2 (#22858) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.1.1 to 26.1.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.1.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 136 ++++++++++++++++++++++++------------------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index 32db4724b36b..3b60aaddf82e 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "@types/mailparser": "3.4.6", "@types/markdown-it": "14.1.2", "@types/module-alias": "2.0.4", - "@types/node": "26.1.1", + "@types/node": "26.1.2", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.65.0", "@typescript-eslint/parser": "8.65.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21b2b5c23575..02101cf77013 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -351,8 +351,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.1.1 - version: 26.1.1 + specifier: 26.1.2 + version: 26.1.2 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -379,7 +379,7 @@ importers: version: 10.8.0 eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.1.1)(eslint@10.8.0) + version: 9.1.1(@types/node@26.1.2)(eslint@10.8.0) eslint-plugin-n: specifier: 18.2.2 version: 18.2.2(@typescript/typescript6@6.0.2)(eslint@10.8.0) @@ -418,7 +418,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.9.0)(utf-8-validate@5.0.10) @@ -466,10 +466,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.1 - version: 7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.114.0 version: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -2770,8 +2770,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.1.1': - resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} + '@types/node@26.1.2': + resolution: {integrity: sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -3697,7 +3697,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.51: @@ -6664,7 +6664,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -7134,76 +7134,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.1.1)': + '@inquirer/checkbox@4.3.2(@types/node@26.1.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.1.1) + '@inquirer/core': 10.3.2(@types/node@26.1.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.1.1) + '@inquirer/type': 3.0.10(@types/node@26.1.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/confirm@5.1.21(@types/node@26.1.1)': + '@inquirer/confirm@5.1.21(@types/node@26.1.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.1.1) - '@inquirer/type': 3.0.10(@types/node@26.1.1) + '@inquirer/core': 10.3.2(@types/node@26.1.2) + '@inquirer/type': 3.0.10(@types/node@26.1.2) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/confirm@6.1.1(@types/node@26.1.1)': + '@inquirer/confirm@6.1.1(@types/node@26.1.2)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.1.2) + '@inquirer/type': 4.0.7(@types/node@26.1.2) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/core@10.3.2(@types/node@26.1.1)': + '@inquirer/core@10.3.2(@types/node@26.1.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.1.1) + '@inquirer/type': 3.0.10(@types/node@26.1.2) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/core@11.2.1(@types/node@26.1.1)': + '@inquirer/core@11.2.1(@types/node@26.1.2)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.2) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.1.1)': + '@inquirer/select@4.4.2(@types/node@26.1.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.1.1) + '@inquirer/core': 10.3.2(@types/node@26.1.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.1.1) + '@inquirer/type': 3.0.10(@types/node@26.1.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/type@3.0.10(@types/node@26.1.1)': + '@inquirer/type@3.0.10(@types/node@26.1.2)': optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 - '@inquirer/type@4.0.7(@types/node@26.1.1)': + '@inquirer/type@4.0.7(@types/node@26.1.2)': optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@ioredis/commands@1.10.0': {} @@ -8146,7 +8146,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/caseless@0.12.5': {} @@ -8159,7 +8159,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/crypto-js@4.2.2': {} @@ -8180,11 +8180,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/express-serve-static-core@5.1.2': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8198,7 +8198,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/html-to-text@9.0.4': {} @@ -8210,7 +8210,7 @@ snapshots: '@types/jsdom@28.0.3': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/tough-cookie': 4.0.5 parse5: 8.0.1 undici-types: 7.28.0 @@ -8221,7 +8221,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/jsrsasign@10.5.15': {} @@ -8229,7 +8229,7 @@ snapshots: '@types/mailparser@3.4.6': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 iconv-lite: 0.6.3 '@types/markdown-it@14.1.2': @@ -8247,7 +8247,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.1.1': + '@types/node@26.1.2': dependencies: undici-types: 8.3.0 @@ -8263,7 +8263,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8273,16 +8273,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@types/statuses@2.0.6': {} @@ -8482,7 +8482,7 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -8493,14 +8493,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) + msw: 2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -9277,12 +9277,12 @@ snapshots: eslint: 10.8.0 optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.1.1)(eslint@10.8.0): + eslint-nibble@9.1.1(@types/node@26.1.2)(eslint@10.8.0): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.1.1) - '@inquirer/confirm': 5.1.21(@types/node@26.1.1) - '@inquirer/select': 4.4.2(@types/node@26.1.1) + '@inquirer/checkbox': 4.3.2(@types/node@26.1.2) + '@inquirer/confirm': 5.1.21(@types/node@26.1.2) + '@inquirer/select': 4.4.2(@types/node@26.1.2) eslint: 10.8.0 eslint-filtered-fix: 0.3.0(eslint@10.8.0) optionator: 0.9.4 @@ -10695,9 +10695,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.1.1) + '@inquirer/confirm': 6.1.1(@types/node@26.1.2) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -11160,7 +11160,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.1.1 + '@types/node': 26.1.2 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -12035,17 +12035,17 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: debug: 4.4.3 oxc-resolver: 11.24.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0): + vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) @@ -12054,16 +12054,16 @@ snapshots: rollup: 4.62.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.1.2 fsevents: 2.3.3 lightningcss: 1.32.0 tsx: 4.23.1 yaml: 2.9.0 - vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -12080,11 +12080,11 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@26.1.1)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.1.1 + '@types/node': 26.1.2 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) jsdom: 30.0.0(@noble/hashes@2.2.0) transitivePeerDependencies: From b047fcde17458d677e575a455529c9dc3ebbeacd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 08:17:48 +0000 Subject: [PATCH 0445/1190] chore(deps-dev): bump discord-api-types from 0.38.51 to 0.38.52 (#22859) Bumps [discord-api-types](https://github.com/discordjs/discord-api-types) from 0.38.51 to 0.38.52. - [Release notes](https://github.com/discordjs/discord-api-types/releases) - [Changelog](https://github.com/discordjs/discord-api-types/blob/main/CHANGELOG.md) - [Commits](https://github.com/discordjs/discord-api-types/compare/0.38.51...0.38.52) --- updated-dependencies: - dependency-name: discord-api-types dependency-version: 0.38.52 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3b60aaddf82e..f2e2874f5106 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,7 @@ "@typescript-eslint/parser": "8.65.0", "@vercel/nft": "1.10.2", "@vitest/coverage-v8": "4.1.10", - "discord-api-types": "0.38.51", + "discord-api-types": "0.38.52", "domhandler": "6.0.1", "eslint": "10.8.0", "eslint-nibble": "9.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02101cf77013..e40a92971803 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -369,8 +369,8 @@ importers: specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) discord-api-types: - specifier: 0.38.51 - version: 0.38.51 + specifier: 0.38.52 + version: 0.38.52 domhandler: specifier: 6.0.1 version: 6.0.1 @@ -3700,8 +3700,8 @@ packages: resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 - discord-api-types@0.38.51: - resolution: {integrity: sha512-SnaFHd+b8Z3HgjEIoSA1wkstXCt6J0Zrxvny0BJZZz7AhmFSetRY3DMrIdO3LFDMeeTxF7WKornnAGxaws5Adw==} + discord-api-types@0.38.52: + resolution: {integrity: sha512-uwe9EKfbjsmgWc2fdFjvDbj+dQqx3lp7wqDCmIha0jInuU+xeQjkCK9tMMn+p7RXfdVQORCInq4cD3U2ymDmyg==} dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -9044,7 +9044,7 @@ snapshots: dependencies: heap: 0.2.7 - discord-api-types@0.38.51: {} + discord-api-types@0.38.52: {} dom-serializer@1.4.1: dependencies: From 3cda7a271729bbae8e6a1016a28fecd43011a8fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:01:27 +0800 Subject: [PATCH 0446/1190] chore(deps): bump actions/stale from 10.4.0 to 11.0.0 (#22854) Bumps [actions/stale](https://github.com/actions/stale) from 10.4.0 to 11.0.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/1e223db275d687790206a7acac4d1a11bd6fe629...4391f3da665fdf50b6810c1a66712fb9ba21aa93) --- updated-dependencies: - dependency-name: actions/stale dependency-version: 11.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 635880db5a54..023ed79686de 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: stale: runs-on: ubuntu-slim steps: - - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 + - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 with: # Don't stale issues days-before-issue-stale: -1 @@ -28,7 +28,7 @@ jobs: any-of-issue-labels: 'more data required' - name: Close Broken PRs - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 + uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 with: days-before-issue-stale: -1 days-before-issue-close: -1 From c849a396de8e64a22781104af1719f54aefd8dab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:04:08 +0800 Subject: [PATCH 0447/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 2 updates (#22856) Bumps the cloudflare group with 2 updates in the / directory: [@cloudflare/playwright](https://github.com/cloudflare/playwright/tree/HEAD/packages/playwright-cloudflare) and [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/playwright` from 1.3.0 to 1.3.2 - [Release notes](https://github.com/cloudflare/playwright/releases) - [Commits](https://github.com/cloudflare/playwright/commits/v1.3.2/packages/playwright-cloudflare) Updates `@cloudflare/workers-types` from 5.20260727.1 to 5.20260728.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/playwright" dependency-version: 1.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260728.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 ++-- pnpm-lock.yaml | 39 ++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index f2e2874f5106..5a55cc9dd4e6 100644 --- a/package.json +++ b/package.json @@ -147,9 +147,9 @@ "@actions/github": "9.1.1", "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", - "@cloudflare/playwright": "1.3.0", + "@cloudflare/playwright": "1.3.2", "@cloudflare/vitest-pool-workers": "0.18.8", - "@cloudflare/workers-types": "5.20260727.1", + "@cloudflare/workers-types": "5.20260728.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.75.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e40a92971803..e85702bad3d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,14 +291,14 @@ importers: specifier: 0.3.7 version: 0.3.7 '@cloudflare/playwright': - specifier: 1.3.0 - version: 1.3.0 + specifier: 1.3.2 + version: 1.3.2 '@cloudflare/vitest-pool-workers': specifier: 0.18.8 - version: 0.18.8(@cloudflare/workers-types@5.20260727.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.18.8(@cloudflare/workers-types@5.20260728.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260727.1 - version: 5.20260727.1 + specifier: 5.20260728.1 + version: 5.20260728.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.114.0 - version: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -585,8 +585,13 @@ packages: resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} engines: {node: '>=22.0.0'} - '@cloudflare/playwright@1.3.0': - resolution: {integrity: sha512-WOlpXzfJ7noXmW8qE/qlU7MbVHyt9IzxxwPcTGeh2+YQS1gqNIe3fl99AZvBsheH0o7oCePYtL9wRV1KPCYghQ==} + '@cloudflare/playwright@1.3.2': + resolution: {integrity: sha512-aADRLAUSxOn4jlwIc9YEE08oXJtjvzH/Nw5vyQtUncn2YboEWElk63/oWq2RAaUlidkMco81VzRhfk2jxMzhKQ==} + peerDependencies: + playwright-core: '*' + peerDependenciesMeta: + playwright-core: + optional: true '@cloudflare/unenv-preset@2.16.1': resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} @@ -634,8 +639,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260727.1': - resolution: {integrity: sha512-b/wT+LMZz0oELzxibww0ujFz5BD8NRz9WJ+xd+JNZJUMXgh8IHjpibKdGDvtkbotmihWUknP5tBPUU8KluLxxA==} + '@cloudflare/workers-types@5.20260728.1': + resolution: {integrity: sha512-rZqmesLEH40Xt67PpQSj+iJqwkYBzVr7U/UOa5twkToMsxO5pYsPw2QAaTKDdiCSiki9fWAgsWTj9OmFnbwLRQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3697,7 +3702,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.52: @@ -6649,7 +6654,7 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/playwright@1.3.0': {} + '@cloudflare/playwright@1.3.2': {} '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1)': dependencies: @@ -6657,7 +6662,7 @@ snapshots: optionalDependencies: workerd: 1.20260722.1 - '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260727.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260728.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6665,7 +6670,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6687,7 +6692,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260722.1': optional: true - '@cloudflare/workers-types@5.20260727.1': {} + '@cloudflare/workers-types@5.20260728.1': {} '@colors/colors@1.6.0': {} @@ -12181,7 +12186,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260722.1 '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.114.0(@cloudflare/workers-types@5.20260727.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) @@ -12192,7 +12197,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260722.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260727.1 + '@cloudflare/workers-types': 5.20260728.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From cc57953a64f5d34a26d0bd44f9e1c514ac981f35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:25:06 +0800 Subject: [PATCH 0448/1190] chore(deps): bump imapflow from 1.5.0 to 1.6.1 (#22860) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.5.0 to 1.6.1. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.5.0...v1.6.1) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 5a55cc9dd4e6..41a269112811 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.5.0", + "imapflow": "1.6.1", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e85702bad3d2..dc58bf7ae854 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.5.0 - version: 1.5.0 + specifier: 1.6.1 + version: 1.6.1 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -3702,7 +3702,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.52: @@ -4374,8 +4374,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.5.0: - resolution: {integrity: sha512-ayj2xIpRpXT9nXlAQQDhfm694faQxEmfAzRYL881q3YGRR5ofyIWsG3l3Lf7oSThxgLwZ/EQ5kh/nLnKBud3LQ==} + imapflow@1.6.1: + resolution: {integrity: sha512-lBkh0ZcKeYHDS3sWfxEcD7j24frrrn4wtbe0UUtLqdLwMSvm9Mygx8j7ZtzWgnvqI7V1DkNfcsidJ/qasb7FBA==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -4419,8 +4419,8 @@ packages: resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==} engines: {node: '>=12.22.0'} - ip-address@10.2.0: - resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + ip-address@10.3.1: + resolution: {integrity: sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==} engines: {node: '>= 12'} ip-regex@4.3.0: @@ -9878,7 +9878,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.5.0: + imapflow@1.6.1: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 @@ -9886,7 +9886,6 @@ snapshots: libbase64: 1.3.0 libmime: 5.4.1 libqp: 2.1.1 - nodemailer: 9.0.3 pino: 10.3.1 socks: 2.8.9 @@ -9954,7 +9953,7 @@ snapshots: transitivePeerDependencies: - supports-color - ip-address@10.2.0: {} + ip-address@10.3.1: {} ip-regex@4.3.0: {} @@ -11563,7 +11562,7 @@ snapshots: socks@2.8.9: dependencies: - ip-address: 10.2.0 + ip-address: 10.3.1 smart-buffer: 4.2.0 sonic-boom@4.2.1: From 3c160afa528cc384f860dc537f3251b4b4260a0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:31:42 +0800 Subject: [PATCH 0449/1190] chore(deps): bump devenv from `6319d63` to `fa2b4dd` (#22861) Bumps [devenv](https://github.com/cachix/devenv) from `6319d63` to `fa2b4dd`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/6319d63344675f65c2c213be95084544866d8951...fa2b4ddf117bb32add8fb53ebed8d5df74f6afae) --- updated-dependencies: - dependency-name: devenv dependency-version: fa2b4ddf117bb32add8fb53ebed8d5df74f6afae dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 556fea0436dd..090e61fd4e0a 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1785127153, - "narHash": "sha256-vX6OVIUWiEiD5v1InHQk1OTjJsK2WgEqWmMPeIS5l78=", + "lastModified": 1785216906, + "narHash": "sha256-0wbVad9KwbSaKlMAFbuI9g3dwOWWhBrLuwYE8y7Y2qc=", "owner": "cachix", "repo": "devenv", - "rev": "6319d63344675f65c2c213be95084544866d8951", + "rev": "fa2b4ddf117bb32add8fb53ebed8d5df74f6afae", "type": "github" }, "original": { From 1a0f4cdec08128486ffdc6bde7be7e80e53dbb11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 21:59:19 +0800 Subject: [PATCH 0450/1190] chore(deps-dev): bump the oxc group across 1 directory with 5 updates (#22857) Bumps the oxc group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.75.0` | `1.76.0` | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.141.0` | `0.142.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.60.0` | `0.61.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.75.0` | `1.76.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.75.0` | `1.76.0` | Updates `@oxlint/plugins` from 1.75.0 to 1.76.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.76.0/npm/oxlint-plugins) Updates `oxc-parser` from 0.141.0 to 0.142.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.142.0/napi/parser) Updates `oxfmt` from 0.60.0 to 0.61.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.61.0/npm/oxfmt) Updates `oxlint` from 1.75.0 to 1.76.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.76.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.75.0 to 1.76.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.76.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.76.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.142.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.61.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.76.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.76.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +- pnpm-lock.yaml | 563 +++++++++++++++++++++++++------------------------ 2 files changed, 291 insertions(+), 282 deletions(-) diff --git a/package.json b/package.json index 41a269112811..bc8fb93a8d2a 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "@cloudflare/workers-types": "5.20260728.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.75.0", + "@oxlint/plugins": "1.76.0", "@stylistic/eslint-plugin": "5.10.0", "@types/babel__preset-env": "7.10.0", "@types/crypto-js": "4.2.2", @@ -191,10 +191,10 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.141.0", - "oxfmt": "0.60.0", - "oxlint": "1.75.0", - "oxlint-plugin-eslint": "1.75.0", + "oxc-parser": "0.142.0", + "oxfmt": "0.61.0", + "oxlint": "1.76.0", + "oxlint-plugin-eslint": "1.76.0", "oxlint-tsgolint": "7.0.2001", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc58bf7ae854..f65595c58b81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,8 +306,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.8.0) '@oxlint/plugins': - specifier: 1.75.0 - version: 1.75.0 + specifier: 1.76.0 + version: 1.76.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.8.0) @@ -423,17 +423,17 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.9.0)(utf-8-validate@5.0.10) oxc-parser: - specifier: 0.141.0 - version: 0.141.0 + specifier: 0.142.0 + version: 0.142.0 oxfmt: - specifier: 0.60.0 - version: 0.60.0 + specifier: 0.61.0 + version: 0.61.0 oxlint: - specifier: 1.75.0 - version: 1.75.0(oxlint-tsgolint@7.0.2001) + specifier: 1.76.0 + version: 1.76.0(oxlint-tsgolint@7.0.2001) oxlint-plugin-eslint: - specifier: 1.75.0 - version: 1.75.0 + specifier: 1.76.0 + version: 1.76.0 oxlint-tsgolint: specifier: 7.0.2001 version: 7.0.2001 @@ -704,6 +704,9 @@ packages: '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} @@ -1447,11 +1450,12 @@ packages: resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} engines: {node: '>=18'} - '@napi-rs/wasm-runtime@1.1.6': - resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + '@napi-rs/wasm-runtime@1.2.0': + resolution: {integrity: sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 + '@emnapi/core': ^2.0.0-alpha.3 + '@emnapi/runtime': ^2.0.0-alpha.3 '@noble/hashes@2.2.0': resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} @@ -1642,129 +1646,129 @@ packages: '@otplib/uri@13.4.1': resolution: {integrity: sha512-xaIm7bvICMhoB2rZIR5luiaMdssWR5nY5nXnR1fdezUgZuEO58D6zrGzLp7pQuBmlpmL0HagnscDQFoskp9yiA==} - '@oxc-parser/binding-android-arm-eabi@0.141.0': - resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} + '@oxc-parser/binding-android-arm-eabi@0.142.0': + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.141.0': - resolution: {integrity: sha512-a4XDQ27ZT7e7zwAlxJDTiCA7IBGWDuy2+MhFq85Of7XlBSmpkfcBFml11q0Zx6f7RMuI0B4xCtt2ytBS4yOptg==} + '@oxc-parser/binding-android-arm64@0.142.0': + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.141.0': - resolution: {integrity: sha512-m/kVk6rzYmBeHYnz+1Y5fod00AVTTxMbC71azFfm/zjx1j9XxwKtA0+VfkKuVMC8rbghb9TtfevnuWZa9OuPEg==} + '@oxc-parser/binding-darwin-arm64@0.142.0': + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.141.0': - resolution: {integrity: sha512-o0X+6KZlfucWU/v5oKRQPwdFXsXAjW8jmpo/Gpw/qyKsbKtlfkHoeH9Bjp/m13TwjewvJnCkwF0DWzgpC4HjTQ==} + '@oxc-parser/binding-darwin-x64@0.142.0': + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.141.0': - resolution: {integrity: sha512-W5KbTnNkTMMMylqj6dYqnsXvkmESVPodPKYLJ5zdzIPdl9fUJtolkpUeSzYEbGGYB4a4A4avl3EePnZ/wLIdJg==} + '@oxc-parser/binding-freebsd-x64@0.142.0': + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': - resolution: {integrity: sha512-g3dtbJa8zeOGK36Sr9cQavsdi5H/ie2hVjrSjIxsNAR1qZA40ZYVXnfdfoMAlq8CmB9qFL1yhsSCUHeNmdmt8w==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': - resolution: {integrity: sha512-e6hwQqd+3lvP13G2jxvFpoA7dzHcFLN+Mq47JCVMtdNHbbyBRo756JCtbbJH6ca8inTfyqZoqBmS3vhQlzAK2w==} + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.141.0': - resolution: {integrity: sha512-vXz2BLAuypA+4MLyBg94pzEo6THVnzYnCtAjXoihIIQo0t2pnp/AmW+SH1EI+4VbuJnC//KplIJ5yyaCGua4jA==} + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.141.0': - resolution: {integrity: sha512-jMkS/EztNW34HKsXIaT/SoHcmtocq/vWhwFOVduF9kduuuRIVwfwQ6uxzIO+qPKSXdd2TXt54of0BJ2zFMXnmw==} + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': - resolution: {integrity: sha512-vo+MR+n3zQJ6Mq92hiP084NZcgDv5iJlVR02gMf28neMvVT1tKVm7VeiW/DxhdqOi3QLeaXIk9cUcLL1qrkngw==} + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': - resolution: {integrity: sha512-oh80w+7RuiO5gBp9Jnoa/H8Qlt3JsHL2MkW+0dwEdlDMdslVZX/YsekSK6EeyEenY66/mhCfypsNATQ7Ph3qlQ==} + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.141.0': - resolution: {integrity: sha512-LOyEmFA8sCnYbEXP1+iQvCC/P1YXHMA/t6x1Ksp0Y9VwhLFsiBJFzV1zIxrOIE2LKaGGhDjQ29xq9cbq6omDXA==} + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.141.0': - resolution: {integrity: sha512-3wnwk/l1CvszVE5TJR1wSl/zSEfydRqrNhn6s7Vr9IzSJpUQIroqVsIoPARHRFA+FQwkxAFDAHDAasa7v8OobQ==} + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.141.0': - resolution: {integrity: sha512-qtyQVAAebFq57B2tifTlel3TgGqUtsYNI/e+p6aya9rN9lOZVTDvr215fGYSA9XWooxzMxDiVxkBLk2jQHbsOQ==} + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.141.0': - resolution: {integrity: sha512-SkGV1nKw40roEc94pv5EaaeH2ay14G6+roe8Q0wIUC1LcEKxzKW921h7+ZuZX0D3q2Mb/7aSFmxEVqnko3lPRw==} + '@oxc-parser/binding-linux-x64-musl@0.142.0': + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.141.0': - resolution: {integrity: sha512-cVgDM7n8QziQqOaP5hNgUYfMG7S/ZeuPxFWXnnHRv7rh025COk0rfQ6eEdKG3j/GaUuyvNZN4ifF1J8KmuXLLA==} + '@oxc-parser/binding-openharmony-arm64@0.142.0': + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.141.0': - resolution: {integrity: sha512-HggH++Fkn3OilBn+bs3jpgIFQa34oMAyUUHy0vpGum+gt1Eb5nyLc8dNU/RAPSw6lsLrx7ncKtHSZE+3Sp0l2g==} + '@oxc-parser/binding-wasm32-wasi@0.142.0': + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.141.0': - resolution: {integrity: sha512-KLSEH9GwgbrqbJOjtGHt9STw96s+78yDzp7IDN8Lno+7Ut9sNBfZ4jYZIz4mD50qmWUjoOI7i9I6UENbhNbMZQ==} + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.141.0': - resolution: {integrity: sha512-9UVWUOOCI/1YkiSSNjg2zyBJYM9E/t1A/8GNobd48JDn/fQ6mzxcVO3H08jb3rAaW/B1VBf8eCORTvSsO9T08g==} + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.141.0': - resolution: {integrity: sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg==} + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1775,8 +1779,8 @@ packages: '@oxc-project/types@0.140.0': resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} - '@oxc-project/types@0.141.0': - resolution: {integrity: sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ==} + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} @@ -1881,124 +1885,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.60.0': - resolution: {integrity: sha512-1q4q4Jc8FlOMVojEisyFAVyl8h1yawNv6phjgmhGVEDeyeOdsSnSr9x0+D4mOnEKvpO5L4mxKZ/DP9X6U3A/Mw==} + '@oxfmt/binding-android-arm-eabi@0.61.0': + resolution: {integrity: sha512-BaS+1OVvg9sr+Xav0+KdWedQRcAzrdoEcwMZeqoc2F6ieC1s/t5eM35YQoRPQ7vAqkZ+p3tbQb1r9I9mrV5oGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.60.0': - resolution: {integrity: sha512-tD41I6nCt9k8SQXft0CSjjU9jg6SwG7uMu7PxodSEHXl+GDW0868oy6tTtoJkyUze8YKFgTpz/k5LuPUnFiGLw==} + '@oxfmt/binding-android-arm64@0.61.0': + resolution: {integrity: sha512-of8atAV0M1egGcVOMbgZCvc10sFOP3ayQBNQV5h5G3fNq8gACdEswfFk9bzGrdbM23rtg0Coxi7np7oPLcueNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.60.0': - resolution: {integrity: sha512-TTpzPug96Zxdyb46KvTyIUQDdsqbumXh2TKG9C23PCT0kF7JkW56Z/quPuG9rqOFKQIi1gpRNZ7DX18LwxXPnw==} + '@oxfmt/binding-darwin-arm64@0.61.0': + resolution: {integrity: sha512-7l8+5ov4BGwtAcmpzvEik/TG3bciwyw/S3e6j5GKH7pcQqcgCVxD3AuJeP6upto+SOTBKQ4wrrdbMt0gq8fHSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.60.0': - resolution: {integrity: sha512-CnOoWgQ7L+JL/YQaRJ+NyATciSfcftncm7y3kqyte1cGtFEGnStaCd1TAyrinkfQ7nRBfHrTs1/vTwUJr3WF2Q==} + '@oxfmt/binding-darwin-x64@0.61.0': + resolution: {integrity: sha512-Fnz4dDDXBb7udk+DmwelNjxbD6yptyxwCqwCH2ebo4RVLxVsRfFsn/AHJC49KIltPrVokamGv4SSOsiV50DTxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.60.0': - resolution: {integrity: sha512-ychJo7S3hZxdO6eDZ9zM6F2lM9fpJS3EKS5CAUSWyprdLYxTu4gbaUKV/VBPTcMJwQa2Bpo+643y3OJ537pihA==} + '@oxfmt/binding-freebsd-x64@0.61.0': + resolution: {integrity: sha512-mddOebKNCP+AucmzfNsk3jgbr681qAUvgMqi865GW5gWLJ/AnzXbvjQRrny0e++NAN8aphav/aRSrfFxNsNjpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': - resolution: {integrity: sha512-36IH5o55T2Fx7E0feDttt+mifxN6yk9pWv4KfhAIsP0dFnUq27331OwbpOsZdoXF9soOLWm7mQUz5+UUmyec4g==} + '@oxfmt/binding-linux-arm-gnueabihf@0.61.0': + resolution: {integrity: sha512-svx59iYL+DbaZGZUIoice4W0CjRXGExnbz7Re+awIb60rVxBS2KrU7Hnlx+nZYanLGLpjneUEgo/VFEKkSZAyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.60.0': - resolution: {integrity: sha512-G1Ve7lAa6sFBolVI2LWHfEAqy0YKh4vnioH8uYO9kAEdgM7mR40IksIx9/Zk4+vbYew/sGa4J9Q4tZ3n9gXDHA==} + '@oxfmt/binding-linux-arm-musleabihf@0.61.0': + resolution: {integrity: sha512-BYK9MPJPCf6d+fLKMTruThmEyCtHzQ1zLcsrTlUVkmnoXIaHAbfpeLYQwX1tkjs7W11dyzoi6HFvKcdnvX1zNg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.60.0': - resolution: {integrity: sha512-LTQdRBf6uzj/h7Xk6lKzbGD2hrF/fK4YI9LIN1c0509tPUn8wRa3mCmrFQpEWJPLYGFrLFFMTYW1Ljj6VqW2Hw==} + '@oxfmt/binding-linux-arm64-gnu@0.61.0': + resolution: {integrity: sha512-QUaCNLq2/EC6G5ljOuFanl9Lgw6ZWp4co7rs4+KOMUzbGfA4Lq58FHRjjF9sVIG+93XSbo343MxFATrOU1qctA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.60.0': - resolution: {integrity: sha512-2JMo3XPxMPx3hiqddSZYyaH+fKJm6cz0u8n1naYjP/CdOQOZW34i8lKBUfmbWiuFvd6KoYXLmhAyBuvojsYS7Q==} + '@oxfmt/binding-linux-arm64-musl@0.61.0': + resolution: {integrity: sha512-S6uvJ6MXnRXl+zTs0CARNDvkE+cymj0EVWEKKsyKnlLlqTyQJBjw5s4D2pSIOZc+S46cy4STefzcr/sm0VzVPA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.60.0': - resolution: {integrity: sha512-L3C+nBD13lr306tr/PjM3RMll+BVqgFrIgUyoeHuai5oueJrRLgO3j+GO5/Cbhtkf5PSlHYTI1JY7iqBd1qa6A==} + '@oxfmt/binding-linux-ppc64-gnu@0.61.0': + resolution: {integrity: sha512-6VDlRcytvZG6UlSIdAFKDLbppo9tvPxrWzle6vHldYFMeuDPQEfMKrkwezp7FaBq1wik9ra554ZZeRPsyIkFpg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.60.0': - resolution: {integrity: sha512-M4MsmvqlxFiPtSRGyBYQSZxchEf463AOyd+Dh4/9xDpjWBsRtDUTDMFN5EdHinjVK1/eDJQ8MLpcYjpYayaCnA==} + '@oxfmt/binding-linux-riscv64-gnu@0.61.0': + resolution: {integrity: sha512-KkBTYbzExpbmn15XjKPLu2fRV2PVlq+KWt+brad5rwIa03vdYoaDRWiS7raHII/dCTR6Ro4UpYUCH4t6lif4WQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.60.0': - resolution: {integrity: sha512-OH+9UskYuxRB+GxqdGkVN8f5UpwhqG8YscNo1wl8+KJ62cd7wZdGga6iGLJIf8kibF1WBwvlfDUx3cez/VXwFg==} + '@oxfmt/binding-linux-riscv64-musl@0.61.0': + resolution: {integrity: sha512-69tzIq7sJLVB9dxYYtvMzcSSsnZHSO+U2U19O2RqDqgj6+Q4O7HjSXdaszbcgqzhsUwzSH7z5kWvk8nmf6BHTg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.60.0': - resolution: {integrity: sha512-y7AAFutt9wFWBFOAn6+BHaV39usZmcr3YYH2385f+NHgPNpIF9HpqKp0jgUxPaUOCyG3oaX5VhJduL1Nw164rw==} + '@oxfmt/binding-linux-s390x-gnu@0.61.0': + resolution: {integrity: sha512-Oqi/N0OvtOVXsPKAOOhKgGH3msRYF8BLJaNBbWiupRiKoKVyc8JRKPCfarkQJC+RgP9U8raUKLe+bNwd0HUMiA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.60.0': - resolution: {integrity: sha512-yKZ9+CXAI+1RO5nH/4Z/9M6DAsfOzd5bw/gtWk81KB4mpalMaRRSXfouc5/tHxazDmBek55HNPepNYBgaCew0Q==} + '@oxfmt/binding-linux-x64-gnu@0.61.0': + resolution: {integrity: sha512-3TKwv/ed4uwJSemAA8P9XcoqETpjQI4waquF9UilhA9Mn/dhr1PdUEXWlL74mtc6ZNfmKPA9+NEJm01nRF8CVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.60.0': - resolution: {integrity: sha512-bCUGaF6hJOYnQzLJdHLZbvGsOd5oSvGAyJhPAKum2uyLYUuXmP8vqg690DWi2hqcnIoYpqSqCrjzE5aiUAgwQg==} + '@oxfmt/binding-linux-x64-musl@0.61.0': + resolution: {integrity: sha512-uFso4u4nLkVSlMCpgjyvWV60Gt7GvDQHnk1mmRxHIkZTMB0ljpUKwCD9FYGgN9H97x2wYl0UwEjgRZaPIuhEhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.60.0': - resolution: {integrity: sha512-GrUeZOvzP30ExxfCuQiyofuUGI+OmvAgFwOO5w5p9mGPlxcyuqI+6Sy9fAKFFfLQrqKYWFgc5sYA2Unj/29nPg==} + '@oxfmt/binding-openharmony-arm64@0.61.0': + resolution: {integrity: sha512-keGLkzeOvkMpNmPp4hffXWpfoSsY6e1K8++KXD4mSSfxdvM8q9QUDsYY689TB1k6Co832DZn1MnaaVx6cIBMWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.60.0': - resolution: {integrity: sha512-WD4Q954kUl2TDJV/6q7UnE2rlKk047kXLJsr4bJ2mXRaAqNXcmV3nwKUsGCc3mz/jYDBnXtJEaBErJEybK8iQQ==} + '@oxfmt/binding-win32-arm64-msvc@0.61.0': + resolution: {integrity: sha512-VzsAISkFxmNhJ5LBDEL9VuH6tJsVJMtqYit2LyIUf/HLnsCe4Pg9SMOjjVQzGWt0bnpyfJ94CrqTqcpNZzK+ug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.60.0': - resolution: {integrity: sha512-HqDekjr8JXzVDUP1YthDZ1Y3CBEcuZT4WX3B+1kaxj8CvZA8Y2YhcEsXqoSop3tVsgjACxjnFQFDkBo0r/jq1Q==} + '@oxfmt/binding-win32-ia32-msvc@0.61.0': + resolution: {integrity: sha512-xv4t7yzwJoYaLB6Zv28B3W+j7brEjsyv50rLTAQgmxJzddce9fAMCxed8dSAkbWES0zz2J29nYK5FaTuD2YBHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.60.0': - resolution: {integrity: sha512-tz78yhmGPKboTMHCHSaUqXK8JrmoSejgDcWeqAtg2s07ZGKQ3rH5Jn8NuXPGNG33CDbY2e9NoQWXIVEmKO21Rw==} + '@oxfmt/binding-win32-x64-msvc@0.61.0': + resolution: {integrity: sha512-6EZXFkqOwxdDYjIn3TSNnPk3ST5E5GiYd4FiM6UF/mCL/LZSfr6D6UygTfW3R1PCQP2quCKpCEGRlij8E3VYbg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2033,130 +2037,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.75.0': - resolution: {integrity: sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==} + '@oxlint/binding-android-arm-eabi@1.76.0': + resolution: {integrity: sha512-ZHIE5Zt9AsPDcY4nOlofXt0YfneEeo+QrKMPcPzLf2Z6Q8VtV2W73d7SFJ920WUwyik783u/doKCs3KXdwG+7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.75.0': - resolution: {integrity: sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw==} + '@oxlint/binding-android-arm64@1.76.0': + resolution: {integrity: sha512-shm/ngQilHK6bs+ElJWa4oHfNj5vL1Gl/iVEJldTQjpr0/67oSgr0KUpbmcnLig5Fo0v/l6j2567A7TOL89ONA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.75.0': - resolution: {integrity: sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ==} + '@oxlint/binding-darwin-arm64@1.76.0': + resolution: {integrity: sha512-rvJmrAPKSQ9aWJ6wIS6CK2tJjwzfW0ApQH9qokq6sfDvmHwoyIHxHFMq7z7i7GiV6fdE6s8qvBqWKPTu8RmT6Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.75.0': - resolution: {integrity: sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ==} + '@oxlint/binding-darwin-x64@1.76.0': + resolution: {integrity: sha512-U/zYdb7VYKGY6pA9Vd2rYl9O/HlCylcOlb5PGPvVLtg+oLGsk6H3XGKEMHKyqD3nmmtmlmwb/8SwU2vfSAtvMw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.75.0': - resolution: {integrity: sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg==} + '@oxlint/binding-freebsd-x64@1.76.0': + resolution: {integrity: sha512-WvKG9CAriuo0XNiFzpXjDngUZcRGFNpaK2kLyMUsnJlShxkT96u+BpJQ3KqdQwGOrvI14L6V8bAwXwAYNNY6Jg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.75.0': - resolution: {integrity: sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg==} + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': + resolution: {integrity: sha512-qJ5+RH99TqFRq3UCDxkW0zJJu9c+OAHFY72vGlxZLEpuO+MpKo3POgqb8sYipL9KYm8XY6ofb0HsOuvY6hQNqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.75.0': - resolution: {integrity: sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA==} + '@oxlint/binding-linux-arm-musleabihf@1.76.0': + resolution: {integrity: sha512-PvPCVptkgVARsucgIqFQQcSmJ6xc6GtnVB5bRBekRahTc9eObMtjHfMjy5M+C2tHt5UCMttWM9RuSk/H9NqYeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.75.0': - resolution: {integrity: sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg==} + '@oxlint/binding-linux-arm64-gnu@1.76.0': + resolution: {integrity: sha512-3KeFDx8Bu4HPAXbuHZOr/oHvN+QT+JQhMw/NYPz7Z071xLSsG27Jfh9PIQVEY7hk1I+jr43ExqRIeJ6VKk2yLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.75.0': - resolution: {integrity: sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ==} + '@oxlint/binding-linux-arm64-musl@1.76.0': + resolution: {integrity: sha512-oPFkkKTgl0K/EIg9fQ8oA3IGcI05/Mq1en04iFa41mmNPT+6KEiByVazTOZZJiHMBBrbsns1YJ2e1Scqwzesjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.75.0': - resolution: {integrity: sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg==} + '@oxlint/binding-linux-ppc64-gnu@1.76.0': + resolution: {integrity: sha512-gN7yZ0eqflA5Fhf1wvHxGUltIV3FsvmB1zhNMDEK9vSHhc7E6qg9CuPeBgPZab66Tjzq6w6kHAtNEvnTHf4cyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.75.0': - resolution: {integrity: sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw==} + '@oxlint/binding-linux-riscv64-gnu@1.76.0': + resolution: {integrity: sha512-S/HqMbn22mQrjtErUxEoS/a55u8kIeXvreIxiJu5G7Le3UecEd6SQZxrDIpuhtgaFnsY/nVra3ytP+pRljDilA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.75.0': - resolution: {integrity: sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w==} + '@oxlint/binding-linux-riscv64-musl@1.76.0': + resolution: {integrity: sha512-ZIga3097VJZolGZk6SrIAUokIGfRkxRlhiHDUznZptGBfwrhD7pNfD1rzEzsCwvk/1DX0A1bLz+liuNh5QKIVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.75.0': - resolution: {integrity: sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ==} + '@oxlint/binding-linux-s390x-gnu@1.76.0': + resolution: {integrity: sha512-ZGiiA7pFzMJSyMWYZTVlPgbTsx+Vl8ihLGMIujPwaslUF7kIPPWAbVmAlTc+9lWDV+DCiB8Ikixu+lSHeOIIWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.75.0': - resolution: {integrity: sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg==} + '@oxlint/binding-linux-x64-gnu@1.76.0': + resolution: {integrity: sha512-JLiy5WuvEBFTT6ErIFV35SLzi0R7Iri6MKU6dZbTxfIx8pndbbPs3Mj780nMipBFcPkti+okAPOJ9POKkHFEgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.75.0': - resolution: {integrity: sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA==} + '@oxlint/binding-linux-x64-musl@1.76.0': + resolution: {integrity: sha512-z7lgKQtbo/I1NIe8G5NHLesxJDv0tRSUWTpXKb9Pm3E9nKFKfO4IOSDtFroKgXtOYb0jQbcdH+0wzTyMXVes+A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.75.0': - resolution: {integrity: sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w==} + '@oxlint/binding-openharmony-arm64@1.76.0': + resolution: {integrity: sha512-JOjKymIpb9QcYfEhZsN6h4V9Ivd474W38cNIBRv6bg2TbIvogbMTH0Mg6YWW9TiRDqfcX+/Hyfsbo5vcSE5guQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.75.0': - resolution: {integrity: sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ==} + '@oxlint/binding-win32-arm64-msvc@1.76.0': + resolution: {integrity: sha512-pqDWZiwcmByWUEm1NFUBNiT6aentCcaoMWJv0HbXEmuYermJ4sg8ppVrshubYP2MZ6SHccJJcpr6x469PuDFIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.75.0': - resolution: {integrity: sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ==} + '@oxlint/binding-win32-ia32-msvc@1.76.0': + resolution: {integrity: sha512-Ba0O659kgMv6pwO3z9PdO+K3aMxQRaw9HnG+e6AtOfgwcKFvYilciQYBoUBmxfQvOCKZe1SwjMkuB542NkuDMQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.75.0': - resolution: {integrity: sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA==} + '@oxlint/binding-win32-x64-msvc@1.76.0': + resolution: {integrity: sha512-5qcirPHO8nKfkoowEVWtpAoVTcYDy6g0UT0NGic450Qv8J2NrOqg4uQ8QppRP4MDTC7Xx47lbZnmadTH03CGGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.75.0': - resolution: {integrity: sha512-dNQBRuvkeecm9nxi1cXRxXA1oAyqJqHof6cdnjY/WDBU1nZ9V07rp9X9gG7+JgShrjJW4VR2dTo7t2EcX4XD8g==} + '@oxlint/plugins@1.76.0': + resolution: {integrity: sha512-twmbsVrYAjkaOw6I2pDiYSAxVLNOV8kcqMzajJ599SpqXzea+GjDCZVad1VUqNR/4thWjM4PER5n+3/TQtTkZQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -5186,15 +5190,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.141.0: - resolution: {integrity: sha512-uFkGGr1KMWd6aWv9UAqooYrN78trw8MWWmoPvgWokfBEUq1+eiIQ+qfj3wokhy0fxtZWZk+0dHoS7/yRTJtd6w==} + oxc-parser@0.142.0: + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.60.0: - resolution: {integrity: sha512-fViX6i+gJuZWY+jI/fnR6WRbRj70GZ9RlCd30MygJrHTUNc4DxvKHWw8vBjMjffv3PgU5qWDR0AzmojQByqaZA==} + oxfmt@0.61.0: + resolution: {integrity: sha512-DxdHBEMYpcEnHoUHjjOigUqV2TYKsvxLwUPXnVYBjgFdqrcQ/91OtwubtZ2PUodCs3sStI8R5Qw3fKNGK4e8wQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5206,16 +5210,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.75.0: - resolution: {integrity: sha512-pgVC0ctNXubXVG7ObuW85kyAf9jHBDPIZm1uKCDwP8JPP2nCjXFtuEpCapmr4JgzW9F84z8x4fik+OpBep81gQ==} + oxlint-plugin-eslint@1.76.0: + resolution: {integrity: sha512-AGpz+tU70Og6tNLsujFkL0h1zX3UnJ7dbEwZpe5veYpixrcGj3/+lPeEOR4ADIeOqR7aNW+tEAMqAZAOL9gTXg==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@7.0.2001: resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.75.0: - resolution: {integrity: sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g==} + oxlint@1.76.0: + resolution: {integrity: sha512-6QoFioEU4fNdiUx/2Eo6TRd6NG7H7njnRCz8rhB66cZmMHDTqcm1Rjvl8Wry+ZTQMBAmyb4Mlf62Mk5X+eHSOw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5363,8 +5367,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.23: - resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + postcss@8.5.24: + resolution: {integrity: sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -6754,6 +6758,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 @@ -7118,7 +7127,7 @@ snapshots: '@img/sharp-wasm32@0.35.2': dependencies: - '@emnapi/runtime': 1.11.2 + '@emnapi/runtime': 1.11.3 optional: true '@img/sharp-webcontainers-wasm32@0.35.2': @@ -7290,14 +7299,14 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + '@napi-rs/wasm-runtime@1.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 @@ -7504,75 +7513,75 @@ snapshots: dependencies: '@otplib/core': 13.4.1 - '@oxc-parser/binding-android-arm-eabi@0.141.0': + '@oxc-parser/binding-android-arm-eabi@0.142.0': optional: true - '@oxc-parser/binding-android-arm64@0.141.0': + '@oxc-parser/binding-android-arm64@0.142.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.141.0': + '@oxc-parser/binding-darwin-arm64@0.142.0': optional: true - '@oxc-parser/binding-darwin-x64@0.141.0': + '@oxc-parser/binding-darwin-x64@0.142.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.141.0': + '@oxc-parser/binding-freebsd-x64@0.142.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.141.0': + '@oxc-parser/binding-linux-arm64-musl@0.142.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.141.0': + '@oxc-parser/binding-linux-x64-gnu@0.142.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.141.0': + '@oxc-parser/binding-linux-x64-musl@0.142.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.141.0': + '@oxc-parser/binding-openharmony-arm64@0.142.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.141.0': + '@oxc-parser/binding-wasm32-wasi@0.142.0': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.141.0': + '@oxc-parser/binding-win32-x64-msvc@0.142.0': optional: true '@oxc-project/types@0.139.0': {} '@oxc-project/types@0.140.0': {} - '@oxc-project/types@0.141.0': {} + '@oxc-project/types@0.142.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7626,7 +7635,7 @@ snapshots: dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@oxc-resolver/binding-win32-arm64-msvc@11.24.2': @@ -7635,61 +7644,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.60.0': + '@oxfmt/binding-android-arm-eabi@0.61.0': optional: true - '@oxfmt/binding-android-arm64@0.60.0': + '@oxfmt/binding-android-arm64@0.61.0': optional: true - '@oxfmt/binding-darwin-arm64@0.60.0': + '@oxfmt/binding-darwin-arm64@0.61.0': optional: true - '@oxfmt/binding-darwin-x64@0.60.0': + '@oxfmt/binding-darwin-x64@0.61.0': optional: true - '@oxfmt/binding-freebsd-x64@0.60.0': + '@oxfmt/binding-freebsd-x64@0.61.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.61.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.60.0': + '@oxfmt/binding-linux-arm-musleabihf@0.61.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.60.0': + '@oxfmt/binding-linux-arm64-gnu@0.61.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.60.0': + '@oxfmt/binding-linux-arm64-musl@0.61.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.60.0': + '@oxfmt/binding-linux-ppc64-gnu@0.61.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.60.0': + '@oxfmt/binding-linux-riscv64-gnu@0.61.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.60.0': + '@oxfmt/binding-linux-riscv64-musl@0.61.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.60.0': + '@oxfmt/binding-linux-s390x-gnu@0.61.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.60.0': + '@oxfmt/binding-linux-x64-gnu@0.61.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.60.0': + '@oxfmt/binding-linux-x64-musl@0.61.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.60.0': + '@oxfmt/binding-openharmony-arm64@0.61.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.60.0': + '@oxfmt/binding-win32-arm64-msvc@0.61.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.60.0': + '@oxfmt/binding-win32-ia32-msvc@0.61.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.60.0': + '@oxfmt/binding-win32-x64-msvc@0.61.0': optional: true '@oxlint-tsgolint/darwin-arm64@7.0.2001': @@ -7710,64 +7719,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.75.0': + '@oxlint/binding-android-arm-eabi@1.76.0': optional: true - '@oxlint/binding-android-arm64@1.75.0': + '@oxlint/binding-android-arm64@1.76.0': optional: true - '@oxlint/binding-darwin-arm64@1.75.0': + '@oxlint/binding-darwin-arm64@1.76.0': optional: true - '@oxlint/binding-darwin-x64@1.75.0': + '@oxlint/binding-darwin-x64@1.76.0': optional: true - '@oxlint/binding-freebsd-x64@1.75.0': + '@oxlint/binding-freebsd-x64@1.76.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.75.0': + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.75.0': + '@oxlint/binding-linux-arm-musleabihf@1.76.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.75.0': + '@oxlint/binding-linux-arm64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.75.0': + '@oxlint/binding-linux-arm64-musl@1.76.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.75.0': + '@oxlint/binding-linux-ppc64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.75.0': + '@oxlint/binding-linux-riscv64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.75.0': + '@oxlint/binding-linux-riscv64-musl@1.76.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.75.0': + '@oxlint/binding-linux-s390x-gnu@1.76.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.75.0': + '@oxlint/binding-linux-x64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-x64-musl@1.75.0': + '@oxlint/binding-linux-x64-musl@1.76.0': optional: true - '@oxlint/binding-openharmony-arm64@1.75.0': + '@oxlint/binding-openharmony-arm64@1.76.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.75.0': + '@oxlint/binding-win32-arm64-msvc@1.76.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.75.0': + '@oxlint/binding-win32-ia32-msvc@1.76.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.75.0': + '@oxlint/binding-win32-x64-msvc@1.76.0': optional: true - '@oxlint/plugins@1.75.0': {} + '@oxlint/plugins@1.76.0': {} '@pinojs/redact@0.4.0': {} @@ -7903,14 +7912,14 @@ snapshots: dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rolldown/binding-wasm32-wasi@1.2.0': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@rolldown/binding-win32-arm64-msvc@1.1.5': @@ -10873,30 +10882,30 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.141.0: + oxc-parser@0.142.0: dependencies: - '@oxc-project/types': 0.141.0 + '@oxc-project/types': 0.142.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.141.0 - '@oxc-parser/binding-android-arm64': 0.141.0 - '@oxc-parser/binding-darwin-arm64': 0.141.0 - '@oxc-parser/binding-darwin-x64': 0.141.0 - '@oxc-parser/binding-freebsd-x64': 0.141.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.141.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.141.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.141.0 - '@oxc-parser/binding-linux-arm64-musl': 0.141.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.141.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.141.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.141.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.141.0 - '@oxc-parser/binding-linux-x64-gnu': 0.141.0 - '@oxc-parser/binding-linux-x64-musl': 0.141.0 - '@oxc-parser/binding-openharmony-arm64': 0.141.0 - '@oxc-parser/binding-wasm32-wasi': 0.141.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.141.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.141.0 - '@oxc-parser/binding-win32-x64-msvc': 0.141.0 + '@oxc-parser/binding-android-arm-eabi': 0.142.0 + '@oxc-parser/binding-android-arm64': 0.142.0 + '@oxc-parser/binding-darwin-arm64': 0.142.0 + '@oxc-parser/binding-darwin-x64': 0.142.0 + '@oxc-parser/binding-freebsd-x64': 0.142.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.142.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.142.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.142.0 + '@oxc-parser/binding-linux-arm64-musl': 0.142.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.142.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-musl': 0.142.0 + '@oxc-parser/binding-openharmony-arm64': 0.142.0 + '@oxc-parser/binding-wasm32-wasi': 0.142.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.142.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.142.0 + '@oxc-parser/binding-win32-x64-msvc': 0.142.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10920,31 +10929,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.60.0: + oxfmt@0.61.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.60.0 - '@oxfmt/binding-android-arm64': 0.60.0 - '@oxfmt/binding-darwin-arm64': 0.60.0 - '@oxfmt/binding-darwin-x64': 0.60.0 - '@oxfmt/binding-freebsd-x64': 0.60.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.60.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.60.0 - '@oxfmt/binding-linux-arm64-gnu': 0.60.0 - '@oxfmt/binding-linux-arm64-musl': 0.60.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.60.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.60.0 - '@oxfmt/binding-linux-riscv64-musl': 0.60.0 - '@oxfmt/binding-linux-s390x-gnu': 0.60.0 - '@oxfmt/binding-linux-x64-gnu': 0.60.0 - '@oxfmt/binding-linux-x64-musl': 0.60.0 - '@oxfmt/binding-openharmony-arm64': 0.60.0 - '@oxfmt/binding-win32-arm64-msvc': 0.60.0 - '@oxfmt/binding-win32-ia32-msvc': 0.60.0 - '@oxfmt/binding-win32-x64-msvc': 0.60.0 - - oxlint-plugin-eslint@1.75.0: {} + '@oxfmt/binding-android-arm-eabi': 0.61.0 + '@oxfmt/binding-android-arm64': 0.61.0 + '@oxfmt/binding-darwin-arm64': 0.61.0 + '@oxfmt/binding-darwin-x64': 0.61.0 + '@oxfmt/binding-freebsd-x64': 0.61.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.61.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.61.0 + '@oxfmt/binding-linux-arm64-gnu': 0.61.0 + '@oxfmt/binding-linux-arm64-musl': 0.61.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.61.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.61.0 + '@oxfmt/binding-linux-riscv64-musl': 0.61.0 + '@oxfmt/binding-linux-s390x-gnu': 0.61.0 + '@oxfmt/binding-linux-x64-gnu': 0.61.0 + '@oxfmt/binding-linux-x64-musl': 0.61.0 + '@oxfmt/binding-openharmony-arm64': 0.61.0 + '@oxfmt/binding-win32-arm64-msvc': 0.61.0 + '@oxfmt/binding-win32-ia32-msvc': 0.61.0 + '@oxfmt/binding-win32-x64-msvc': 0.61.0 + + oxlint-plugin-eslint@1.76.0: {} oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -10955,27 +10964,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.75.0(oxlint-tsgolint@7.0.2001): + oxlint@1.76.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.75.0 - '@oxlint/binding-android-arm64': 1.75.0 - '@oxlint/binding-darwin-arm64': 1.75.0 - '@oxlint/binding-darwin-x64': 1.75.0 - '@oxlint/binding-freebsd-x64': 1.75.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.75.0 - '@oxlint/binding-linux-arm-musleabihf': 1.75.0 - '@oxlint/binding-linux-arm64-gnu': 1.75.0 - '@oxlint/binding-linux-arm64-musl': 1.75.0 - '@oxlint/binding-linux-ppc64-gnu': 1.75.0 - '@oxlint/binding-linux-riscv64-gnu': 1.75.0 - '@oxlint/binding-linux-riscv64-musl': 1.75.0 - '@oxlint/binding-linux-s390x-gnu': 1.75.0 - '@oxlint/binding-linux-x64-gnu': 1.75.0 - '@oxlint/binding-linux-x64-musl': 1.75.0 - '@oxlint/binding-openharmony-arm64': 1.75.0 - '@oxlint/binding-win32-arm64-msvc': 1.75.0 - '@oxlint/binding-win32-ia32-msvc': 1.75.0 - '@oxlint/binding-win32-x64-msvc': 1.75.0 + '@oxlint/binding-android-arm-eabi': 1.76.0 + '@oxlint/binding-android-arm64': 1.76.0 + '@oxlint/binding-darwin-arm64': 1.76.0 + '@oxlint/binding-darwin-x64': 1.76.0 + '@oxlint/binding-freebsd-x64': 1.76.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.76.0 + '@oxlint/binding-linux-arm-musleabihf': 1.76.0 + '@oxlint/binding-linux-arm64-gnu': 1.76.0 + '@oxlint/binding-linux-arm64-musl': 1.76.0 + '@oxlint/binding-linux-ppc64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-musl': 1.76.0 + '@oxlint/binding-linux-s390x-gnu': 1.76.0 + '@oxlint/binding-linux-x64-gnu': 1.76.0 + '@oxlint/binding-linux-x64-musl': 1.76.0 + '@oxlint/binding-openharmony-arm64': 1.76.0 + '@oxlint/binding-win32-arm64-msvc': 1.76.0 + '@oxlint/binding-win32-ia32-msvc': 1.76.0 + '@oxlint/binding-win32-x64-msvc': 1.76.0 oxlint-tsgolint: 7.0.2001 p-cancelable@4.0.1: {} @@ -11116,7 +11125,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.23: + postcss@8.5.24: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -12054,7 +12063,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.23 + postcss: 8.5.24 rollup: 4.62.3 tinyglobby: 0.2.17 optionalDependencies: From 267eb1e615019fafcc8f35a47fc8db5d669e0b19 Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:25:19 +0800 Subject: [PATCH 0451/1190] docs: add back route metadata for unknown routes (#22865) * fix: add back route metadata * fix: update route handlers to use ctx.req.param for path retrieval * fix: add back route metadata for tsx * fix: remove unnecessary reposition * fix: remove * path * docs: fix route name * fix: radar url * fix: more maintainers * fix: typo * fix: last batch of empty maintainers * feat!: remove biodiscover * fix: correct eslint directive in data258 route --- lib/routes/141jav/index.tsx | 7 +- lib/routes/141ppv/index.tsx | 7 +- lib/routes/163/music/userevents.tsx | 4 + lib/routes/1point3acres/offer.tsx | 2 +- lib/routes/1point3acres/thread.ts | 2 +- lib/routes/2cycd/index.ts | 16 +- lib/routes/3dmgame/game.ts | 5 + lib/routes/4gamers/category.ts | 15 +- lib/routes/4gamers/index.ts | 19 ++ lib/routes/50forum/zhuanjia.ts | 4 +- lib/routes/6park/news.ts | 15 +- lib/routes/8world/index.ts | 21 ++- lib/routes/8world/topic.ts | 13 ++ lib/routes/9to5/subsite.ts | 15 +- lib/routes/aamacau/index.ts | 2 +- lib/routes/abmedia/category.ts | 2 +- lib/routes/abmedia/index.ts | 2 +- lib/routes/abskoop/index.ts | 2 + lib/routes/abskoop/nsfw.ts | 2 + lib/routes/acs/journal.tsx | 11 +- lib/routes/aicaijing/cover.ts | 18 ++ lib/routes/aicaijing/index.tsx | 19 +- lib/routes/aicaijing/information.ts | 38 ++++ lib/routes/aicaijing/recommend.ts | 18 ++ lib/routes/aijishu/index.ts | 2 +- lib/routes/aisixiang/toplist.ts | 7 +- lib/routes/aljazeera/index.tsx | 39 ++++- lib/routes/aljazeera/rss.ts | 34 ++++ lib/routes/aljazeera/tag.ts | 39 +++++ lib/routes/amazon/awsblogs.ts | 10 +- lib/routes/amazon/kindle-software-updates.tsx | 2 +- lib/routes/aqara/news.ts | 6 +- lib/routes/aqara/post.tsx | 18 +- lib/routes/aqara/region.ts | 17 +- lib/routes/arcteryx/new-arrivals.ts | 2 +- lib/routes/arcteryx/outlet.ts | 2 +- lib/routes/arcteryx/regear-new-arrivals.tsx | 2 +- lib/routes/asiantolick/category.ts | 35 ++++ lib/routes/asiantolick/index.ts | 21 +-- lib/routes/asiantolick/page.ts | 25 +++ lib/routes/asiantolick/search.ts | 25 +++ lib/routes/asiantolick/tag.ts | 64 +++++++ lib/routes/bad/index.ts | 16 +- lib/routes/bast/index.ts | 24 ++- lib/routes/bellroy/new-releases.ts | 2 +- lib/routes/bilibili/video-all.ts | 2 +- lib/routes/biodiscover/index.ts | 60 ------- lib/routes/biodiscover/namespace.ts | 7 - lib/routes/biquge/index.ts | 37 +++- lib/routes/bnu/fdy.ts | 10 +- lib/routes/bnu/lib.ts | 8 +- lib/routes/brooklynmuseum/exhibitions.ts | 2 +- lib/routes/bwsg/index.ts | 6 +- lib/routes/bytes/bytes.ts | 5 +- lib/routes/caam/index.ts | 112 +++++++++++- lib/routes/caixin/blog.ts | 2 +- lib/routes/cas/genetics/index.ts | 14 +- lib/routes/cas/is/index.ts | 10 +- lib/routes/cbaigui/index.ts | 22 ++- lib/routes/cbnweek/index.ts | 4 +- lib/routes/cebbank/all.tsx | 3 +- lib/routes/cfmmc/index.ts | 57 +++++- lib/routes/chaincatcher/home.tsx | 4 +- lib/routes/chinafactcheck/index.ts | 4 +- lib/routes/chinanews/index.ts | 4 +- lib/routes/chinawriter/index.ts | 133 +++++++++++++- lib/routes/cmde/index.ts | 10 +- lib/routes/cncf/reports.ts | 6 +- lib/routes/cneb/yjxx.ts | 38 +++- lib/routes/cnjxol/index.tsx | 46 ++++- lib/routes/cnjxol/nhwb.ts | 37 ++++ lib/routes/codeforces/recent-actions.ts | 2 +- lib/routes/cpuid/news.ts | 2 +- lib/routes/cs/index.ts | 165 +++++++++++++++++- lib/routes/cs/zzkx.ts | 15 -- lib/routes/curiouscat/user.ts | 5 +- lib/routes/cyzone/author.ts | 2 +- lib/routes/cyzone/index.ts | 15 +- lib/routes/cyzone/label.ts | 2 +- lib/routes/dayanzai/index.ts | 2 +- lib/routes/deadline/posts.tsx | 4 +- lib/routes/dgjyw/index.ts | 31 +++- lib/routes/digitalcameraworld/news.ts | 2 +- lib/routes/discourse/notifications.ts | 2 +- lib/routes/discuz/discuz.ts | 14 +- lib/routes/distill/index.ts | 4 +- lib/routes/dlsite/z-index/index.ts | 18 +- lib/routes/douban/other/explore-column.ts | 7 +- lib/routes/dut/index.ts | 25 ++- lib/routes/e-hentai/index.tsx | 47 ++++- lib/routes/e-hentai/search.ts | 31 ++++ lib/routes/e-hentai/tag.ts | 31 ++++ lib/routes/elsevier/issue.ts | 16 +- lib/routes/elsevier/journal.ts | 11 +- lib/routes/embassy/index.ts | 8 +- lib/routes/fda/cdrh.ts | 7 +- lib/routes/firefox/release.ts | 10 +- lib/routes/fishshell/index.ts | 4 +- lib/routes/fx-markets/channel.ts | 2 +- lib/routes/gamme/category.ts | 10 +- lib/routes/gamme/tag.ts | 10 +- lib/routes/gdsrx/index.ts | 2 +- lib/routes/gelonghui/live.tsx | 2 +- lib/routes/genossenschaften/index.ts | 5 +- lib/routes/gesiba/index.ts | 6 +- lib/routes/getdr/index.ts | 4 +- lib/routes/getitfree/index.ts | 30 +++- lib/routes/getitfree/search.ts | 24 +++ lib/routes/getitfree/tag.ts | 68 ++++++++ lib/routes/globallawreview/index.ts | 4 +- lib/routes/gocn/news.ts | 20 ++- lib/routes/gov/chongqing/gzw.ts | 2 + lib/routes/gov/mfa/wjdt.ts | 5 + lib/routes/gq/news.ts | 2 +- lib/routes/grubstreet/index.ts | 4 +- lib/routes/guancha/topic.ts | 20 ++- lib/routes/guanhai/index.ts | 4 +- lib/routes/hackertalk/index.ts | 4 +- lib/routes/hackyournews/index.ts | 4 +- lib/routes/hafu/news.ts | 2 +- lib/routes/hinatazaka46/blog.ts | 2 +- lib/routes/hinatazaka46/news.ts | 2 +- lib/routes/hk01/channel.ts | 7 +- lib/routes/hk01/issue.ts | 7 +- lib/routes/hk01/tag.ts | 7 +- lib/routes/hk01/zone.ts | 7 +- lib/routes/hkjunkcall/index.ts | 4 +- lib/routes/hongkong/chp.ts | 16 +- lib/routes/hostmonit/cloudflareyesv6.ts | 12 -- lib/routes/hrbeu/gx/card.ts | 10 +- lib/routes/hrbeu/gx/list.ts | 10 +- lib/routes/hrbeu/uae/news.ts | 2 +- lib/routes/hunau/gfxy/index.ts | 2 +- lib/routes/hunau/jwc.ts | 2 +- lib/routes/hunau/xky/index.ts | 2 +- lib/routes/ielts/index.ts | 4 +- lib/routes/ifeng/news.tsx | 18 +- lib/routes/ifi-audio/download.ts | 2 +- lib/routes/iiilab/index.ts | 4 +- lib/routes/inoreader/index.ts | 3 +- lib/routes/inoreader/rss.ts | 2 +- lib/routes/iqilu/program.ts | 29 ++- lib/routes/itch/index.ts | 19 +- lib/routes/javdb/lists.ts | 20 ++- lib/routes/javlibrary/bestrated.ts | 7 +- lib/routes/javlibrary/genre.ts | 7 +- lib/routes/javlibrary/maker.ts | 2 +- lib/routes/javlibrary/mostwanted.ts | 7 +- lib/routes/javlibrary/newentries.ts | 7 +- lib/routes/javlibrary/newrelease.ts | 7 +- lib/routes/javlibrary/update.ts | 7 +- lib/routes/javlibrary/user.ts | 7 +- lib/routes/jiaoliudao/index.ts | 4 +- lib/routes/jseea/namespace.ts | 2 +- lib/routes/jseea/news.ts | 8 +- lib/routes/jsu/cxzx.ts | 16 +- lib/routes/kantarworldpanel/index.tsx | 77 +++++++- lib/routes/keepass/news.ts | 4 +- lib/routes/layoffs/index.ts | 2 + lib/routes/leetcode/dailyquestion-cn.ts | 6 +- lib/routes/leetcode/dailyquestion-en.ts | 6 +- .../leetcode/dailyquestion-solution-cn.ts | 6 +- .../leetcode/dailyquestion-solution-en.ts | 6 +- lib/routes/leiphone/category.ts | 100 +++++++++++ lib/routes/leiphone/index.ts | 36 +--- lib/routes/leiphone/newsflash.ts | 3 +- lib/routes/lfsyd/tag.ts | 13 +- lib/routes/lfsyd/user.ts | 9 +- lib/routes/lifeweek/channel.ts | 12 +- lib/routes/lifeweek/tag.ts | 12 +- lib/routes/lightnovel/light-novel.ts | 8 +- lib/routes/literotica/category.ts | 7 +- lib/routes/liulinblog/index.ts | 47 ++++- lib/routes/liulinblog/itnews.ts | 10 +- lib/routes/liulinblog/kuaixun.ts | 18 ++ lib/routes/liulinblog/search.ts | 19 ++ lib/routes/liulinblog/series.ts | 22 +++ lib/routes/liulinblog/tag.ts | 42 +++++ lib/routes/lkong/forum.ts | 8 +- lib/routes/lkong/thread.tsx | 7 +- lib/routes/logclub/columnist.ts | 106 +++++++++++ lib/routes/logclub/company.ts | 145 +++++++++++++++ lib/routes/logclub/index.ts | 32 +++- lib/routes/logclub/original.ts | 18 ++ lib/routes/logclub/recruit.ts | 18 ++ lib/routes/logclub/report.ts | 23 ++- lib/routes/logclub/tender.ts | 18 ++ lib/routes/logonews/category.ts | 22 +++ lib/routes/logonews/index.tsx | 18 +- lib/routes/logonews/tag.ts | 21 +++ lib/routes/logonews/work-category.ts | 22 +++ lib/routes/logonews/work-tag.ts | 22 +++ lib/routes/logonews/work.ts | 19 ++ lib/routes/m4/index.ts | 36 ++-- lib/routes/m4/mil.ts | 28 +++ lib/routes/magazinelib/latest-magazine.tsx | 2 +- lib/routes/magnumphotos/magazine.ts | 2 +- lib/routes/mail/imap.ts | 11 +- lib/routes/medieval-china/post.ts | 4 +- lib/routes/metacritic/index.tsx | 33 +++- lib/routes/metacritic/movie.ts | 31 ++++ lib/routes/metacritic/tv.ts | 31 ++++ lib/routes/metmuseum/exhibitions.ts | 15 +- lib/routes/nature/highlight.ts | 2 +- lib/routes/nature/news-and-comment.ts | 26 ++- lib/routes/ncwu/notice.ts | 2 +- lib/routes/nenu/sohac.ts | 19 +- lib/routes/nenu/yjsy.ts | 19 +- lib/routes/netflav/index.tsx | 4 +- lib/routes/newyorker/news.ts | 2 +- lib/routes/niaogebiji/index.ts | 4 +- lib/routes/nintendo/eshop-cn.ts | 6 +- lib/routes/nintendo/eshop-hk.ts | 6 +- lib/routes/nintendo/eshop-jp.ts | 6 +- lib/routes/nintendo/eshop-us.ts | 6 +- lib/routes/nju/exchangesys.ts | 2 +- lib/routes/nogizaka46/blog.ts | 2 +- lib/routes/nuaa/college/cae.ts | 8 +- lib/routes/nuist/library/lib.ts | 4 +- lib/routes/nuist/yjs.ts | 21 ++- .../oceanengine/arithmetic-index-toutiao.ts | 20 +++ lib/routes/oceanengine/arithmetic-index.tsx | 25 ++- lib/routes/oeeee/app/channel.ts | 14 +- lib/routes/oesw/index.ts | 12 +- lib/routes/onehu/common.ts | 4 +- lib/routes/openwrt/releases.ts | 10 +- lib/routes/patagonia/new-arrivals.tsx | 2 +- lib/routes/people/xjpjh.ts | 2 +- lib/routes/peopo/topic.ts | 2 +- lib/routes/pianyuan/search.ts | 5 +- lib/routes/pikabu/community.ts | 21 ++- lib/routes/pikabu/tag.ts | 19 ++ lib/routes/pincong/topic.ts | 13 +- lib/routes/pku/ss/notice.ts | 13 +- lib/routes/pnas/index.tsx | 17 +- lib/routes/pts/category.ts | 38 ++++ lib/routes/pts/index.ts | 27 ++- lib/routes/pts/live.ts | 2 +- lib/routes/pts/opinion.ts | 27 +++ lib/routes/pts/report.ts | 27 +++ lib/routes/pts/tag.ts | 26 +++ lib/routes/pubmed/trending.tsx | 12 +- lib/routes/qianp/news.ts | 7 +- lib/routes/qiyoujiage/price.ts | 12 +- lib/routes/qoo-app/notes/topic.ts | 13 +- lib/routes/qq/ac/comic.ts | 9 +- lib/routes/quicker/versions.ts | 6 +- lib/routes/rarehistoricalphotos/index.ts | 4 +- lib/routes/reactnewsletter/reactnewsletter.ts | 5 +- lib/routes/researchgate/publications.ts | 11 +- lib/routes/routledge/book-series.tsx | 8 +- lib/routes/rsshub/transform/sitemap.ts | 23 ++- lib/routes/ruancan/category.ts | 4 +- lib/routes/ruancan/index.ts | 6 +- lib/routes/ruancan/search.ts | 4 +- lib/routes/ruancan/user.ts | 7 +- lib/routes/sakurazaka46/blog.ts | 2 +- lib/routes/sec-in/index.ts | 4 +- lib/routes/secretsanfrancisco/rss.tsx | 2 +- lib/routes/sehuatang/index.ts | 6 + lib/routes/shu/jwb.ts | 3 + lib/routes/shuiguopai/index.tsx | 4 +- lib/routes/sinchew/category.ts | 27 +++ lib/routes/sinchew/index.tsx | 16 +- lib/routes/sinchew/latest.ts | 18 ++ lib/routes/snowpeak/us-new-arrivals.tsx | 2 +- lib/routes/sony/downloads.ts | 2 +- lib/routes/sspu/jwc.ts | 8 +- lib/routes/sspu/pe.ts | 49 +++++- lib/routes/stratechery/index.ts | 4 +- lib/routes/subhd/index.ts | 42 +++-- lib/routes/subhd/zu.ts | 22 +++ lib/routes/supchina/index.ts | 4 +- lib/routes/sysu/cse.ts | 2 +- lib/routes/tableau/viz-of-the-day.ts | 2 +- lib/routes/techcrunch/news.ts | 2 +- lib/routes/tencent/news/coronavirus/data.tsx | 8 +- lib/routes/tencent/news/coronavirus/total.tsx | 4 +- lib/routes/theatlantic/news.ts | 2 +- lib/routes/thegadgetflow/rss.tsx | 2 +- lib/routes/thenewslens/author.ts | 19 ++ lib/routes/thenewslens/category.ts | 19 ++ lib/routes/thenewslens/channel.ts | 19 ++ lib/routes/thenewslens/index.ts | 20 ++- lib/routes/thenewslens/news.ts | 22 +++ lib/routes/thenewslens/review.ts | 19 ++ lib/routes/thenewslens/tag.ts | 19 ++ lib/routes/thenewslens/videos.ts | 19 ++ lib/routes/thepaper/839studio/category.ts | 6 + lib/routes/thepaper/839studio/studio.ts | 2 + lib/routes/tingshuitz/wuhan.ts | 11 +- lib/routes/tokeninsight/bulletin.ts | 2 +- lib/routes/tokeninsight/report.ts | 2 +- lib/routes/toodaylab/column.ts | 22 +++ lib/routes/toodaylab/field.ts | 22 +++ lib/routes/toodaylab/hot.ts | 18 ++ lib/routes/toodaylab/index.ts | 23 ++- lib/routes/toodaylab/topic.ts | 25 +++ lib/routes/tradingview/blog.ts | 52 +++++- lib/routes/tradingview/pine.ts | 11 +- lib/routes/transcriptforest/index.ts | 62 ++++++- lib/routes/tribalfootball/latest.tsx | 4 +- lib/routes/tynu/tynu.ts | 4 +- lib/routes/ulapia/research.ts | 2 +- lib/routes/uraaka-joshi/uraaka-joshi.ts | 4 +- lib/routes/usts/jwch.ts | 2 +- lib/routes/v1tx/index.ts | 4 +- lib/routes/v2rayshare/index.ts | 5 +- lib/routes/vcb-s/index.ts | 4 +- lib/routes/wallpaperhub/index.tsx | 4 +- lib/routes/wdc/download.ts | 2 +- lib/routes/web3caff/index.ts | 17 +- lib/routes/wechat/data258.ts | 20 ++- lib/routes/wechat/sogou.ts | 2 +- lib/routes/wenku8/index.ts | 16 +- lib/routes/whu/hyxt.ts | 47 ++++- lib/routes/whu/news.ts | 4 +- lib/routes/wiensued/index.ts | 8 +- lib/routes/wmc-bj/publish.tsx | 14 +- lib/routes/worldjournal/index.ts | 7 +- lib/routes/wzu/news.ts | 8 +- lib/routes/xjtu/dyyy/index.ts | 7 +- lib/routes/xjtu/international.ts | 7 +- lib/routes/xmnn/news.ts | 32 +++- lib/routes/xmut/jwc/bkjw.ts | 10 +- lib/routes/xmut/jwc/yjs.ts | 10 +- lib/routes/xueqiu/stock-comments.tsx | 2 +- lib/routes/xyzrank/hot-episodes-new.ts | 19 ++ lib/routes/xyzrank/hot-podcasts.ts | 19 ++ lib/routes/xyzrank/index.tsx | 36 ++-- lib/routes/xyzrank/new-podcasts.ts | 19 ++ lib/routes/yangtzeu/dongke.ts | 17 +- lib/routes/yoasobi-music/info.tsx | 2 +- lib/routes/zagg/new-arrivals.tsx | 2 +- lib/routes/zhibo8/luxiang.ts | 8 +- lib/routes/zhihu/question.ts | 2 +- lib/routes/zhuwang/index.ts | 2 +- lib/routes/zjgtjy/index.ts | 8 +- lib/routes/zyshow/index.tsx | 21 ++- 339 files changed, 4383 insertions(+), 706 deletions(-) create mode 100644 lib/routes/4gamers/index.ts create mode 100644 lib/routes/8world/topic.ts create mode 100644 lib/routes/aicaijing/cover.ts create mode 100644 lib/routes/aicaijing/information.ts create mode 100644 lib/routes/aicaijing/recommend.ts create mode 100644 lib/routes/aljazeera/rss.ts create mode 100644 lib/routes/aljazeera/tag.ts create mode 100644 lib/routes/asiantolick/category.ts create mode 100644 lib/routes/asiantolick/page.ts create mode 100644 lib/routes/asiantolick/search.ts create mode 100644 lib/routes/asiantolick/tag.ts delete mode 100644 lib/routes/biodiscover/index.ts delete mode 100644 lib/routes/biodiscover/namespace.ts create mode 100644 lib/routes/cnjxol/nhwb.ts delete mode 100644 lib/routes/cs/zzkx.ts create mode 100644 lib/routes/e-hentai/search.ts create mode 100644 lib/routes/e-hentai/tag.ts create mode 100644 lib/routes/getitfree/search.ts create mode 100644 lib/routes/getitfree/tag.ts delete mode 100644 lib/routes/hostmonit/cloudflareyesv6.ts create mode 100644 lib/routes/leiphone/category.ts create mode 100644 lib/routes/liulinblog/kuaixun.ts create mode 100644 lib/routes/liulinblog/search.ts create mode 100644 lib/routes/liulinblog/series.ts create mode 100644 lib/routes/liulinblog/tag.ts create mode 100644 lib/routes/logclub/columnist.ts create mode 100644 lib/routes/logclub/company.ts create mode 100644 lib/routes/logclub/original.ts create mode 100644 lib/routes/logclub/recruit.ts create mode 100644 lib/routes/logclub/tender.ts create mode 100644 lib/routes/logonews/category.ts create mode 100644 lib/routes/logonews/tag.ts create mode 100644 lib/routes/logonews/work-category.ts create mode 100644 lib/routes/logonews/work-tag.ts create mode 100644 lib/routes/logonews/work.ts create mode 100644 lib/routes/m4/mil.ts create mode 100644 lib/routes/metacritic/movie.ts create mode 100644 lib/routes/metacritic/tv.ts create mode 100644 lib/routes/oceanengine/arithmetic-index-toutiao.ts create mode 100644 lib/routes/pikabu/tag.ts create mode 100644 lib/routes/pts/category.ts create mode 100644 lib/routes/pts/opinion.ts create mode 100644 lib/routes/pts/report.ts create mode 100644 lib/routes/pts/tag.ts create mode 100644 lib/routes/sinchew/category.ts create mode 100644 lib/routes/sinchew/latest.ts create mode 100644 lib/routes/subhd/zu.ts create mode 100644 lib/routes/thenewslens/author.ts create mode 100644 lib/routes/thenewslens/category.ts create mode 100644 lib/routes/thenewslens/channel.ts create mode 100644 lib/routes/thenewslens/news.ts create mode 100644 lib/routes/thenewslens/review.ts create mode 100644 lib/routes/thenewslens/tag.ts create mode 100644 lib/routes/thenewslens/videos.ts create mode 100644 lib/routes/toodaylab/column.ts create mode 100644 lib/routes/toodaylab/field.ts create mode 100644 lib/routes/toodaylab/hot.ts create mode 100644 lib/routes/toodaylab/topic.ts create mode 100644 lib/routes/xyzrank/hot-episodes-new.ts create mode 100644 lib/routes/xyzrank/hot-podcasts.ts create mode 100644 lib/routes/xyzrank/new-podcasts.ts diff --git a/lib/routes/141jav/index.tsx b/lib/routes/141jav/index.tsx index 03818ae3fecb..87517a609f01 100644 --- a/lib/routes/141jav/index.tsx +++ b/lib/routes/141jav/index.tsx @@ -2,13 +2,13 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:type/:keyword{.+}?', categories: ['multimedia'], + example: '/141jav/popular/30', name: '通用', maintainers: ['cgkings', 'nczitzk'], parameters: { type: '类型,可查看下表的类型说明', keyword: '关键词,可查看下表的关键词说明' }, @@ -65,11 +65,6 @@ async function handler(ctx) { const $ = load(response.data); - if (getSubPath(ctx) === '/') { - ctx.set('redirect', `/141jav${$('.overview').first().attr('href')}`); - return; - } - const items = $('.columns') .toArray() .map((item) => { diff --git a/lib/routes/141ppv/index.tsx b/lib/routes/141ppv/index.tsx index 4cd480a5396c..05172e45a878 100644 --- a/lib/routes/141ppv/index.tsx +++ b/lib/routes/141ppv/index.tsx @@ -2,13 +2,13 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:type/:keyword{.+}?', categories: ['multimedia'], + example: '/141ppv/popular/30', name: '通用', maintainers: ['cgkings', 'nczitzk'], parameters: { type: '类型,可查看下表的类型说明', keyword: '关键词,可查看下表的关键词说明' }, @@ -65,11 +65,6 @@ async function handler(ctx) { const $ = load(response.data); - if (getSubPath(ctx) === '/') { - ctx.set('redirect', `/141ppv${$('.overview').first().attr('href')}`); - return; - } - const items = $('.columns') .toArray() .map((item) => { diff --git a/lib/routes/163/music/userevents.tsx b/lib/routes/163/music/userevents.tsx index 8efb40e0822f..379c1f09e2ac 100644 --- a/lib/routes/163/music/userevents.tsx +++ b/lib/routes/163/music/userevents.tsx @@ -23,6 +23,10 @@ const renderDescription = ({ description, pics }) => { export const route: Route = { path: '/music/user/events/:id', categories: ['multimedia'], + example: '/163/music/user/events/585804522', + parameters: { + id: '用户 uid, 可在用户主页 URL 中找到', + }, radar: [ { source: ['music.163.com/user/event'], diff --git a/lib/routes/1point3acres/offer.tsx b/lib/routes/1point3acres/offer.tsx index a7f1797c2da9..03542881a68e 100644 --- a/lib/routes/1point3acres/offer.tsx +++ b/lib/routes/1point3acres/offer.tsx @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: '录取结果', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'offer.1point3acres.com/', description: `::: tip 三个 id 获取方式 diff --git a/lib/routes/1point3acres/thread.ts b/lib/routes/1point3acres/thread.ts index 8bf50056935b..956478978428 100644 --- a/lib/routes/1point3acres/thread.ts +++ b/lib/routes/1point3acres/thread.ts @@ -8,7 +8,7 @@ export const route: Route = { parameters: { type: '帖子分类, 见下表,默认为 hot,即热门帖子', order: '排序方式,见下表,默认为空,即最新回复' }, name: '帖子', categories: ['bbs'], - maintainers: ['EthanWng97', 'DIYgod', 'nczitzk'], + maintainers: ['IvanWng97', 'DIYgod', 'nczitzk'], handler, url: 'instant.1point3acres.com/', description: `分类 diff --git a/lib/routes/2cycd/index.ts b/lib/routes/2cycd/index.ts index 4b52e75997e9..453587ef9fcd 100644 --- a/lib/routes/2cycd/index.ts +++ b/lib/routes/2cycd/index.ts @@ -11,8 +11,22 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/:fid/:sort?', - name: 'Unknown', + categories: ['bbs'], + example: '/2cycd/43/dateline', + parameters: { fid: '板块', sort: '排序' }, + name: '板块', maintainers: ['shelken'], + description: `板块(更多板块请自行 [查看](http://www.2cycd.com)) + +| 音乐下载(默认) | 动漫下载 | 游戏下载 | +| ---------------- | -------- | -------- | +| 43 | 53 | 42 | + +排序 + +| 发布时间排序(默认) | 回复/查看 | 查看 | +| -------------------- | ---------- | ----- | +| dateline | replies | views |`, handler, }; diff --git a/lib/routes/3dmgame/game.ts b/lib/routes/3dmgame/game.ts index c6ebcd8458cc..f8996f4aaabb 100644 --- a/lib/routes/3dmgame/game.ts +++ b/lib/routes/3dmgame/game.ts @@ -8,6 +8,8 @@ import { parseArticle } from './utils'; export const route: Route = { path: '/games/:name/:type?', + example: '/3dmgame/games/detroitbecomehuman/news', + parameters: { name: '游戏名字,可以在专题页的 url 中找到', type: '资讯类型,见下表,默认为 `news`' }, radar: [ { source: ['3dmgame.com/games/:name/:type'], @@ -17,6 +19,9 @@ export const route: Route = { categories: ['game'], maintainers: ['sinchang', 'jacky2001114', 'HenryQW', 'lyqluis'], handler, + description: `| 新闻 | 攻略 | 资源 | +| ---- | ---- | -------- | +| news | gl | resource |`, }; async function handler(ctx) { diff --git a/lib/routes/4gamers/category.ts b/lib/routes/4gamers/category.ts index da6c33f73aa7..f64867b84218 100644 --- a/lib/routes/4gamers/category.ts +++ b/lib/routes/4gamers/category.ts @@ -5,25 +5,28 @@ import got from '@/utils/got'; import { getCategories, parseItem, parseList } from './utils'; export const route: Route = { - path: ['/', '/category/:category'], + path: '/category/:category', + categories: ['game'], + example: '/4gamers/category/352', + parameters: { category: '分类 ID,可从分类 URL 中找到' }, radar: [ { - source: ['www.4gamers.com.tw/news', 'www.4gamers.com.tw/'], - target: '', + source: ['www.4gamers.com.tw/news/category/:category/:categoryName'], + target: '/category/:category', }, ], - name: 'Unknown', + name: '分类', maintainers: ['TonyRL'], handler, url: 'www.4gamers.com.tw/news', }; -async function handler(ctx) { +export async function handler(ctx) { const category = ctx.req.param('category'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const isLatest = !category; - const { data: response } = await got(`https://www.4gamers.com.tw/site/api/news/${isLatest ? 'latest' : `by-category/${category}`}`, { + const { data: response } = await got(`https://www.4gamers.com.tw/site/api/news/${isLatest ? 'latest' : `of-category/${category}`}`, { searchParams: { nextStart: 0, pageSize: limit, diff --git a/lib/routes/4gamers/index.ts b/lib/routes/4gamers/index.ts new file mode 100644 index 000000000000..9a55d4a121a4 --- /dev/null +++ b/lib/routes/4gamers/index.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './category'; + +export const route: Route = { + path: '/', + categories: ['game'], + example: '/4gamers', + radar: [ + { + source: ['www.4gamers.com.tw/news', 'www.4gamers.com.tw/'], + target: '/', + }, + ], + name: '最新消息', + maintainers: ['TonyRL'], + handler, + url: 'www.4gamers.com.tw/news', +}; diff --git a/lib/routes/50forum/zhuanjia.ts b/lib/routes/50forum/zhuanjia.ts index 4251b4c60fc9..5cdf4c6dfdf8 100644 --- a/lib/routes/50forum/zhuanjia.ts +++ b/lib/routes/50forum/zhuanjia.ts @@ -8,6 +8,8 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/', + categories: ['study'], + example: '/50forum', radar: [ { source: ['www.50forum.org.cn/portal/list/index.html?id=6'], @@ -18,7 +20,7 @@ export const route: Route = { target: '', }, ], - name: 'Unknown', + name: '专家文章', maintainers: ['sddiky'], handler, url: 'https://www.50forum.org.cn/portal/list/index.html?id=6', diff --git a/lib/routes/6park/news.ts b/lib/routes/6park/news.ts index c6d53cb59dfc..2e6ffab358c5 100644 --- a/lib/routes/6park/news.ts +++ b/lib/routes/6park/news.ts @@ -8,6 +8,8 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/news/:site?/:id?/:keyword?', + categories: ['new-media'], + example: '/6park/news', radar: [ { source: ['club.6parkbbs.com/:id/index.php', 'club.6parkbbs.com/'], @@ -17,11 +19,16 @@ export const route: Route = { name: '新闻栏目', maintainers: ['nczitzk', 'cscnk52'], parameters: { - site: '分站,可选newspark、local,默认为 newspark', - id: '栏目 id,可选,默认为空', - keyword: '关键词,可选,默认为空', + site: '分站,见下表,默认为 newspark', + id: '栏目 id', + keyword: '关键字', }, - description: `::: tip 提示 + description: `分站 + +| newspark | local | +| -------- | ----- | + +::: tip 提示 若订阅 [时政](https://www.6parknews.com/newspark/index.php?type=1),其网址为 <https://www.6parknews.com/newspark/index.php?type=1>,其中 \`newspark\` 为分站,\`1\` 为栏目 id。 若订阅 [美国](https://local.6parknews.com/index.php?type_id=1),其网址为 <https://local.6parknews.com/index.php?type_id=1>,其中 \`local\` 为分站,\`1\` 为栏目 id。 :::`, diff --git a/lib/routes/8world/index.ts b/lib/routes/8world/index.ts index d64bbbfa1e39..285d8d1bd838 100644 --- a/lib/routes/8world/index.ts +++ b/lib/routes/8world/index.ts @@ -7,13 +7,26 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:category?', + categories: ['new-media'], + example: '/8world/realtime', + parameters: { category: '分类 id,见下表,默认为即时 REALTIME' }, + description: `| 分类 | id | +| ---------------------- | -------------- | +| 即时 REALTIME | realtime | +| 新加坡 SINGAPORE | singapore | +| 东南亚 SOUTH-EAST ASIA | southeast-asia | +| 中港台 GREATER CHINA | greater-china | +| 国际 WORLD | world | +| 财经 FINANCE | finance | +| 体育 SPORTS | sports | +| 社团 COMMUNITY | community |`, + name: '分类', + maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { +export async function handler(ctx) { const path = getSubPath(ctx) === '/' ? '/realtime' : getSubPath(ctx); const rootUrl = 'https://www.8world.com'; diff --git a/lib/routes/8world/topic.ts b/lib/routes/8world/topic.ts new file mode 100644 index 000000000000..dfd9b6ee291d --- /dev/null +++ b/lib/routes/8world/topic.ts @@ -0,0 +1,13 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/topic/:id', + categories: ['new-media'], + example: '/8world/topic/xianggang-3', + parameters: { id: '标签 id,可在对应标签页中找到' }, + name: '标签', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/9to5/subsite.ts b/lib/routes/9to5/subsite.ts index eefa7f3002a9..5b8d492ab5a4 100644 --- a/lib/routes/9to5/subsite.ts +++ b/lib/routes/9to5/subsite.ts @@ -7,9 +7,20 @@ import utils from './utils'; export const route: Route = { path: '/:subsite/:tag?', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/9to5/mac/aapl', + parameters: { + subsite: 'Subsite name', + tag: 'Tag name inside the url of the tag page', + }, + name: 'Sub-site', + maintainers: ['HenryQW'], handler, + description: `Supported sub-sites: + +| 9To5Mac | 9To5Google | 9To5Toys | +| ------- | ---------- | -------- | +| Mac | Google | Toys |`, }; async function handler(ctx) { diff --git a/lib/routes/aamacau/index.ts b/lib/routes/aamacau/index.ts index 86d330dffa4e..5e039dcc6f34 100644 --- a/lib/routes/aamacau/index.ts +++ b/lib/routes/aamacau/index.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: '话题', - maintainers: [], + maintainers: ['nczitzk'], handler, url: 'aamacau.com/', description: `| 即時報道 | 每週專題 | 藝文爛鬼樓 | 論盡紙本 | 新聞事件 | 特別企劃 | diff --git a/lib/routes/abmedia/category.ts b/lib/routes/abmedia/category.ts index a99ea2b70f6b..feb8c2d4027a 100644 --- a/lib/routes/abmedia/category.ts +++ b/lib/routes/abmedia/category.ts @@ -31,7 +31,7 @@ export const route: Route = { }, ], name: '类别', - maintainers: [], + maintainers: ['Fatpandac'], handler, description: `参数可以从链接中拿到,如: diff --git a/lib/routes/abmedia/index.ts b/lib/routes/abmedia/index.ts index 7513d30b668b..a1558cd73c9f 100644 --- a/lib/routes/abmedia/index.ts +++ b/lib/routes/abmedia/index.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: '首页最新新闻', - maintainers: [], + maintainers: ['Fatpandac'], handler, url: 'www.abmedia.io/', }; diff --git a/lib/routes/abskoop/index.ts b/lib/routes/abskoop/index.ts index 67fb0b6ae8dc..e3f668b73d6b 100644 --- a/lib/routes/abskoop/index.ts +++ b/lib/routes/abskoop/index.ts @@ -7,6 +7,8 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['multimedia'], + example: '/abskoop', radar: [ { source: ['ahhhhfs.com/'], diff --git a/lib/routes/abskoop/nsfw.ts b/lib/routes/abskoop/nsfw.ts index 4cc07f67c168..72c3f07d379a 100644 --- a/lib/routes/abskoop/nsfw.ts +++ b/lib/routes/abskoop/nsfw.ts @@ -4,6 +4,8 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/nsfw', + categories: ['multimedia'], + example: '/abskoop/nsfw', radar: [ { source: ['ahhhhfs.com/'], diff --git a/lib/routes/acs/journal.tsx b/lib/routes/acs/journal.tsx index 24d1278bc3cc..68b4b0a704b9 100644 --- a/lib/routes/acs/journal.tsx +++ b/lib/routes/acs/journal.tsx @@ -10,14 +10,23 @@ import playwright from '@/utils/playwright'; export const route: Route = { path: '/journal/:id', + categories: ['journal'], + example: '/acs/journal/jacsat', + parameters: { id: 'Journal id, can be found in URL' }, + features: { + supportScihub: true, + }, radar: [ { source: ['pubs.acs.org/journal/:id', 'pubs.acs.org/'], }, ], - name: 'Unknown', + name: 'Journal', maintainers: ['nczitzk'], handler, + description: `::: tip +See [Browse Content](https://pubs.acs.org) +:::`, }; async function handler(ctx) { diff --git a/lib/routes/aicaijing/cover.ts b/lib/routes/aicaijing/cover.ts new file mode 100644 index 000000000000..e73ec8a3f462 --- /dev/null +++ b/lib/routes/aicaijing/cover.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/cover', + categories: ['finance'], + example: '/aicaijing/cover', + radar: [ + { + source: ['www.aicaijing.com/'], + target: '/cover', + }, + ], + name: '封面文章', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/aicaijing/index.tsx b/lib/routes/aicaijing/index.tsx index 19d768e4bdfa..276fbecb3858 100644 --- a/lib/routes/aicaijing/index.tsx +++ b/lib/routes/aicaijing/index.tsx @@ -2,18 +2,27 @@ import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/:category?/:id?', - name: 'Unknown', - maintainers: [], + path: '/latest', + categories: ['finance'], + example: '/aicaijing/latest', + radar: [ + { + source: ['www.aicaijing.com/'], + target: '/latest', + }, + ], + name: '最新文章', + maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { - const category = ctx.req.param('category') ?? 'latest'; +export async function handler(ctx) { + const category = getSubPath(ctx).split('/', 2)[1]; const id = ctx.req.param('id') ?? 14; const titles = { diff --git a/lib/routes/aicaijing/information.ts b/lib/routes/aicaijing/information.ts new file mode 100644 index 000000000000..fa00cb2af870 --- /dev/null +++ b/lib/routes/aicaijing/information.ts @@ -0,0 +1,38 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/information/:id?', + categories: ['finance'], + example: '/aicaijing/information/14', + parameters: { + id: '栏目 id,可在对应栏目页 URL 中找到,默认为 14,即热点最新', + }, + description: `| 栏目 id | 栏目 | +| ------- | ----------- | +| 14 | 热点 - 最新 | +| 5 | 热点 - 科技 | +| 9 | 热点 - 消费 | +| 7 | 热点 - 出行 | +| 13 | 热点 - 文娱 | +| 10 | 热点 - 教育 | +| 25 | 热点 - 地产 | +| 11 | 热点 - 更多 | +| 28 | 深度 - 出行 | +| 29 | 深度 - 科技 | +| 31 | 深度 - 消费 | +| 33 | 深度 - 教育 | +| 34 | 深度 - 更多 | +| 8 | 深度 - 地产 | +| 6 | 深度 - 文娱 |`, + radar: [ + { + source: ['www.aicaijing.com/information/:id', 'www.aicaijing.com/'], + target: '/information/:id?', + }, + ], + name: '热点 & 深度', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/aicaijing/recommend.ts b/lib/routes/aicaijing/recommend.ts new file mode 100644 index 000000000000..8b106d607d97 --- /dev/null +++ b/lib/routes/aicaijing/recommend.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/recommend', + categories: ['finance'], + example: '/aicaijing/recommend', + radar: [ + { + source: ['www.aicaijing.com/'], + target: '/recommend', + }, + ], + name: '推荐资讯', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/aijishu/index.ts b/lib/routes/aijishu/index.ts index 1184967c33df..42c446b04491 100644 --- a/lib/routes/aijishu/index.ts +++ b/lib/routes/aijishu/index.ts @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: '频道、专栏、用户', - maintainers: [], + maintainers: ['bigfei'], handler, description: `| type | 说明 | | ------- | ---- | diff --git a/lib/routes/aisixiang/toplist.ts b/lib/routes/aisixiang/toplist.ts index dd225c45a4fc..a4908ea578e6 100644 --- a/lib/routes/aisixiang/toplist.ts +++ b/lib/routes/aisixiang/toplist.ts @@ -7,8 +7,11 @@ import { parseDate } from '@/utils/parse-date'; import { ossUrl, ProcessFeed, rootUrl } from './utils'; export const route: Route = { - path: ['/ranking/:id?/:period?', '/toplist/:id?/:period?'], - name: 'Unknown', + path: '/toplist/:id?/:period?', + categories: ['reading'], + example: '/aisixiang/toplist/1/7', + parameters: { id: '类型', period: '范围, 仅适用于点击排行榜, 可选一天(1),一周(7),一月(30),所有(-1),默认为一天' }, + name: '排行', maintainers: ['HenryQW', 'nczitzk'], handler, description: `| 文章点击排行 | 最近更新文章 | 文章推荐排行 | diff --git a/lib/routes/aljazeera/index.tsx b/lib/routes/aljazeera/index.tsx index 1e64c901373e..5905d5fcaaaa 100644 --- a/lib/routes/aljazeera/index.tsx +++ b/lib/routes/aljazeera/index.tsx @@ -35,14 +35,45 @@ const renderDescription = (image, description) => ); export const route: Route = { - path: '*', - name: 'Unknown', + path: '/:language?/:category{.+}?', + categories: ['traditional-media'], + example: '/aljazeera/english/news', + parameters: { + language: 'Language, see below, arabic by default, as Arabic', + category: 'Category, can be found in URL, homepage by default', + }, + description: `Language + +| Arabic | Chinese | English | +| ------ | ------- | ------- | +| arabic | chinese | english | + +::: tip +If you subscribe to [Al Jazeera English - Economy](https://www.aljazeera.com/economy), whose language is \`english\` and whose path is \`economy\`, you can get the route as [\`/aljazeera/english/economy\`](https://rsshub.app/aljazeera/english/economy) + +If you subscribe to [Al Jazeera Chinese - Political](https://chinese.aljazeera.net/news/political) with language \`chinese\` and path \`news/political\`, you can get the route as [\`/aljazeera/chinese/news/political\`](https://rsshub.app/aljazeera/chinese/news/political) +:::`, + radar: [ + { + source: ['www.aljazeera.com/:category', 'www.aljazeera.com/'], + target: '/english/:category', + }, + { + source: ['www.aljazeera.net/:category', 'www.aljazeera.net/'], + target: '/arabic/:category', + }, + { + source: ['chinese.aljazeera.net/:category', 'chinese.aljazeera.net/'], + target: '/chinese/:category', + }, + ], + name: 'News', maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { - const params = getSubPath(ctx) === '/' ? ['arabic'] : getSubPath(ctx).replace(/^\//, '').split('/'); +export async function handler(ctx) { + const params = getSubPath(ctx).split('/').filter(Boolean); if (!Object.hasOwn(languages, params[0])) { params.unshift('arabic'); diff --git a/lib/routes/aljazeera/rss.ts b/lib/routes/aljazeera/rss.ts new file mode 100644 index 000000000000..d31f95809318 --- /dev/null +++ b/lib/routes/aljazeera/rss.ts @@ -0,0 +1,34 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/:language?/rss', + categories: ['traditional-media'], + example: '/aljazeera/english/rss', + parameters: { + language: 'Language, see below, arabic by default, as Arabic', + }, + description: `Language + +| Arabic | Chinese | English | +| ------ | ------- | ------- | +| arabic | chinese | english | + +::: tip +There is no RSS source for Al Jazeera Chinese, returning homepage content by default +:::`, + radar: [ + { + source: ['www.aljazeera.com/xml/rss/all.xml', 'www.aljazeera.com/'], + target: '/english/rss', + }, + { + source: ['www.aljazeera.net/rss', 'www.aljazeera.net/'], + target: '/arabic/rss', + }, + ], + name: 'Official RSS', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/aljazeera/tag.ts b/lib/routes/aljazeera/tag.ts new file mode 100644 index 000000000000..7509769b6b94 --- /dev/null +++ b/lib/routes/aljazeera/tag.ts @@ -0,0 +1,39 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/:language?/tag/:id', + categories: ['traditional-media'], + example: '/aljazeera/english/tag/science-and-technology', + parameters: { + language: 'Language, see below, arabic by default, as Arabic', + id: 'Tag id, can be found in URL', + }, + description: `Language + +| Arabic | Chinese | English | +| ------ | ------- | ------- | +| arabic | chinese | english | + +::: tip +If you subscribe to [Al Jazeera English - Science and Technology](https://www.aljazeera.com/tag/science-and-technology), whose language is \`english\` and whose path is \`science-and-technology\`, you can get the route as [\`/aljazeera/english/tag/science-and-technology\`](https://rsshub.app/aljazeera/english/tag/science-and-technology) +:::`, + radar: [ + { + source: ['www.aljazeera.com/tag/:id', 'www.aljazeera.com/'], + target: '/english/tag/:id', + }, + { + source: ['www.aljazeera.net/tag/:id', 'www.aljazeera.net/'], + target: '/arabic/tag/:id', + }, + { + source: ['chinese.aljazeera.net/tag/:id', 'chinese.aljazeera.net/'], + target: '/chinese/tag/:id', + }, + ], + name: 'Tag', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/amazon/awsblogs.ts b/lib/routes/amazon/awsblogs.ts index c9257551569f..c2911e4c170c 100644 --- a/lib/routes/amazon/awsblogs.ts +++ b/lib/routes/amazon/awsblogs.ts @@ -4,7 +4,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/awsblogs/:locale?', - name: 'Unknown', + categories: ['blog'], + example: '/amazon/awsblogs', + parameters: { + locale: 'Blog posts in a specified language, only the following options are supported. Default `zh_CN`', + }, + description: `| zh\\_CN | en\\_US | fr\\_FR | de\\_DE | ja\\_JP | ko\\_KR | pt\\_BR | es\\_ES | ru\\_RU | id\\_ID | tr\\_TR | +| ------- | ------- | ------ | ------ | -------- | ------ | ---------- | ------- | ------- | ---------- | ------- | +| Chinese | English | French | German | Japanese | Korean | Portuguese | Spanish | Russian | Indonesian | Turkish |`, + name: 'AWS Blogs', maintainers: ['HankChow'], handler, }; diff --git a/lib/routes/amazon/kindle-software-updates.tsx b/lib/routes/amazon/kindle-software-updates.tsx index 85510f647dcb..a26c078831c2 100644 --- a/lib/routes/amazon/kindle-software-updates.tsx +++ b/lib/routes/amazon/kindle-software-updates.tsx @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: 'Kindle Software Updates', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/aqara/news.ts b/lib/routes/aqara/news.ts index 6017ad4a0259..e28bfe468db5 100644 --- a/lib/routes/aqara/news.ts +++ b/lib/routes/aqara/news.ts @@ -7,8 +7,10 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/cn/news', - name: 'Unknown', - maintainers: [], + categories: ['other'], + example: '/aqara/cn/news', + name: '新闻', + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/aqara/post.tsx b/lib/routes/aqara/post.tsx index d692b1eb6ac4..bcdc2aeab7c6 100644 --- a/lib/routes/aqara/post.tsx +++ b/lib/routes/aqara/post.tsx @@ -2,14 +2,22 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['other'], + example: '/aqara/en/category/press-release', + parameters: { path: '路径,默认为首页' }, + description: `路径处填写对应页面 URL 中 \`https://aqara.com/\` 后的字段,形如 \`:region/category/:id\` 或 \`:region/tag/:id\`。 + +| 参数 | 说明 | +| ------ | -------------------------------------------------------- | +| region | 地区 id,可在对应分类页 URL 中找到,默认为 en,即 Global | +| id | 分类 id 或标签 id,可在对应分类页或标签页 URL 中找到 |`, + name: '分类、标签', + maintainers: ['nczitzk'], handler, }; @@ -24,7 +32,7 @@ async function handler(ctx) { let currentUrl = rootUrl; let apiUrl = new URL(`${apiSlug}/posts?_embed=true&per_page=${limit}`, rootUrl).href; - const filterMatches = getSubPath(ctx).match(/^\/([^/]*)\/([^/]*)\/(.*)$/); + const filterMatches = (ctx.req.param('path') ?? '').match(/^([^/]*)\/([^/]*)\/(.*)$/); if (filterMatches) { const filterRegion = filterMatches[1]; diff --git a/lib/routes/aqara/region.ts b/lib/routes/aqara/region.ts index b321cfeb5449..d8954d3c94f8 100644 --- a/lib/routes/aqara/region.ts +++ b/lib/routes/aqara/region.ts @@ -2,8 +2,21 @@ import type { Route } from '@/types'; export const route: Route = { path: '/:region/:type?', - name: 'Unknown', - maintainers: [], + categories: ['other'], + example: '/aqara/en/news', + parameters: { + region: '地区 id,可在对应新闻页 URL 中找到,默认为 en,即 Global', + type: '类型,见下表,默认为 news,即新闻', + }, + description: `| 中国 / 大陆 | 대한민국 | Europe | United States | Russia | Global | +| ----------- | -------- | ------ | ------------- | ------ | ------ | +| cn | kr | eu | us | ru | en | + +| 新闻 | 博客 | +| ---- | ---- | +| news | blog |`, + name: '新闻、博客', + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/arcteryx/new-arrivals.ts b/lib/routes/arcteryx/new-arrivals.ts index 18ff9acdfca0..72cf961de5f0 100644 --- a/lib/routes/arcteryx/new-arrivals.ts +++ b/lib/routes/arcteryx/new-arrivals.ts @@ -23,7 +23,7 @@ export const route: Route = { }, ], name: 'New Arrivals', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: `Country diff --git a/lib/routes/arcteryx/outlet.ts b/lib/routes/arcteryx/outlet.ts index 86f011f411c1..222d51e0929b 100644 --- a/lib/routes/arcteryx/outlet.ts +++ b/lib/routes/arcteryx/outlet.ts @@ -23,7 +23,7 @@ export const route: Route = { }, ], name: 'Outlet', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: `Country diff --git a/lib/routes/arcteryx/regear-new-arrivals.tsx b/lib/routes/arcteryx/regear-new-arrivals.tsx index 7394abf81ec0..bd6b4773ea1e 100644 --- a/lib/routes/arcteryx/regear-new-arrivals.tsx +++ b/lib/routes/arcteryx/regear-new-arrivals.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'Regear New Arrivals', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'regear.arcteryx.com/shop/new-arrivals', }; diff --git a/lib/routes/asiantolick/category.ts b/lib/routes/asiantolick/category.ts new file mode 100644 index 000000000000..5f62ab65a1a8 --- /dev/null +++ b/lib/routes/asiantolick/category.ts @@ -0,0 +1,35 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/category/:id', + categories: ['picture'], + example: '/asiantolick/category/90', + parameters: { + id: 'Category id, can be found in URL', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['asiantolick.com/category-:id'], + target: '/category/:id', + }, + ], + name: 'Category', + maintainers: ['nczitzk'], + handler, + url: 'asiantolick.com/', + description: `| Category | id | +| ---------- | ---- | +| Lolita | 90 | +| Hot Sister | 91 | +| Cosplay | 1030 | +| Sexy | 93 | +| Others | 94 | +| Thailand | 99 | +| Magazine | 100 | +| Hard Sexy | 103 |`, +}; diff --git a/lib/routes/asiantolick/index.ts b/lib/routes/asiantolick/index.ts index 04b58df8fe56..d9996974d2dc 100644 --- a/lib/routes/asiantolick/index.ts +++ b/lib/routes/asiantolick/index.ts @@ -2,21 +2,24 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '/:category{.+}?', + path: '/', + categories: ['picture'], + example: '/asiantolick', radar: [ { source: ['asiantolick.com/'], - target: '', + target: '/', }, ], - name: 'Unknown', - maintainers: [], + name: 'Top rated', + maintainers: ['nczitzk'], handler, url: 'asiantolick.com/', features: { @@ -24,23 +27,21 @@ export const route: Route = { }, }; -async function handler(ctx) { - const category = ctx.req.param('category'); +export async function handler(ctx) { + const category = getSubPath(ctx).slice(1); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 24; const rootUrl = 'https://asiantolick.com'; const apiUrl = new URL('ajax/buscar_posts.php', rootUrl).href; - const currentUrl = new URL(category?.replace(/^(tag|category)?\/(\d+)/, '$1-$2') ?? '', rootUrl).href; + const currentUrl = new URL(category.replace(/^(tag|category)?\/(\d+)/, '$1-$2'), rootUrl).href; const searchParams = {}; - const matches = category?.match(/^(tag|category|search|page)?[/-]?(\w+)/) ?? undefined; + const matches = category.match(/^(tag|category|search|page)?[/-]?(\w+)/); if (matches) { const key = matches[1] === 'category' ? 'cat' : matches[1]; const value = matches[2]; searchParams[key] = value; - } else if (category) { - searchParams.page = 'news'; } const { data: response } = await got(apiUrl, { diff --git a/lib/routes/asiantolick/page.ts b/lib/routes/asiantolick/page.ts new file mode 100644 index 000000000000..9e77c05ff279 --- /dev/null +++ b/lib/routes/asiantolick/page.ts @@ -0,0 +1,25 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/page/:id', + categories: ['picture'], + example: '/asiantolick/page/news', + parameters: { + id: 'Page id', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['asiantolick.com/page/:id'], + target: '/page/:id', + }, + ], + name: 'Page', + maintainers: ['nczitzk'], + handler, + url: 'asiantolick.com/', +}; diff --git a/lib/routes/asiantolick/search.ts b/lib/routes/asiantolick/search.ts new file mode 100644 index 000000000000..0e4312475b7d --- /dev/null +++ b/lib/routes/asiantolick/search.ts @@ -0,0 +1,25 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/search/:keyword', + categories: ['picture'], + example: '/asiantolick/search/lolita', + parameters: { + keyword: 'Keyword', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['asiantolick.com/search/:keyword'], + target: '/search/:keyword', + }, + ], + name: 'Search', + maintainers: ['nczitzk'], + handler, + url: 'asiantolick.com/', +}; diff --git a/lib/routes/asiantolick/tag.ts b/lib/routes/asiantolick/tag.ts new file mode 100644 index 000000000000..8f5dba395ec9 --- /dev/null +++ b/lib/routes/asiantolick/tag.ts @@ -0,0 +1,64 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:id', + categories: ['picture'], + example: '/asiantolick/tag/1045', + parameters: { + id: 'Tag id, can be found in URL', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['asiantolick.com/tag-:id'], + target: '/tag/:id', + }, + ], + name: 'Tag', + maintainers: ['nczitzk'], + handler, + url: 'asiantolick.com/', + description: `| Aidol | Anal | Babe | Big Boobs | Big Pussy | +| ----- | ---- | ---- | --------- | --------- | +| 2310 | 2233 | 1385 | 1106 | 1722 | + +| Bikini | Blonde | blowjob | Close Up | Creamy Pussy | +| ------ | ------ | ------- | -------- | ------------ | +| 1206 | 2244 | 2167 | 2267 | 1117 | + +| cum | Cute Girl | Dildo | Ebony | Feet | +| ---- | --------- | ----- | ----- | ---- | +| 2163 | 1090 | 1082 | 2245 | 1323 | + +| fetish | fingers | Fox Tail | Glasses | Hairy Pussy | +| ------ | ------- | -------- | ------- | ----------- | +| 2219 | 2197 | 1540 | 1268 | 1099 | + +| Interracial | Lesbian | licked | Loli | Maid | +| ----------- | ------- | ------ | ---- | ---- | +| 2284 | 1080 | 2208 | 1045 | 1072 | + +| Masturbate | Milf | non-nude | Nude | Nurse | +| ---------- | ---- | -------- | ---- | ----- | +| 1081 | 1705 | 2307 | 1393 | 1552 | + +| Office | oiled | Outdoor | Pink Pussy | Pink Tits | +| ------ | ----- | ------- | ---------- | --------- | +| 1724 | 2176 | 2250 | 1161 | 1498 | + +| Public | School Girl | Short Hair | skirt | Small Girl | +| ------ | ----------- | ---------- | ----- | ---------- | +| 1277 | 1046 | 1160 | 2159 | 1103 | + +| Socks | sucking | Tattoo | Teen | Tiny Tits | +| ----- | ------- | ------ | ---- | --------- | +| 2256 | 2199 | 1593 | 1036 | 2251 | + +| Underwear | Uniform | wet | Young | +| --------- | ------- | ---- | ----- | +| 1324 | 1084 | 2179 | 1098 |`, +}; diff --git a/lib/routes/bad/index.ts b/lib/routes/bad/index.ts index 0bd8050db006..ac4654c4d158 100644 --- a/lib/routes/bad/index.ts +++ b/lib/routes/bad/index.ts @@ -1,21 +1,27 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['new-media'], + example: '/bad', + parameters: { path: '路径,默认为首页热门' }, + description: `若订阅 [每日热点 - 最新](https://bad.news/tag/每日热点/sort-new),网址为 \`https://bad.news/tag/每日热点/sort-new\`。截取 \`https://bad.news\` 到末尾的部分 \`/tag/每日热点/sort-new\` 作为参数,此时路由为 [\`/bad/tag/每日热点/sort-new\`](https://rsshub.app/bad/tag/每日热点/sort-new)。 + +若订阅子分类 [大陆资讯 - 热门](https://bad.news/tag/大陆资讯/sort-hot),网址为 \`https://bad.news/tag/大陆资讯/sort-hot\`。截取 \`https://bad.news\` 到末尾的部分 \`/tag/大陆资讯/sort-hot\` 作为参数,路由为 [\`/bad/tag/大陆资讯/sort-hot\`](https://rsshub.app/bad/tag/大陆资讯/sort-hot)。`, + name: '通用', + maintainers: ['nczitzk'], handler, }; async function handler(ctx) { const rootUrl = 'https://bad.news'; - const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '' : getSubPath(ctx)}`; + const path = ctx.req.param('path'); + const currentUrl = path ? `${rootUrl}/${path}` : rootUrl; const response = await got({ method: 'get', diff --git a/lib/routes/bast/index.ts b/lib/routes/bast/index.ts index ade47e47361a..462a95673084 100644 --- a/lib/routes/bast/index.ts +++ b/lib/routes/bast/index.ts @@ -2,24 +2,36 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['new-media'], + example: '/bast/col/col31266', + parameters: { path: '路径,默认为通知公告' }, + features: { + antiCrawler: true, + }, + name: '通用', + maintainers: ['nczitzk'], + description: `路径处填写对应页面 URL 中 \`https://www.bast.net.cn/\` 后的字段。下面是两个例子。 + +若订阅 [通知公告](https://www.bast.net.cn/col/col31266) 则将对应页面 URL <https://www.bast.net.cn/col/col31266> 中 \`https://www.bast.net.cn/\` 后的字段 \`col/col31266\` 作为路径填入。此时路由为 [\`/bast/col/col31266\`](https://rsshub.app/bast/col/col31266) + +若订阅 [学术动态](https://www.bast.net.cn/col/col31530) 则将对应页面 URL <https://www.bast.net.cn/col/col31530> 中 \`https://www.bast.net.cn/\` 后的字段 \`col/col31530\` 作为路径填入。此时路由为 [\`/bast/col/col31530\`](https://rsshub.app/bast/col/col31530) + +如果路由符合 \`/col/colXXXXX\` 的格式,可以由 [\`/bast/col/col31266\`](https://rsshub.app/bast/col/col31266) 精简为 [\`/bast/31266\`](https://rsshub.app/bast/31266)`, handler, }; async function handler(ctx) { - const colPath = getSubPath(ctx).replace(/^\//, '') || '32942'; + const colPath = ctx.req.param('path') ?? '32942'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; const rootUrl = 'https://www.bast.net.cn'; - const currentUrl = `${rootUrl}/${Number.isNaN(colPath) ? colPath : `col/col${colPath}`}/`; + const currentUrl = `${rootUrl}/${Number.isNaN(Number(colPath)) ? colPath : `col/col${colPath}`}/`; const response = await got({ method: 'get', diff --git a/lib/routes/bellroy/new-releases.ts b/lib/routes/bellroy/new-releases.ts index a706ef4fbed1..7b3925cd8047 100644 --- a/lib/routes/bellroy/new-releases.ts +++ b/lib/routes/bellroy/new-releases.ts @@ -20,7 +20,7 @@ export const route: Route = { }, ], name: 'New Releases', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'bellroy.com/collection/new-releases', }; diff --git a/lib/routes/bilibili/video-all.ts b/lib/routes/bilibili/video-all.ts index ea5e55655ee6..574b17e1fa20 100644 --- a/lib/routes/bilibili/video-all.ts +++ b/lib/routes/bilibili/video-all.ts @@ -8,7 +8,7 @@ import utils from './utils'; export const route: Route = { path: '/user/video-all/:uid/:embed?', name: '用户所有视频', - maintainers: [], + maintainers: ['CcccFz'], handler, example: '/bilibili/user/video-all/2267573', parameters: { diff --git a/lib/routes/biodiscover/index.ts b/lib/routes/biodiscover/index.ts deleted file mode 100644 index 9f576d06fa32..000000000000 --- a/lib/routes/biodiscover/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { load } from 'cheerio'; - -import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; - -export const route: Route = { - path: '/:channel?', - radar: [ - { - source: ['www.biodiscover.com/:channel'], - target: '/:channel', - }, - ], - name: 'Unknown', - maintainers: ['aidistan'], - handler, -}; - -async function handler(ctx) { - const channel = ctx.req.param('channel'); - const listUrl = 'http://www.biodiscover.com/' + channel; - const response = await got({ url: listUrl }); - const $ = load(response.data); - - const items = $('.new_list .newList_box') - .toArray() - .map((item) => ({ - pubDate: parseDate($(item).find('.news_flow_tag .times').text().trim()), - link: 'http://www.biodiscover.com' + $(item).find('h2 a').attr('href'), - })); - - return { - title: '生物探索 - ' + $('.header li.sel a').text(), - link: listUrl, - description: $('meta[name=description]').attr('content'), - item: await Promise.all( - items.map((item) => - cache.tryGet(item.link, async () => { - const detailResponse = await got({ url: item.link }); - const $ = load(detailResponse.data); - - // remove sharing info if exists - const lastNode = $('.main_info').children().last(); - if (lastNode.css('display') === 'none') { - lastNode.remove(); - } - - return { - title: $('h1').text().trim(), - description: $('.main_info').html(), - pubDate: item.pubDate, - link: item.link, - }; - }) - ) - ), - }; -} diff --git a/lib/routes/biodiscover/namespace.ts b/lib/routes/biodiscover/namespace.ts deleted file mode 100644 index e81481e842eb..000000000000 --- a/lib/routes/biodiscover/namespace.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Namespace } from '@/types'; - -export const namespace: Namespace = { - name: 'biodiscover.com 生物探索', - url: 'www.biodiscover.com', - lang: 'zh-CN', -}; diff --git a/lib/routes/biquge/index.ts b/lib/routes/biquge/index.ts index d2d27c128035..29286c71c6dd 100644 --- a/lib/routes/biquge/index.ts +++ b/lib/routes/biquge/index.ts @@ -5,7 +5,6 @@ import { config } from '@/config'; import ConfigNotFoundError from '@/errors/types/config-not-found'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -29,15 +28,41 @@ const allowHost = new Set([ ]); export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:url{.+}', + categories: ['reading'], + example: '/biquge/http://www.biqu5200.net/0_7/', + parameters: { url: '小说 Url,即对应小说详情页的 Url,可在地址栏中找到' }, + features: { + antiCrawler: true, + }, + name: '小说', + maintainers: ['nczitzk'], + description: `::: tip + +#### 使用方法 + +如订阅 [《大主宰》](http://www.biqu5200.net/0_7/),此时在 [biqu5200.net](http://www.biqu5200.net) 中查询得到对应小说详情页 URL 为 \`http://www.biqu5200.net/0_7/\`。此时,路由为 [\`/biquge/http://www.biqu5200.net/0_7/\`](https://rsshub.app/biquge/http://www.biqu5200.net/0_7/) + +又如同样订阅 [《大主宰》](https://www.shuquge.com/txt/70/index.html),此时在 [shuquge.com](https://www.shuquge.com) 中查询得到对应小说详情页 URL 为 \`https://www.shuquge.com/txt/70/index.html\`。此时,把末尾的 \`index.html\` 去掉,路由为 [\`/biquge/https://www.shuquge.com/txt/70/\`](https://rsshub.app/biquge/https://www.shuquge.com/txt/70/) + +#### 关于章节数 + +路由默认返回最新 **1** 个章节,如有需要一次性获取多个章节,可在路由后指定 \`limit\` 参数。如上面的例子:订阅 [《大主宰》](http://www.biqu5200.net/0_7/) 并获取最新的 **10** 个章节。此时,路由为 [\`/biquge/http://www.biqu5200.net/0_7/?limit=10\`](https://rsshub.app/biquge/http://www.biqu5200.net/0_7/?limit=10) + +需要注意的是,单次获取的所有章节更新时间统一设定为最新章节的更新时间。也就是说,获取最新的 **10** 个章节时,除了最新 **1** 个章节的更新时间是准确的(和网站一致的),其他 **9** 个章节的更新时间是不准确的。 + +另外,若设置获取章节数目过多,可能会触发网站反爬,导致路由不可用。 +::: + +::: warning +上方列举的网址可能部分不可用,这取决于该网站的维护者是否持续运营网站。请选择可以正常访问的网址,获取更新的前提是该网站可以正常访问。 +:::`, handler, }; async function handler(ctx) { - const rootUrl = getSubPath(ctx).split('/').slice(1, 4).join('/'); - const currentUrl = getSubPath(ctx).slice(1); + const currentUrl = ctx.req.param('url'); + const rootUrl = currentUrl.split('/').slice(0, 3).join('/'); if (!config.feature.allow_user_supply_unsafe_domain && !allowHost.has(new URL(rootUrl).hostname)) { throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } diff --git a/lib/routes/bnu/fdy.ts b/lib/routes/bnu/fdy.ts index b94dbfc775ab..1c0cdb53d7e1 100644 --- a/lib/routes/bnu/fdy.ts +++ b/lib/routes/bnu/fdy.ts @@ -7,9 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/fdy/:path{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/bnu/fdy/tzgg/dwjs', + parameters: { path: '路径,默认为 `tzgg`' }, + name: '党委学生工作部辅导员发展中心', + maintainers: ['TonyRL'], handler, + description: `路径处填写对应页面 URL 中 \`https://fdy.bnu.edu.cn/\` 和 \`/index.htm\` 之间的字段。下面是一个例子。 + +若订阅 [通知公告 > 队伍建设](https://fdy.bnu.edu.cn/tzgg/dwjs/index.htm) 则将对应页面 URL <https://fdy.bnu.edu.cn/tzgg/dwjs/index.htm> 中 \`https://fdy.bnu.edu.cn/\` 和 \`/index.htm\` 之间的字段 \`tzgg/dwjs\` 作为路径填入。此时路由为 [\`/bnu/fdy/tzgg/dwjs\`](https://rsshub.app/bnu/fdy/tzgg/dwjs)`, }; async function handler(ctx) { diff --git a/lib/routes/bnu/lib.ts b/lib/routes/bnu/lib.ts index 12c815ad8f96..e5eda46adbe9 100644 --- a/lib/routes/bnu/lib.ts +++ b/lib/routes/bnu/lib.ts @@ -7,15 +7,21 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/lib/:category?', + categories: ['university'], + example: '/bnu/lib/zydt', + parameters: { category: '分类,见下表,默认为 `zydt`' }, radar: [ { source: ['www.lib.bnu.edu.cn/:category/index.htm'], target: '/lib/:category', }, ], - name: 'Unknown', + name: '图书馆通知', maintainers: ['TonyRL'], handler, + description: `| 资源动态 | 新闻动态 | 系列讲座 | +| -------- | -------- | -------- | +| zydt | xwdt | xljz1 |`, }; async function handler(ctx) { diff --git a/lib/routes/brooklynmuseum/exhibitions.ts b/lib/routes/brooklynmuseum/exhibitions.ts index 9e7980ccf52b..0c55e9efb1bc 100644 --- a/lib/routes/brooklynmuseum/exhibitions.ts +++ b/lib/routes/brooklynmuseum/exhibitions.ts @@ -15,7 +15,7 @@ export const route: Route = { supportScihub: false, }, name: 'Exhibitions', - maintainers: [], + maintainers: ['chazeon'], handler, }; diff --git a/lib/routes/bwsg/index.ts b/lib/routes/bwsg/index.ts index 2b9cfc04a28e..8a1b1eca7bf6 100644 --- a/lib/routes/bwsg/index.ts +++ b/lib/routes/bwsg/index.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Data, DataItem, Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import ofetch from '@/utils/ofetch'; const FEED_TITLE = 'Immobilien - BWSG' as const; @@ -13,7 +12,8 @@ const BASE_URL = `${SITE_URL}/immobilien/immobilie-suchen/`; export const route: Route = { name: 'Angebote', example: '/bwsg/_vermarktungsart=miete&_objektart=wohnung&_zimmer=2,3&_wohnflaeche=45,70&_plz=1210,1220', - path: '*', + path: '/:path{.+}?', + parameters: { path: 'Query parameters of the search, without the leading `?`, see the description below' }, maintainers: ['sk22'], categories: ['other'], description: `Copy the query parameters for your <https://www.bwsg.at/immobilien/immobilie-suchen> @@ -28,7 +28,7 @@ RSS feed might not get all items. :::`, async handler(ctx) { - let params = getSubPath(ctx).slice(1); + let params = ctx.req.param('path') ?? ''; if (params.startsWith('&')) { params = params.slice(1); } diff --git a/lib/routes/bytes/bytes.ts b/lib/routes/bytes/bytes.ts index 906306c1106a..f348a358e397 100644 --- a/lib/routes/bytes/bytes.ts +++ b/lib/routes/bytes/bytes.ts @@ -8,14 +8,17 @@ const currentURL = 'https://bytes.dev/archives'; export const route: Route = { path: '/', + categories: ['programming'], + example: '/bytes', radar: [ { source: ['bytes.dev/archives', 'bytes.dev/'], target: '', }, ], - name: 'Unknown', + name: 'Your weekly dose of JS', maintainers: ['meixger'], + description: 'Staying informed on the JavaScript ecosystem has never been so entertaining. Delivered every Monday and Thursday, for free.', handler, url: 'bytes.dev/archives', }; diff --git a/lib/routes/caam/index.ts b/lib/routes/caam/index.ts index 79090a018c82..2cd763d78162 100644 --- a/lib/routes/caam/index.ts +++ b/lib/routes/caam/index.ts @@ -7,7 +7,117 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:category?', - name: 'Unknown', + categories: ['other'], + example: '/caam/1', + parameters: { category: '分类,见下表,默认为首页' }, + description: `| [首页](http://www.caam.org.cn/chn/1/cate_1/list_1.html) | [行业要闻](http://www.caam.org.cn/chn/1/cate_2/list_1.html) | [协会活动](http://www.caam.org.cn/chn/1/cate_3/list_1.html) | [会员专区](http://www.caam.org.cn/chn/1/cate_4/list_1.html) | [协会概况](http://www.caam.org.cn/chn/1/cate_5/list_1.html) | [协会概况 - 会员单位](http://www.caam.org.cn/chn/1/cate_6/list_1.html) | +| ------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------- | +| 1 | 2 | 3 | 4 | 5 | 6 | + +| [协会概况 - 协会简介](http://www.caam.org.cn/chn/1/cate_7/list_1.html) | [协会概况 - 协会章程](http://www.caam.org.cn/chn/1/cate_8/list_1.html) | [协会概况 - 组织机构](http://www.caam.org.cn/chn/1/cate_9/list_1.html) | [协会概况 - 主要职责](http://www.caam.org.cn/chn/1/cate_10/list_1.html) | [协会概况 - 协会荣誉](http://www.caam.org.cn/chn/1/cate_11/list_1.html) | [协会概况 - 分支机构](http://www.caam.org.cn/chn/1/cate_12/list_1.html) | +| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 7 | 8 | 9 | 10 | 11 | 12 | + +| [协会概况 - 分支机构 - 机构介绍](http://www.caam.org.cn/chn/1/cate_13/list_1.html) | [协会概况 - 分支机构 - 管理办法](http://www.caam.org.cn/chn/1/cate_14/list_1.html) | [协会概况 - 分支机构 - 业务范围](http://www.caam.org.cn/chn/1/cate_15/list_1.html) | [协会工作](http://www.caam.org.cn/chn/1/cate_16/list_1.html) | [协会工作 - 协会动态](http://www.caam.org.cn/chn/1/cate_17/list_1.html) | [协会工作 - 分支机构动态](http://www.caam.org.cn/chn/1/cate_18/list_1.html) | +| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| 13 | 14 | 15 | 16 | 17 | 18 | + +| [协会工作 - 重点工作](http://www.caam.org.cn/chn/1/cate_19/list_1.html) | [协会工作 - 法规标准](http://www.caam.org.cn/chn/1/cate_20/list_1.html) | [协会工作 - 信用服务](http://www.caam.org.cn/chn/1/cate_21/list_1.html) | [协会工作 - 会员服务](http://www.caam.org.cn/chn/1/cate_22/list_1.html) | [协会工作 - 政策研究](http://www.caam.org.cn/chn/1/cate_23/list_1.html) | [协会工作 - 协调合作](http://www.caam.org.cn/chn/1/cate_24/list_1.html) | +| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 19 | 20 | 21 | 22 | 23 | 24 | + +| [协会工作 - 展会信息](http://www.caam.org.cn/chn/1/cate_25/list_1.html) | [协会工作 - 国际合作](http://www.caam.org.cn/chn/1/cate_26/list_1.html) | [协会工作 - 会员动态](http://www.caam.org.cn/chn/1/cate_27/list_1.html) | [协会工作 - 行业培训](http://www.caam.org.cn/chn/1/cate_28/list_1.html) | [统计数据](http://www.caam.org.cn/chn/1/cate_29/list_1.html) | [统计数据 - 产销](http://www.caam.org.cn/chn/1/cate_30/list_1.html) | +| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------- | +| 25 | 26 | 27 | 28 | 29 | 30 | + +| [统计数据 - 产销 - 汽车](http://www.caam.org.cn/chn/1/cate_31/list_1.html) | [统计数据 - 产销 - 摩托车](http://www.caam.org.cn/chn/1/cate_32/list_1.html) | [统计数据 - 产销 - 零部件](http://www.caam.org.cn/chn/1/cate_33/list_1.html) | [统计数据 - 进出口](http://www.caam.org.cn/chn/1/cate_34/list_1.html) | [统计数据 - 进出口 - 汽车](http://www.caam.org.cn/chn/1/cate_35/list_1.html) | [统计数据 - 进出口 - 摩托车](http://www.caam.org.cn/chn/1/cate_36/list_1.html) | +| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| 31 | 32 | 33 | 34 | 35 | 36 | + +| [统计数据 - 进出口 - 零部件](http://www.caam.org.cn/chn/1/cate_37/list_1.html) | [统计数据 - 数据分析](http://www.caam.org.cn/chn/1/cate_38/list_1.html) | [统计数据 - 数据分析 - 国内数据](http://www.caam.org.cn/chn/1/cate_39/list_1.html) | [统计数据 - 数据分析 - 国外数据](http://www.caam.org.cn/chn/1/cate_40/list_1.html) | [统计数据 - 亚洲](http://www.caam.org.cn/chn/1/cate_41/list_1.html) | [统计数据 - 亚洲 - 日本](http://www.caam.org.cn/chn/1/cate_42/list_1.html) | +| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| 37 | 38 | 39 | 40 | 41 | 42 | + +| [统计数据 - 亚洲 - 泰国](http://www.caam.org.cn/chn/1/cate_43/list_1.html) | [统计数据 - 亚洲 - 印度](http://www.caam.org.cn/chn/1/cate_44/list_1.html) | [统计数据 - 亚洲 - 印尼](http://www.caam.org.cn/chn/1/cate_45/list_1.html) | [统计数据 - 亚洲 - 韩国](http://www.caam.org.cn/chn/1/cate_46/list_1.html) | [统计数据 - 亚洲 - 巴基斯坦](http://www.caam.org.cn/chn/1/cate_47/list_1.html) | [统计数据 - 亚洲 - 其他](http://www.caam.org.cn/chn/1/cate_48/list_1.html) | +| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | +| 43 | 44 | 45 | 46 | 47 | 48 | + +| [统计数据 - 欧洲](http://www.caam.org.cn/chn/1/cate_49/list_1.html) | [统计数据 - 欧洲 - 总汇](http://www.caam.org.cn/chn/1/cate_50/list_1.html) | [统计数据 - 欧洲 - 德国](http://www.caam.org.cn/chn/1/cate_51/list_1.html) | [统计数据 - 欧洲 - 英国](http://www.caam.org.cn/chn/1/cate_52/list_1.html) | [统计数据 - 欧洲 - 法国](http://www.caam.org.cn/chn/1/cate_53/list_1.html) | [统计数据 - 欧洲 - 意大利](http://www.caam.org.cn/chn/1/cate_54/list_1.html) | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| 49 | 50 | 51 | 52 | 53 | 54 | + +| [统计数据 - 欧洲 - 西班牙](http://www.caam.org.cn/chn/1/cate_55/list_1.html) | [统计数据 - 大洋洲](http://www.caam.org.cn/chn/1/cate_56/list_1.html) | [统计数据 - 大洋洲 - 澳大利亚](http://www.caam.org.cn/chn/1/cate_57/list_1.html) | [统计数据 - 大洋洲 - 新西兰](http://www.caam.org.cn/chn/1/cate_58/list_1.html) | [统计数据 - 北美洲](http://www.caam.org.cn/chn/1/cate_59/list_1.html) | [统计数据 - 北美洲 - 总汇](http://www.caam.org.cn/chn/1/cate_60/list_1.html) | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| 55 | 56 | 57 | 58 | 59 | 60 | + +| [统计数据 - 北美洲 - 美国](http://www.caam.org.cn/chn/1/cate_61/list_1.html) | [统计数据 - 南美洲](http://www.caam.org.cn/chn/1/cate_62/list_1.html) | [统计数据 - 南美洲 - 阿根廷](http://www.caam.org.cn/chn/1/cate_63/list_1.html) | [统计数据 - 南美洲 - 巴西](http://www.caam.org.cn/chn/1/cate_64/list_1.html) | [统计数据 - 南美洲 - 其他](http://www.caam.org.cn/chn/1/cate_65/list_1.html) | [统计数据 - 非洲](http://www.caam.org.cn/chn/1/cate_66/list_1.html) | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------- | +| 61 | 62 | 63 | 64 | 65 | 66 | + +| [文件公告](http://www.caam.org.cn/chn/1/cate_67/list_1.html) | [文件公告 - 协会文件](http://www.caam.org.cn/chn/1/cate_68/list_1.html) | [文件公告 - 公示公告](http://www.caam.org.cn/chn/1/cate_69/list_1.html) | [文件公告 - 会议通知](http://www.caam.org.cn/chn/1/cate_70/list_1.html) | [文件公告 - 分支机构文件](http://www.caam.org.cn/chn/1/cate_71/list_1.html) | [专题子站](http://www.caam.org.cn/chn/1/cate_72/list_1.html) | +| ------------------------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------ | +| 67 | 68 | 69 | 70 | 71 | 72 | + +| [专题子站 - 网站专题](http://www.caam.org.cn/chn/1/cate_73/list_1.html) | [专题子站 - 分支机构网站](http://www.caam.org.cn/chn/1/cate_74/list_1.html) | [专题子站 - 子网站](http://www.caam.org.cn/chn/1/cate_75/list_1.html) | [信息资料](http://www.caam.org.cn/chn/1/cate_76/list_1.html) | [信息资料 - 信息资料](http://www.caam.org.cn/chn/1/cate_77/list_1.html) | [信息资料 - 当前最热](http://www.caam.org.cn/chn/1/cate_78/list_1.html) | +| ----------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 73 | 74 | 75 | 76 | 77 | 78 | + +| [行业动态](http://www.caam.org.cn/chn/1/cate_79/list_1.html) | [行业动态 - 政府资讯](http://www.caam.org.cn/chn/1/cate_80/list_1.html) | [行业动态 - 企业新闻](http://www.caam.org.cn/chn/1/cate_81/list_1.html) | [行业动态 - 行业动态](http://www.caam.org.cn/chn/1/cate_82/list_1.html) | [行业动态 - 车市动态](http://www.caam.org.cn/chn/1/cate_83/list_1.html) | [行业动态 - 国内召回](http://www.caam.org.cn/chn/1/cate_84/list_1.html) | +| ------------------------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 79 | 80 | 81 | 82 | 83 | 84 | + +| [行业动态 - 国外召回](http://www.caam.org.cn/chn/1/cate_85/list_1.html) | [行业动态 - 汽车新技术](http://www.caam.org.cn/chn/1/cate_86/list_1.html) | [行业动态 - 市场环境](http://www.caam.org.cn/chn/1/cate_87/list_1.html) | [行业动态 - 国际动态](http://www.caam.org.cn/chn/1/cate_88/list_1.html) | [行业动态 - 国际动态 - 新闻](http://www.caam.org.cn/chn/1/cate_89/list_1.html) | [行业动态 - 国际动态 - 企业](http://www.caam.org.cn/chn/1/cate_90/list_1.html) | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| 85 | 86 | 87 | 88 | 89 | 90 | + +| [行业动态 - 产品资讯](http://www.caam.org.cn/chn/1/cate_91/list_1.html) | [行业动态 - 相关行业](http://www.caam.org.cn/chn/1/cate_92/list_1.html) | [行业政策](http://www.caam.org.cn/chn/1/cate_93/list_1.html) | [行业政策 - 最新政策](http://www.caam.org.cn/chn/1/cate_95/list_1.html) | [行业政策 - 政策解读](http://www.caam.org.cn/chn/1/cate_96/list_1.html) | [行业政策 - 国家政策](http://www.caam.org.cn/chn/1/cate_97/list_1.html) | +| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| 91 | 92 | 93 | 95 | 96 | 97 | + +| [行业政策 - 国家政策 - 产业政策](http://www.caam.org.cn/chn/1/cate_98/list_1.html) | [行业政策 - 国家政策 - 新能源汽车](http://www.caam.org.cn/chn/1/cate_99/list_1.html) | [行业政策 - 国家政策 - 节能环保](http://www.caam.org.cn/chn/1/cate_100/list_1.html) | [行业政策 - 国家政策 - 税收](http://www.caam.org.cn/chn/1/cate_101/list_1.html) | [行业政策 - 国家政策 - 进出口](http://www.caam.org.cn/chn/1/cate_102/list_1.html) | [行业政策 - 国家政策 - 其他](http://www.caam.org.cn/chn/1/cate_103/list_1.html) | +| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| 98 | 99 | 100 | 101 | 102 | 103 | + +| [行业政策 - 地方政策](http://www.caam.org.cn/chn/1/cate_104/list_1.html) | [行业政策 - 相关政策](http://www.caam.org.cn/chn/1/cate_105/list_1.html) | [行业政策 - 国外政策](http://www.caam.org.cn/chn/1/cate_106/list_1.html) | [行业政策 - 政策报道](http://www.caam.org.cn/chn/1/cate_107/list_1.html) | [标准法规](http://www.caam.org.cn/chn/1/cate_108/list_1.html) | [标准法规 - 标准政策](http://www.caam.org.cn/chn/1/cate_109/list_1.html) | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------ | +| 104 | 105 | 106 | 107 | 108 | 109 | + +| [标准法规 - 国家标准](http://www.caam.org.cn/chn/1/cate_110/list_1.html) | [标准法规 - 行业标准](http://www.caam.org.cn/chn/1/cate_111/list_1.html) | [标准法规 - 标准动态](http://www.caam.org.cn/chn/1/cate_112/list_1.html) | [标准法规 - 国际标准](http://www.caam.org.cn/chn/1/cate_113/list_1.html) | [标准法规 - 标准资料](http://www.caam.org.cn/chn/1/cate_114/list_1.html) | [基础资料](http://www.caam.org.cn/chn/1/cate_115/list_1.html) | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------- | +| 110 | 111 | 112 | 113 | 114 | 115 | + +| [信息资料 - 公众资料](http://www.caam.org.cn/chn/1/cate_116/list_1.html) | [信息资料 - 成品油](http://www.caam.org.cn/chn/1/cate_117/list_1.html) | [信息资料 - 成品油 - 国内市场](http://www.caam.org.cn/chn/1/cate_118/list_1.html) | [信息资料 - 成品油 - 国际市场](http://www.caam.org.cn/chn/1/cate_119/list_1.html) | [信息资料 - 保有量](http://www.caam.org.cn/chn/1/cate_120/list_1.html) | [信息资料 - 公路交通](http://www.caam.org.cn/chn/1/cate_121/list_1.html) | +| ------------------------------------------------------------------------ | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| 116 | 117 | 118 | 119 | 120 | 121 | + +| [信息资料 - 公路交通 - 公路客运量](http://www.caam.org.cn/chn/1/cate_122/list_1.html) | [信息资料 - 公路交通 - 旅客周转量](http://www.caam.org.cn/chn/1/cate_123/list_1.html) | [信息资料 - 公路交通 - 公路货运量](http://www.caam.org.cn/chn/1/cate_124/list_1.html) | [信息资料 - 经济环境](http://www.caam.org.cn/chn/1/cate_125/list_1.html) | [公告查询](http://www.caam.org.cn/chn/1/cate_126/list_1.html) | [公告查询 - 公告批文](http://www.caam.org.cn/chn/1/cate_127/list_1.html) | +| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------ | +| 122 | 123 | 124 | 125 | 126 | 127 | + +| [公告查询 - 公示公告](http://www.caam.org.cn/chn/1/cate_128/list_1.html) | [公告查询 - 节能与新能源](http://www.caam.org.cn/chn/1/cate_129/list_1.html) | [公告查询 - 公告动态](http://www.caam.org.cn/chn/1/cate_130/list_1.html) | [人物访谈](http://www.caam.org.cn/chn/1/cate_131/list_1.html) | [人物访谈 - 访谈列表](http://www.caam.org.cn/chn/1/cate_132/list_1.html) | [视频新闻](http://www.caam.org.cn/chn/1/cate_133/list_1.html) | +| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------- | +| 128 | 129 | 130 | 131 | 132 | 133 | + +| [视频新闻 - 热门视频](http://www.caam.org.cn/chn/1/cate_134/list_1.html) | [视频新闻 - 推荐视频](http://www.caam.org.cn/chn/1/cate_135/list_1.html) | [视频新闻 - 视频列表](http://www.caam.org.cn/chn/1/cate_136/list_1.html) | [统计数据 - 欧洲 - 俄罗斯](http://www.caam.org.cn/chn/1/cate_137/list_1.html) | [统计数据 - 欧洲 - 爱尔兰](http://www.caam.org.cn/chn/1/cate_138/list_1.html) | [统计数据 - 欧洲 - 丹麦](http://www.caam.org.cn/chn/1/cate_139/list_1.html) | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| 134 | 135 | 136 | 137 | 138 | 139 | + +| [统计数据 - 欧洲 - 其他](http://www.caam.org.cn/chn/1/cate_140/list_1.html) | [统计数据 - 大洋洲 - 其他](http://www.caam.org.cn/chn/1/cate_141/list_1.html) | [统计数据 - 北美洲 - 加拿大](http://www.caam.org.cn/chn/1/cate_142/list_1.html) | [统计数据 - 北美洲 - 其他](http://www.caam.org.cn/chn/1/cate_144/list_1.html) | [统计数据 - 北美洲 - 墨西哥](http://www.caam.org.cn/chn/1/cate_143/list_1.html) | [统计数据 - 非洲 - 南非](http://www.caam.org.cn/chn/1/cate_145/list_1.html) | +| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| 140 | 141 | 142 | 144 | 143 | 145 | + +| [统计数据 - 非洲 - 其他](http://www.caam.org.cn/chn/1/cate_146/list_1.html) | [协会概况 - 分支机构 - 机构名单](http://www.caam.org.cn/chn/1/cate_147/list_1.html) | [首页 - 轮播](http://www.caam.org.cn/chn/1/cate_148/list_1.html) | [标准法规 - 标准政策 - 轮播](http://www.caam.org.cn/chn/1/cate_149/list_1.html) | [信息资料 - 信息资料 - 轮播](http://www.caam.org.cn/chn/1/cate_150/list_1.html) | [行业动态 - 行业资讯 - 轮播](http://www.caam.org.cn/chn/1/cate_151/list_1.html) | +| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| 146 | 147 | 148 | 149 | 150 | 151 | + +| [行业政策 - 行业政策 - 轮播](http://www.caam.org.cn/chn/1/cate_152/list_1.html) | [协会工作 - 协会工作 - 轮播图](http://www.caam.org.cn/chn/1/cate_153/list_1.html) | [统计数据 - 数据统计 - 轮播图](http://www.caam.org.cn/chn/1/cate_154/list_1.html) | [Home](http://www.caam.org.cn/chn/1/cate_155/list_1.html) | [Home - Focus News](http://www.caam.org.cn/chn/1/cate_156/list_1.html) | [Home - Overview](http://www.caam.org.cn/chn/1/cate_157/list_1.html) | +| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------- | +| 152 | 153 | 154 | 155 | 156 | 157 | + +| [Home - News](http://www.caam.org.cn/chn/1/cate_158/list_1.html) | [Home - Member sign](http://www.caam.org.cn/chn/1/cate_161/list_1.html) | [车桥分会](http://www.caam.org.cn/chn/1/cate_162/list_1.html) | [上市公司委员会](http://www.caam.org.cn/chn/1/cate_183/list_1.html) | [上市公司委员会 - 单位动态](http://www.caam.org.cn/chn/1/cate_184/list_1.html) | [上市公司委员会 - 临时公告](http://www.caam.org.cn/chn/1/cate_185/list_1.html) | +| ---------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| 158 | 161 | 162 | 183 | 184 | 185 |`, + name: '分类', maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/caixin/blog.ts b/lib/routes/caixin/blog.ts index ce118899bf79..29f4674b3821 100644 --- a/lib/routes/caixin/blog.ts +++ b/lib/routes/caixin/blog.ts @@ -23,7 +23,7 @@ export const route: Route = { supportScihub: false, }, name: '用户博客', - maintainers: [], + maintainers: ['Maecenas'], handler, description: '通过提取文章全文,以提供比官方源更佳的阅读体验.', }; diff --git a/lib/routes/cas/genetics/index.ts b/lib/routes/cas/genetics/index.ts index 9b0330d9cfb6..9bd8db3cd208 100644 --- a/lib/routes/cas/genetics/index.ts +++ b/lib/routes/cas/genetics/index.ts @@ -8,9 +8,19 @@ const baseUrl = 'https://genetics.cas.cn'; export const route: Route = { path: '/genetics/:path{.+}', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/cas/genetics/jixs/yg', + parameters: { path: '路径,可在 URL 找到' }, + name: '遗传与发育生物学研究所', + maintainers: ['panyq357'], handler, + description: `| 路径 | 栏目 | +| :--------------------- | :--------- | +| jixs/yg | 学术预告 | +| dtxw/kyjz | 科研进展 | +| edu/zsxx/ssszs\\_187556 | 硕士生招生 | +| edu/zsxx/bsszs\\_187557 | 博士生招生 | +| dqyd/djgz/dwyw | 党委要闻 |`, }; async function handler(ctx) { diff --git a/lib/routes/cas/is/index.ts b/lib/routes/cas/is/index.ts index ec7390918db5..f977aaca89f1 100644 --- a/lib/routes/cas/is/index.ts +++ b/lib/routes/cas/is/index.ts @@ -9,9 +9,15 @@ const baseUrl = 'https://is.cas.cn'; export const route: Route = { path: '/is/:path{.+}', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/cas/is/xwdt2016/tzgg2016', + parameters: { path: '路径,可在 URL 找到' }, + name: '软件研究所', + maintainers: ['Misaka13514'], handler, + description: `| 通知公告 | 科技动态 | 科普动态 | +| ----------------- | ----------------- | ----------------- | +| xwdt2016/tzgg2016 | xwdt2016/kjdt2016 | kxcb2016/kpdt2016 |`, }; async function handler(ctx) { diff --git a/lib/routes/cbaigui/index.ts b/lib/routes/cbaigui/index.ts index b07d491428fe..b415738b61d7 100644 --- a/lib/routes/cbaigui/index.ts +++ b/lib/routes/cbaigui/index.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -9,9 +8,17 @@ import { renderFigure } from './templates/figure'; import { apiSlug, GetFilterId, rootUrl } from './utils'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['new-media'], + example: '/cbaigui', + parameters: { path: '路径,默认为首页' }, + name: '通用', + maintainers: ['nczitzk'], + description: `若订阅 [标签:妖](https://www.cbaigui.com/post-tag/妖),网址为 \`https://www.cbaigui.com/post-tag/妖\`。截取 \`https://www.cbaigui.com\` 到末尾的部分 \`/post-tag/妖\` 作为参数,此时路由为 [\`/cbaigui/post-tag/妖\`](https://rsshub.app/cbaigui/post-tag/妖)。 + +若订阅 [分类:埃及](https://www.cbaigui.com/post-category/世界/非洲/埃及),网址为 \`https://www.cbaigui.com/post-category/世界/非洲/埃及\`。截取 \`https://www.cbaigui.com\` 到末尾的部分 \`/post-category/世界/非洲/埃及\` 作为参数,此时路由为 [\`/cbaigui/post-category/世界/非洲/埃及\`](https://rsshub.app/cbaigui/post-category/世界/非洲/埃及)。 + +若订阅 [词条:白泽图](https://www.cbaigui.com/post-category/词条/白泽图),网址为 \`https://www.cbaigui.com/post-category/词条/白泽图\`。截取 \`https://www.cbaigui.com\` 到末尾的部分 \`/post-category/词条/白泽图\` 作为参数,此时路由为 [\`/cbaigui/post-category/词条/白泽图\`](https://rsshub.app/cbaigui/post-category/词条/白泽图)。`, handler, }; @@ -20,13 +27,14 @@ async function handler(ctx) { let filterName; - const currentUrl = new URL(getSubPath(ctx).replace(/^\/cbaigui/, ''), rootUrl).href; + const path = ctx.req.param('path') ?? ''; + const currentUrl = new URL(`/${path}`, rootUrl).href; let apiUrl = new URL(`${apiSlug}/posts?_embed=true&per_page=${limit}`, rootUrl).href; - const filterMatches = getSubPath(ctx).match(/^\/post-(tag|category)\/(.*)$/); + const filterMatches = path.match(/^post-(tag|category)\/(.*)$/); if (filterMatches) { - filterName = decodeURI(filterMatches[2].split('/').pop()); + filterName = filterMatches[2].split('/').pop(); const filterType = filterMatches[1] === 'tag' ? 'tags' : 'categories'; const filterId = await GetFilterId(filterType, filterName); diff --git a/lib/routes/cbnweek/index.ts b/lib/routes/cbnweek/index.ts index 9b2474be1c1d..c323849b8056 100644 --- a/lib/routes/cbnweek/index.ts +++ b/lib/routes/cbnweek/index.ts @@ -5,13 +5,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['finance'], + example: '/cbnweek', radar: [ { source: ['cbnweek.com/'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['nczitzk'], handler, url: 'cbnweek.com/', diff --git a/lib/routes/cebbank/all.tsx b/lib/routes/cebbank/all.tsx index cd6c59cc263d..76aaaf520dbc 100644 --- a/lib/routes/cebbank/all.tsx +++ b/lib/routes/cebbank/all.tsx @@ -11,7 +11,6 @@ export const route: Route = { path: '/quotation/all', categories: ['other'], example: '/cebbank/quotation/all', - parameters: {}, features: { requireConfig: false, requirePuppeteer: false, @@ -25,7 +24,7 @@ export const route: Route = { source: ['cebbank.com/site/ygzx/whpj/index.html', 'cebbank.com/eportal/ui', 'cebbank.com/'], }, ], - name: 'Unknown', + name: '外汇牌价 - 总览', maintainers: ['linbuxiao'], handler, url: 'cebbank.com/site/ygzx/whpj/index.html', diff --git a/lib/routes/cfmmc/index.ts b/lib/routes/cfmmc/index.ts index faa2a6a2420f..1e6cb87f37e2 100644 --- a/lib/routes/cfmmc/index.ts +++ b/lib/routes/cfmmc/index.ts @@ -8,9 +8,62 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/:id{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['finance'], + example: '/cfmmc/main/noticeannouncement/cfmmcnotice', + parameters: { id: '栏目 id,见下表,默认为中国期货监控公告' }, + radar: [ + { + source: ['cfmmc.com/:id'], + target: (params) => `/cfmmc${params.id ? `/${params.id.replace(/\/index\.shtml/, '')}` : ''}`, + }, + ], + name: '栏目', + maintainers: ['nczitzk'], handler, + description: `#### 党的建设 + +| 栏目 | id | +| -------- | -------------------------------------- | +| 党建动态 | main/partybuilding/partybuildingtrends | +| 基层风采 | main/partybuilding/basestyle | +| 学习园地 | main/partybuilding/learninggarden | + +#### 通知公告 + +| 栏目 | id | +| ---------------- | ----------------------------------- | +| 中国期货监控公告 | main/noticeannouncement/cfmmcnotice | +| 证监会公告 | main/noticeannouncement/csrcnotice | +| 上期所公告 | main/noticeannouncement/shfenotice | +| 郑商所公告 | main/noticeannouncement/czcenotice | +| 大商所公告 | main/noticeannouncement/dcenotice | +| 中金所公告 | main/noticeannouncement/cffexnotice | +| 广期所公告 | main/noticeannouncement/gfexnotice | + +#### 焦点新闻 + +| 栏目 | id | +| -------- | -------------------------------- | +| 财经要闻 | main/focusnews/financialnews | +| 专题聚焦 | main/focusnews/thematicfocus | +| 金融动态 | main/focusnews/financialdynamics | + +#### 保障基金 + +| 栏目 | id | +| -------- | ------------------------------------- | +| 基金概况 | main/securityfund/fundoverview | +| 政策法规 | main/securityfund/policiesregulations | +| 公告信息 | main/securityfund/noticeinformation | + +#### 政策法规 + +| 栏目 | id | +| -------------------- | --------------------------------------------- | +| 国家法律法规 | main/policiesregulations/lawsregulations | +| 部门规章及规范性文件 | main/policiesregulations/regulationsnormative | +| 行业法规政策 | main/policiesregulations/industrypolicies | +| 中国期货监控相关规则 | main/policiesregulations/cfmmcrules |`, }; async function handler(ctx) { diff --git a/lib/routes/chaincatcher/home.tsx b/lib/routes/chaincatcher/home.tsx index ad89be87ab7a..7c394ac3b784 100644 --- a/lib/routes/chaincatcher/home.tsx +++ b/lib/routes/chaincatcher/home.tsx @@ -11,13 +11,15 @@ const rootUrl = 'https://www.chaincatcher.com'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/chaincatcher', radar: [ { source: ['chaincatcher.com/'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['TonyRL'], handler, url: 'chaincatcher.com/', diff --git a/lib/routes/chinafactcheck/index.ts b/lib/routes/chinafactcheck/index.ts index 75d1721a7446..efe5326ea454 100644 --- a/lib/routes/chinafactcheck/index.ts +++ b/lib/routes/chinafactcheck/index.ts @@ -8,13 +8,15 @@ import utils from './utils'; export const route: Route = { path: '/', + categories: ['other'], + example: '/chinafactcheck', radar: [ { source: ['chinafactcheck.com/'], target: '', }, ], - name: 'Unknown', + name: '最新文章列表', maintainers: ['kdanfly'], handler, url: 'chinafactcheck.com/', diff --git a/lib/routes/chinanews/index.ts b/lib/routes/chinanews/index.ts index 883131a784cb..1f2a9d3a197c 100644 --- a/lib/routes/chinanews/index.ts +++ b/lib/routes/chinanews/index.ts @@ -10,13 +10,15 @@ const rootUrl = 'https://www.chinanews.com.cn'; export const route: Route = { path: '/', + categories: ['traditional-media'], + example: '/chinanews', radar: [ { source: ['chinanews.com.cn/'], target: '', }, ], - name: 'Unknown', + name: '最新', maintainers: ['yuxinliu-alex'], handler, url: 'chinanews.com.cn/', diff --git a/lib/routes/chinawriter/index.ts b/lib/routes/chinawriter/index.ts index 87c61b8ecfb0..ccd4f692f7cc 100644 --- a/lib/routes/chinawriter/index.ts +++ b/lib/routes/chinawriter/index.ts @@ -8,8 +8,137 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/:id{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/chinawriter', + parameters: { id: '栏目 id,见下表,默认为首页' }, + description: `| 服务 | 文学奖项 | +| ------ | -------- | +| 403937 | 403973 | + +| 新闻 | 访谈 | 艺术 | +| ------ | ------ | ------ | +| 403990 | 403997 | 404002 | + +| 理论评论 | 文史 | 科幻 | 书汇 | 新作品 | +| -------- | ------ | ------ | ------ | ------ | +| 404029 | 404057 | 404078 | 404058 | 404015 | + +| 世界文坛 | 民族文艺 | 网络文学 | 儿童文学 | +| -------- | -------- | -------- | -------- | +| 404085 | 404086 | 404022 | 404059 | + +<details> + <summary>更多栏目</summary> + +#### 会员 + +| 新发展会员名单 | 讣告 | +| -------------- | ------------- | +| 403978/403979 | 403978/403981 | + +#### 文学奖项 + +| 其他文学奖项 | +| ------------- | +| 403973/419349 | + +#### 新闻 + +| 时政新闻 | 中国作协 | 主席 | 党组书记 | 各地文讯 | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| 403990/403991 | 403990/403993 | 403990/441519 | 403990/441520 | 403990/403994 | + +#### 艺术 + +| 新闻 | 影视 | 舞台 | 人物 | 展览 | 书画 | +| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | +| 404002/404003 | 404002/419388 | 404002/419389 | 404002/404005 | 404002/419390 | 404002/419391 | + +#### 理论评论 + +| 重要理论文章 | 理论热点 | 文学评论 | 创作谈 | 争鸣 | 综述 | 《中国当代文学研究》 | +| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | -------------------- | +| 404029/419350 | 404029/419351 | 404029/404030 | 404029/404032 | 404029/404033 | 404029/404034 | 404087/404988/425775 | + +#### 文史 + +| 文坛轶事 | 文史漫谈 | 重温经典 | 版本研究 | 名人手迹 | 茅盾文学奖获奖作家研究 | +| ------------- | ------------- | ------------- | ------------- | ------------- | ---------------------- | +| 404057/404063 | 404057/442005 | 404057/419384 | 404057/419387 | 404057/419382 | 404087/404988/429369 | + +#### 科幻 + +| 动态 | 评论 | 作家印象 | 作品 | 科声幻影 | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| 404078/404079 | 404078/404080 | 404078/404081 | 404078/404083 | 404078/404084 | + +#### 书汇 + +| 书摘 | 图书排行 | +| ------------- | ------------- | +| 404058/404067 | 404058/404069 | + +#### 新作品 + +| 小说 | 诗歌 | 散文 | 纪实 | 其他 | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| 404015/404017 | 404015/404020 | 404015/404018 | 404015/404019 | 404015/419926 | + +| 平台推荐 | 本周之星 | 2018 年 5 月 18 日前原创作品 | +| ------------- | ------------- | ---------------------------- | +| 404015/419789 | 404015/431511 | 404009 | + +| 《人民文学》 | 《诗刊》 | 《民族文学》 | 《收获》 | 《十月》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/418925 | 404015/416204/418926 | 404015/416204/418928 | 404015/416204/418958 | 404015/416204/418956 | + +| 《小说选刊》 | 《北京文学》 | 《上海文学》 | 《天津文学》 | 《草原》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/418929 | 404015/416204/418954 | 404015/416204/418962 | 404015/416204/419004 | 404015/416204/418989 | + +| 《黄河》 | 《江南》 | 《钟山》 | 《广州文艺》 | 《湖南文学》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/426204 | 404015/416204/418957 | 404015/416204/418984 | 404015/416204/419881 | 404015/416204/419156 | + +| 《山西文学》 | 《花城》 | 《青年作家》 | 《雨花》 | 《红豆》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/419827 | 404015/416204/418960 | 404015/416204/418967 | 404015/416204/419885 | 404015/416204/418993 | + +| 《长江文艺》 | 《中国作家》 | 《青年文学》 | 《美文》 | 《芙蓉》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/418961 | 404015/416204/418927 | 404015/416204/418979 | 404015/416204/418985 | 404015/416204/418986 | + +| 《长城》 | 《福建文学》 | 《啄木鸟》 | 《芳草》 | 《小说月报》 | +| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | +| 404015/416204/418987 | 404015/416204/419003 | 404015/416204/435225 | 404015/416204/424311 | 404015/416204/418963 | + +#### 世界文坛 + +| 视点 | 译介 | 作家印象 | 文学评论 | 影像艺术 | 作品推介 | +| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | +| 404085/404090 | 404085/431803 | 404085/404091 | 404085/404092 | 404085/404093 | 404085/404095 | + +#### 民族文艺 + +| 动态 | 品评 | 作家印象 | 作品 | 影像 | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| 404086/404098 | 404086/404101 | 404086/404099 | 404086/404100 | 404086/404102 | + +#### 网络文学 + +| 动态 | 观察 | 访谈 | 中国网络小说排行榜 | +| ------------- | ------------- | ------------- | ------------------ | +| 404022/404023 | 404022/404027 | 404022/404024 | 404022/404028 | + +#### 儿童文学 + +| 视点 | 文学评论 | 作家印象 | 作品推介 | 动漫艺术 | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| 404059/404071 | 404059/404072 | 404059/404073 | 404059/404075 | 404059/404076 | + +</details>`, + name: '栏目', + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/cmde/index.ts b/lib/routes/cmde/index.ts index aa71731617a6..95eee1effe3e 100644 --- a/lib/routes/cmde/index.ts +++ b/lib/routes/cmde/index.ts @@ -10,9 +10,15 @@ const rootURL = 'https://www.cmde.org.cn'; export const route: Route = { path: '/:cate{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['government'], + example: '/cmde/xwdt/zxyw', + parameters: { cate: '路径,默认为最新要闻' }, + name: '通用', + maintainers: ['run-ze'], handler, + description: `路径处填写对应页面 URL 中 \`https://www.cmde.org.cn/\` 与 \`/index.html\` 之间的字段,下面是一个例子。 + +若订阅 [最新要闻](https://www.cmde.org.cn/xwdt/zxyw/index.html) 则将对应页面 URL <https://www.cmde.org.cn/xwdt/zxyw/index.html> 中 \`https://www.cmde.org.cn/\` 和 \`/index.html\` 之间的字段 \`xwdt/zxyw\` 作为路径填入。此时路由为 [\`/cmde/xwdt/zxyw\`](https://rsshub.app/cmde/xwdt/zxyw)`, }; async function handler(ctx) { diff --git a/lib/routes/cncf/reports.ts b/lib/routes/cncf/reports.ts index 52400646c384..45c5e040e6b7 100644 --- a/lib/routes/cncf/reports.ts +++ b/lib/routes/cncf/reports.ts @@ -9,13 +9,15 @@ const rootURL = 'https://www.cncf.io'; export const route: Route = { path: '/reports', + categories: ['programming'], + example: '/cncf/reports', radar: [ { source: ['cncf.io/reports'], }, ], - name: 'Unknown', - maintainers: [], + name: 'Reports', + maintainers: ['Fatpandac'], handler, url: 'cncf.io/reports', }; diff --git a/lib/routes/cneb/yjxx.ts b/lib/routes/cneb/yjxx.ts index 5a56b9e57516..a7fb54891ccb 100644 --- a/lib/routes/cneb/yjxx.ts +++ b/lib/routes/cneb/yjxx.ts @@ -5,17 +5,49 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/yjxx/*', + path: '/yjxx/:level?/:province?/:city?', + categories: ['forecast'], + example: '/cneb/yjxx', + parameters: { level: '灾害级别,见下表,默认为全部', province: '省份,默认为空,即全国', city: '城市,默认为空,即全省' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['cneb.gov.cn/yjxx', 'cneb.gov.cn/'], target: '/yjxx', }, ], - name: 'Unknown', - maintainers: [], + name: '预警信息', + maintainers: ['muzea', 'nczitzk'], handler, url: 'cneb.gov.cn/yjxx', + description: `灾害级别 + +| 全部 | 红色 | 橙色 | 黄色 | 蓝色 | +| ---- | ---- | ---- | ---- | ---- | +| | 红色 | 橙色 | 黄色 | 蓝色 | + +::: tip +若订阅全国的全部预警信息,此时路由为 [\`/cneb/yjxx\`](https://rsshub.app/cneb/yjxx)。 + +若订阅全国的 **红色** 预警信息,此时路由为 [\`/cneb/yjxx/红色\`](https://rsshub.app/cneb/yjxx/红色)。 + +若订阅 **北京市** 的全部预警信息,此时路由为 [\`/cneb/yjxx/北京市\`](https://rsshub.app/cneb/yjxx/北京市)。 + +若订阅 **北京市** 的 **蓝色** 预警信息,此时路由为 [\`/cneb/yjxx/北京市/蓝色\`](https://rsshub.app/cneb/yjxx/北京市/蓝色)。 + +若订阅 **广东省** 的 **橙色** 预警信息,此时路由为 [\`/cneb/yjxx/广东省/橙色\`](https://rsshub.app/cneb/yjxx/广东省/橙色)。 + +若订阅 **广东省广州市** 的全部预警信息,此时路由为 [\`/cneb/yjxx/广东省/广州市\`](https://rsshub.app/cneb/yjxx/广东省/广州市)。 + +若订阅 **广东省广州市** 的 **黄色** 预警信息,此时路由为 [\`/cneb/yjxx/广东省/广州市/黄色\`](https://rsshub.app/cneb/yjxx/广东省/广州市/黄色)。 +:::`, }; async function handler(ctx) { diff --git a/lib/routes/cnjxol/index.tsx b/lib/routes/cnjxol/index.tsx index c8dcb7fd5d8c..0430892753a4 100644 --- a/lib/routes/cnjxol/index.tsx +++ b/lib/routes/cnjxol/index.tsx @@ -2,9 +2,9 @@ import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; -import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -14,18 +14,46 @@ const categories = { }; export const route: Route = { - path: '/:category?/:id?', - name: 'Unknown', - maintainers: [], + path: '/jxrb/:id?', + categories: ['traditional-media'], + example: '/cnjxol/jxrb', + parameters: { id: '编号,见下表,默认为全部' }, + radar: [ + { + source: ['cnjxol.com/'], + target: '/jxrb/:id', + }, + ], + description: `| 版 | 编号 | +| -------------------- | ---- | +| 全部 | | +| 第 01 版:要闻 | 01 | +| 第 02 版:要闻 | 02 | +| 第 03 版:要闻 | 03 | +| 第 04 版:嘉一度 | 04 | +| 第 05 版:聚焦 | 05 | +| 第 06 版:党报热线 | 06 | +| 第 07 版:天下 | 07 | +| 第 08 版:聚焦 | 08 | +| 第 09 版:南湖新闻 | 09 | +| 第 10 版:综合 | 10 | +| 第 11 版:梅花洲 | 11 | +| 第 12 版:南湖纵横 | 12 | +| 第 13 版:秀洲新闻 | 13 | +| 第 14 版:综合 | 14 | +| 第 15 版:秀・观察 | 15 | +| 第 16 版:走进高新区 | 16 |`, + features: { + antiCrawler: true, + }, + name: '嘉兴日报', + maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { - const category = ctx.req.param('category') ?? 'jxrb'; +export async function handler(ctx) { + const category = getSubPath(ctx).split('/', 2)[1]; const id = ctx.req.param('id'); - if (!Object.keys(categories).includes(category)) { - throw new InvalidParameterError('Invalid category'); - } const rootUrl = `https://${category}.cnjxol.com`; const currentUrl = `${rootUrl}/${category}Paper/pc/layout`; diff --git a/lib/routes/cnjxol/nhwb.ts b/lib/routes/cnjxol/nhwb.ts new file mode 100644 index 000000000000..e88482d0b91a --- /dev/null +++ b/lib/routes/cnjxol/nhwb.ts @@ -0,0 +1,37 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/nhwb/:id?', + categories: ['traditional-media'], + example: '/cnjxol/nhwb', + parameters: { id: '编号,见下表,默认为全部' }, + radar: [ + { + source: ['cnjxol.com/'], + target: '/nhwb/:id', + }, + ], + description: `| 版 | 编号 | +| ------------------------------------ | ---- | +| 全部 | | +| 第 01 版:要闻 | 01 | +| 第 02 版:品质嘉兴・红船旁的美丽城镇 | 02 | +| 第 03 版:嘉兴新闻 | 03 | +| 第 04 版:嘉兴新闻 | 04 | +| 第 05 版:今日聚焦 | 05 | +| 第 06 版:嘉兴新闻 | 06 | +| 第 07 版:热线新闻 | 07 | +| 第 08 版:财经新闻 | 08 | +| 第 09 版:热线新闻 | 09 | +| 第 10 版:公益广告 | 10 | +| 第 11 版:消费周刊 | 11 | +| 第 12 版:悦读坊 | 12 |`, + features: { + antiCrawler: true, + }, + name: '南湖晚报', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/codeforces/recent-actions.ts b/lib/routes/codeforces/recent-actions.ts index 867350429152..67f9173fe37e 100644 --- a/lib/routes/codeforces/recent-actions.ts +++ b/lib/routes/codeforces/recent-actions.ts @@ -23,7 +23,7 @@ export const route: Route = { }, ], name: 'Recent actions', - maintainers: [], + maintainers: ['ftiasch'], handler, url: 'codeforces.com/recent-actions', }; diff --git a/lib/routes/cpuid/news.ts b/lib/routes/cpuid/news.ts index 08c2cee9b439..46b436403efb 100644 --- a/lib/routes/cpuid/news.ts +++ b/lib/routes/cpuid/news.ts @@ -25,7 +25,7 @@ export const route: Route = { }, ], name: 'News', - maintainers: [], + maintainers: ['TonyRL'], handler, url: 'cpuid.com/news.html', }; diff --git a/lib/routes/cs/index.ts b/lib/routes/cs/index.ts index 85b4e998dd39..5690d6954da7 100644 --- a/lib/routes/cs/index.ts +++ b/lib/routes/cs/index.ts @@ -16,8 +16,170 @@ const decodeBufferByCharset = (buffer) => { export const route: Route = { path: '/:category{.+}?', + categories: ['finance'], + example: '/cs', name: '栏目', - parameters: { category: '分类,见下表,默认为首页' }, + parameters: { category: '分类,见下表,默认为要闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + title: '要闻', + source: ['cs.com.cn/xwzx/'], + target: '/xwzx', + }, + { + title: '公司', + source: ['cs.com.cn/ssgs/'], + target: '/ssgs', + }, + { + title: '市场', + source: ['cs.com.cn/gppd/'], + target: '/gppd', + }, + { + title: '基金', + source: ['cs.com.cn/tzjj/'], + target: '/tzjj', + }, + { + title: '科创', + source: ['cs.com.cn/5g/'], + target: '/5g', + }, + { + title: '产经', + source: ['cs.com.cn/cj2020/'], + target: '/cj2020', + }, + { + title: '期货', + source: ['cs.com.cn/zzqh2020/'], + target: '/zzqh2020', + }, + { + title: '海外', + source: ['cs.com.cn/hw2020/'], + target: '/hw2020', + }, + { + title: '财经要闻', + source: ['cs.com.cn/xwzx/hg/'], + target: '/xwzx/hg', + }, + { + title: '观点评论', + source: ['cs.com.cn/xwzx/jr/'], + target: '/xwzx/jr', + }, + { + title: '民生消费', + source: ['cs.com.cn/xwzx/msxf/'], + target: '/xwzx/msxf', + }, + { + title: '公司要闻', + source: ['cs.com.cn/ssgs/gsxw/'], + target: '/ssgs/gsxw', + }, + { + title: '公司深度', + source: ['cs.com.cn/ssgs/gssd/'], + target: '/ssgs/gssd', + }, + { + title: '公司巡礼', + source: ['cs.com.cn/ssgs/gsxl/'], + target: '/ssgs/gsxl', + }, + { + title: 'A股市场', + source: ['cs.com.cn/gppd/gsyj/'], + target: '/gppd/gsyj', + }, + { + title: '港股资讯', + source: ['cs.com.cn/gppd/ggzx/'], + target: '/gppd/ggzx', + }, + { + title: '债市研究', + source: ['cs.com.cn/gppd/zqxw/'], + target: '/gppd/zqxw', + }, + { + title: '海外报道', + source: ['cs.com.cn/gppd/hwbd/'], + target: '/gppd/hwbd', + }, + { + title: '期货报道', + source: ['cs.com.cn/gppd/qhbd/'], + target: '/gppd/qhbd', + }, + { + title: '基金动态', + source: ['cs.com.cn/tzjj/jjdt/'], + target: '/tzjj/jjdt', + }, + { + title: '基金视点', + source: ['cs.com.cn/tzjj/jjks/'], + target: '/tzjj/jjks', + }, + { + title: '基金持仓', + source: ['cs.com.cn/tzjj/jjcs/'], + target: '/tzjj/jjcs', + }, + { + title: '私募基金', + source: ['cs.com.cn/tzjj/smjj/'], + target: '/tzjj/smjj', + }, + { + title: '基民学苑', + source: ['cs.com.cn/tzjj/tjdh/'], + target: '/tzjj/tjdh', + }, + { + title: '券商', + source: ['cs.com.cn/qs/'], + target: '/qs', + }, + { + title: '银行', + source: ['cs.com.cn/yh/'], + target: '/yh', + }, + { + title: '保险', + source: ['cs.com.cn/bx/'], + target: '/bx', + }, + { + title: '中证快讯 7x24', + source: ['cs.com.cn/sylm/jsbd/'], + target: '/sylm/jsbd', + }, + { + title: 'IPO鉴真', + source: ['cs.com.cn/yc/ipojz/'], + target: '/yc/ipojz', + }, + { + title: '公司能见度', + source: ['cs.com.cn/yc/gsnjd/'], + target: '/yc/gsnjd', + }, + ], maintainers: ['nczitzk'], description: `| 要闻 | 公司 | 市场 | 基金 | | ---- | ---- | ---- | ---- | @@ -68,6 +230,7 @@ export const route: Route = { </details>`, handler, + url: 'www.cs.com.cn', }; async function handler(ctx) { diff --git a/lib/routes/cs/zzkx.ts b/lib/routes/cs/zzkx.ts deleted file mode 100644 index 9faf0397e66e..000000000000 --- a/lib/routes/cs/zzkx.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Route } from '@/types'; - -export const route: Route = { - path: ['/news/zzkx', '/zzkx'], - name: 'Unknown', - maintainers: [], - handler, -}; - -function handler(ctx) { - // https://www.cs.com.cn/sylm/jsbd/ - - const redirectTo = '/cs/sylm/jsbd'; - ctx.set('redirect', redirectTo); -} diff --git a/lib/routes/curiouscat/user.ts b/lib/routes/curiouscat/user.ts index 36972528f572..531a0b5a6ce2 100644 --- a/lib/routes/curiouscat/user.ts +++ b/lib/routes/curiouscat/user.ts @@ -9,12 +9,15 @@ const fetchAPIByUser = async (user) => { export const route: Route = { path: '/user/:id', + categories: ['social-media'], + example: '/curiouscat/user/username', + parameters: { id: 'username that is in the URL' }, radar: [ { source: ['curiouscat.live/:id'], }, ], - name: 'Unknown', + name: 'User', maintainers: ['lucasew'], handler, }; diff --git a/lib/routes/cyzone/author.ts b/lib/routes/cyzone/author.ts index 760055725758..15fd2449d424 100644 --- a/lib/routes/cyzone/author.ts +++ b/lib/routes/cyzone/author.ts @@ -21,7 +21,7 @@ export const route: Route = { }, ], name: '作者', - maintainers: ['nczitzk'], + maintainers: ['xyqfer', 'nczitzk'], handler, }; diff --git a/lib/routes/cyzone/index.ts b/lib/routes/cyzone/index.ts index c359bdb18afd..c21349813755 100644 --- a/lib/routes/cyzone/index.ts +++ b/lib/routes/cyzone/index.ts @@ -3,14 +3,25 @@ import type { Route } from '@/types'; import { apiRootUrl, getInfo, processItems, rootUrl } from './util'; export const route: Route = { - path: ['/channel/:id?', '/:id?'], + path: '/:id?', + categories: ['new-media'], + example: '/cyzone', + parameters: { id: '频道 id,可在对应频道页 URL 中找到,默认为 news,即最新资讯' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['cyzone.cn/channel/:id', 'cyzone.cn/'], target: '/:id', }, ], - name: 'Unknown', + name: '资讯', maintainers: ['nczitzk'], handler, description: `| 最新 | 快鲤鱼 | 创投 | 科创板 | 汽车 | diff --git a/lib/routes/cyzone/label.ts b/lib/routes/cyzone/label.ts index 55fae5981856..1e1a4c0e7ed2 100644 --- a/lib/routes/cyzone/label.ts +++ b/lib/routes/cyzone/label.ts @@ -21,7 +21,7 @@ export const route: Route = { }, ], name: '标签', - maintainers: ['nczitzk'], + maintainers: ['LogicJake', 'nczitzk'], handler, }; diff --git a/lib/routes/dayanzai/index.ts b/lib/routes/dayanzai/index.ts index dfd56492f744..6b2f00205f1c 100644 --- a/lib/routes/dayanzai/index.ts +++ b/lib/routes/dayanzai/index.ts @@ -28,7 +28,7 @@ export const route: Route = { }, ], name: '分类', - maintainers: [], + maintainers: ['gl0zzy'], handler, description: `| 微软应用 | 安卓应用 | 教程资源 | 其他资源 | | -------- | -------- | -------- | -------- | diff --git a/lib/routes/deadline/posts.tsx b/lib/routes/deadline/posts.tsx index 0d285abb2fc2..d2b0f010b186 100644 --- a/lib/routes/deadline/posts.tsx +++ b/lib/routes/deadline/posts.tsx @@ -26,13 +26,15 @@ const renderDescription = (embedded, desc) => export const route: Route = { path: '/', + categories: ['new-media'], + example: '/deadline', radar: [ { source: ['deadline.com/'], target: '', }, ], - name: 'Unknown', + name: 'Latest Article', maintainers: ['TonyRL'], handler, url: 'deadline.com/', diff --git a/lib/routes/dgjyw/index.ts b/lib/routes/dgjyw/index.ts index 57b3c5c0b269..a3f6a3db3cf4 100644 --- a/lib/routes/dgjyw/index.ts +++ b/lib/routes/dgjyw/index.ts @@ -2,23 +2,42 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:category{.+}?', + categories: ['study'], + example: '/dgjyw/tz', + parameters: { category: '分类,见下表,默认为通知' }, + radar: [ + { + source: ['www.dgjyw.com/:category.htm'], + target: '/:category', + }, + ], + name: '分类', + maintainers: ['nczitzk'], + description: `| 通知 | 动态 | 公示 | +| ---- | ---- | ---- | +| tz | dt | gs | + +::: tip +分类字段处填写的是对应东莞教研网网址中介于 \`https://www.dgjyw.com/\` 和 \`.htm\` 中间的一段。 + +如 [通知](https://www.dgjyw.com/tz.htm) 的网址为 \`https://www.dgjyw.com/tz.htm\`,其中间字段为 \`tz\`,所以可得路由为 [\`/dgjyw/tz\`](https://rsshub.app/dgjyw/tz); + +同理,[教育科研 - 科研文件](https://www.dgjyw.com/jyky/kywj.htm) 的网址为 \`https://www.dgjyw.com/jyky/kywj.htm\`,其中间字段为 \`jyky/kywj\`,所以可得路由为 [\`/dgjyw/jyky/kywj\`](https://rsshub.app/dgjyw/jyky/kywj)。 +:::`, handler, }; async function handler(ctx) { - const params = getSubPath(ctx); + const category = ctx.req.param('category') ?? 'tz'; const rootUrl = 'https://www.dgjyw.com'; - const currentUrl = `${rootUrl}${params === '/' ? '/tz' : params}.htm`; + const currentUrl = `${rootUrl}/${category}.htm`; const response = await got(currentUrl); diff --git a/lib/routes/digitalcameraworld/news.ts b/lib/routes/digitalcameraworld/news.ts index 7955857e43b2..ee36113ab84e 100644 --- a/lib/routes/digitalcameraworld/news.ts +++ b/lib/routes/digitalcameraworld/news.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: 'News', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/discourse/notifications.ts b/lib/routes/discourse/notifications.ts index 189c16c1b98c..a98824841063 100644 --- a/lib/routes/discourse/notifications.ts +++ b/lib/routes/discourse/notifications.ts @@ -24,7 +24,7 @@ export const route: Route = { supportScihub: false, }, name: 'Notifications', - maintainers: [], + maintainers: ['dzx-dzx'], handler, description: `::: warning If you opt to enable \`fulltext\` feature, consider adding \`limit\` parameter to your query to avoid sending too many request. diff --git a/lib/routes/discuz/discuz.ts b/lib/routes/discuz/discuz.ts index cd1e59c37eef..c73e862279aa 100644 --- a/lib/routes/discuz/discuz.ts +++ b/lib/routes/discuz/discuz.ts @@ -79,9 +79,19 @@ async function loadContent(itemLink, charset, header) { export const route: Route = { path: ['/:ver{[7x]}/:cid{[0-9]{2}}/:link{.+}', '/:ver{[7x]}/:link{.+}', '/:link{.+}'], - name: 'Unknown', - maintainers: ['pseudoyu'], + categories: ['bbs'], + example: '/discuz/x/https%3a%2f%2fwww.52pojie.cn%2fforum-16-1.html', + parameters: { + ver: 'discuz version,see below table', + cid: 'Cookie id,require self hosted and set environment parameters, see Deploy - Configuration pages for detail', + link: 'link of subforum, require url encoded', + }, + name: '通用子版块', + maintainers: ['junfengP', 'pseudoyu'], handler, + description: `| Discuz X Series | Discuz 7.x Series | +| --------------- | ----------------- | +| x | 7 |`, }; async function handler(ctx) { diff --git a/lib/routes/distill/index.ts b/lib/routes/distill/index.ts index 022f54bbf7a9..ad4d16ffb4b4 100644 --- a/lib/routes/distill/index.ts +++ b/lib/routes/distill/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['programming'], + example: '/distill', radar: [ { source: ['distill.pub/'], target: '', }, ], - name: 'Unknown', + name: 'Latest', maintainers: ['nczitzk'], handler, url: 'distill.pub/', diff --git a/lib/routes/dlsite/z-index/index.ts b/lib/routes/dlsite/z-index/index.ts index 7f0fb90b8a3f..690b6d803c5d 100644 --- a/lib/routes/dlsite/z-index/index.ts +++ b/lib/routes/dlsite/z-index/index.ts @@ -3,9 +3,21 @@ import type { Route } from '@/types'; import { ProcessItems } from '../utils'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['anime'], + example: '/dlsite/home/new', + parameters: { + path: 'Path, `/home/new` by default, as Release Calendar', + }, + description: `::: tip +To subscribe to this route, you can first visit the site and specify filters, and then fill in the field after \`https://www.dlsite.com/\` in the URL of the corresponding page at the path of the route. Here are 2 examples. + +If you subscribe to [Voice / ASMR works Release date - New to Old](https://www.dlsite.com/home/works/type/=/work_type_category/audio/order/release_d), at the URL of the corresponding page \`https://www.dlsite.com/home/works/type/=/work_type_category/audio/order/release_d\` and after \`https://www.dlsite.com/\` is \`home/works/type/=/work_type_category/audio/order/release_d\`, which can be seen as the path. In this case the route is [\`/dlsite/home/works/type/=/work_type_category/audio/order/release_d\`](https://rsshub.app/dlsite/home/works/type/=/work_type_category/audio/order/release_d) + +If you subscribe to [Discounted works Latest Discounts - Newest to Oldest](https://www.dlsite.com/home/works/discount/=/order/cstart_d), at the URL of the corresponding page \`https://www.dlsite.com/home/works/discount/=/order/cstart_d\` and after \`https://www.dlsite.com/\` is \`home/works/discount/=/order/cstart_d\`, which can be seen as the path. In this case the route is [\`/dlsite/home/works/discount/=/order/cstart_d\`](https://rsshub.app/dlsite/home/works/discount/=/order/cstart_d) +:::`, + name: 'General', + maintainers: ['nczitzk'], handler, features: { nsfw: true, diff --git a/lib/routes/douban/other/explore-column.ts b/lib/routes/douban/other/explore-column.ts index 5f0955aa249d..15ecfa686160 100644 --- a/lib/routes/douban/other/explore-column.ts +++ b/lib/routes/douban/other/explore-column.ts @@ -6,8 +6,11 @@ import got from '@/utils/got'; const host = 'https://www.douban.com/explore/column/'; export const route: Route = { path: '/explore/column/:id', - name: 'Unknown', - maintainers: [], + categories: ['social-media'], + example: '/douban/explore/column/2', + parameters: { id: '分栏目id' }, + name: '浏览发现分栏目', + maintainers: ['LogicJake'], handler, }; diff --git a/lib/routes/dut/index.ts b/lib/routes/dut/index.ts index 870fc93e1891..c8eb01810a6b 100644 --- a/lib/routes/dut/index.ts +++ b/lib/routes/dut/index.ts @@ -12,9 +12,30 @@ import shortcuts from './shortcuts'; export const route: Route = { path: ['/*/*', '/:0?'], - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/dut', + name: '通用', + maintainers: ['beautyyuyanli', 'nczitzk', 'ueiu'], handler, + description: `订阅 **单级** 栏目如 [大连理工大学新闻网](https://news.dlut.edu.cn) 的 [头条关注](https://news.dlut.edu.cn/ttgz.htm) 分类栏目,分为 3 步: + +1. 将 URL \`https://news.dlut.edu.cn/ttgz.htm\` 中 \`https://\` 与 \`.dlut.edu.cn/\` 中间的 \`news\` 作为 \`site\` 参数填入; +2. 将 \`https://news.dlut.edu.cn/\` 与 \`.htm\` 间的 \`ttgz\` 作为 \`category\` 参数填入; +3. 最终可获得 [\`/dut/news/ttgz\`](https://rsshub.app/dut/news/tzgg)。 + +订阅 **多级** 栏目如 [大连理工大学新闻网](https://news.dlut.edu.cn) 的 [人才培养](https://news.dlut.edu.cn/xwjj01/rcpy.htm) 分类栏目,同样分为 3 步: + +1. 将 URL \`https://news.dlut.edu.cn/xwjj01/rcpy.htm\` 中 \`https://\` 与 \`.dlut.edu.cn/\` 中间的 \`news\` 作为 \`site\` 参数填入; +2. 把 \`https://news.dlut.edu.cn/\` 与 \`.htm\` 间 \`xwjj01/rcpy\` 作为 \`category\` 参数填入; +3. 最终可获得 [\`/dut/news/xwjj01/rcpy\`](https://rsshub.app/dut/news/xwjj01/rcpy)。 + +::: tip 小提示 +大连理工大学大部分站点支持上述通用规则进行订阅。下方的大连理工大学相关路由基本适用于该规则,在其对应的表格中没有提及的分类栏目,可以使用上方的方法自行扩展。 +::: + +::: tip 小小提示 +你会发现 [大连理工大学新闻网](https://news.dlut.edu.cn) 的 [人才培养](https://news.dlut.edu.cn/xwjj01/rcpy.htm) 分类栏目在下方 **新闻网** 参数表格中 \`category\` 参数为 \`rcpy\`,并非上面例子中给出的 \`xwjj01/rcpy\`。这意味着开发者对路由 \`/dut/news/xwjj01/rcpy\` 指定了快捷方式 \`/dut/news/rcpy\`。两者的效果是一致的。 +:::`, }; async function handler(ctx) { diff --git a/lib/routes/e-hentai/index.tsx b/lib/routes/e-hentai/index.tsx index fb5c2073ab74..6832229cc1aa 100644 --- a/lib/routes/e-hentai/index.tsx +++ b/lib/routes/e-hentai/index.tsx @@ -3,22 +3,55 @@ import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/:what?/:id?/:needTorrents?/:needImages?', - name: 'Unknown', - maintainers: [], + path: '/category/:category?/:needTorrents?/:needImages?', + categories: ['multimedia'], + example: '/e-hentai/category/manga', + parameters: { + category: '分类,可在对应分类页中找到,默认为首页', + needTorrents: '需要输出种子文件,填写 true/yes 表示需要,默认需要', + needImages: '需要显示大图,填写 true/yes 表示需要,默认需要', + }, + name: '分类', + maintainers: ['nczitzk'], + description: `::: tip +参数 **需要输出种子文件** 设置为 \`true\` \`yes\` \`t\` \`y\` 等值后,RSS 会携带种子文件的路径,以供支持 RSS 的下载工具订阅下载。 + +同理,参数 **需要显示大图** 启用后,RSS 会携带每项内容中的大图,而不只提供缩略图。 + +当然,选择 **需要输出种子文件**、**需要显示大图** 后获取内容时间需要更久,同时若指定获取数量过多,可能会出现获取超时错误。此时,可以在路由末尾处加上 \`?limit=限制获取数目\` 来限制获取条目数量,或直接修改全局的超时参数 \`REQUEST_TIMEOUT\`(详见文档中的 [其他应用配置](https://docs.rsshub.app/install/#pei-zhi-qi-ta-ying-yong-pei-zhi))。 + +以下是一个例子: + +选择浏览 [Manga 分类](https://e-hentai.org/manga),并指定 **不携带种子文件**,**只显示大图**,并只 **输出 5 个**。由于 [Manga 分类](https://e-hentai.org/manga) 的 URL \`https://e-hentai.org/manga\` 中对应分类字段为 \`manga\`,所以对应路由为 [\`/e-hentai/category/manga/no/yes?limit=5\`](https://rsshub.app/e-hentai/category/manga/no/yes?limit=5) +::: + +| Doujinshi | Manga | Artist CG | Game CG | Western | +| --------- | ----- | --------- | ------- | ------- | +| doujinshi | manga | artistcg | gamecg | western | + +| Non-H | Image Set | Cosplay | Asian Porn | Misc | Popular | +| ----- | --------- | ------- | ---------- | ---- | ------- | +| non-h | imageset | cosplay | asianporn | misc | popular |`, features: { nsfw: true, }, + radar: [ + { + source: ['e-hentai.org/:category', 'e-hentai.org/'], + target: '/category/:category', + }, + ], handler, }; -async function handler(ctx) { - const id = ctx.req.param('id') ?? ''; - const what = ctx.req.param('what') ?? ''; +export async function handler(ctx) { + const id = ctx.req.param('category') ?? ctx.req.param('tag') ?? ctx.req.param('keyword') ?? ''; + const what = getSubPath(ctx).split('/', 2)[1]; const needTorrents = /t|y/i.test(ctx.req.param('needTorrents') ?? 'true'); const needImages = /t|y/i.test(ctx.req.param('needImages') ?? 'true'); @@ -128,7 +161,7 @@ async function handler(ctx) { ); return { - title: `${id || what || 'Front Page'} - E-Hentai Galleries`, + title: `${id || what} - E-Hentai Galleries`, link: currentUrl, item: items, }; diff --git a/lib/routes/e-hentai/search.ts b/lib/routes/e-hentai/search.ts new file mode 100644 index 000000000000..53bfab3be4b9 --- /dev/null +++ b/lib/routes/e-hentai/search.ts @@ -0,0 +1,31 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/search/:keyword?/:needTorrents?/:needImages?', + categories: ['multimedia'], + example: '/e-hentai/search/f_search=haha', + parameters: { + keyword: '关键字,可以在搜索结果页的 URL 中找到,默认为首页', + needTorrents: '需要输出种子文件,填写 true/yes 表示需要,默认需要', + needImages: '需要显示大图,填写 true/yes 表示需要,默认需要', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['e-hentai.org/:keyword', 'e-hentai.org/'], + target: '/search/:keyword', + }, + ], + name: '搜索', + maintainers: ['nczitzk'], + description: `::: tip +参数 **需要输出种子文件**、**需要显示大图** 的说明同上,以下是一个例子: + +选择浏览 [f\\_search=cosplay 搜索结果](https://e-hentai.org/?f_search=cosplay),并指定 **携带种子文件**,且 **显示大图**。由于 [f\\_search=cosplay 搜索结果](https://e-hentai.org/?f_search=cosplay) 的 URL \`https://e-hentai.org/?f_search=cosplay\` 中对应关键字字段为 \`?\` 后的 \`f_search=cosplay\`,所以对应路由为 [\`/e-hentai/search/f_search=cosplay/y/y\`](https://rsshub.app/e-hentai/search/f_search=cosplay/y/y) +:::`, + handler, +}; diff --git a/lib/routes/e-hentai/tag.ts b/lib/routes/e-hentai/tag.ts new file mode 100644 index 000000000000..112ebccf05dd --- /dev/null +++ b/lib/routes/e-hentai/tag.ts @@ -0,0 +1,31 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:tag?/:needTorrents?/:needImages?', + categories: ['multimedia'], + example: '/e-hentai/tag/language:chinese', + parameters: { + tag: '标签,可在对应标签页中找到,默认为首页', + needTorrents: '需要输出种子文件,填写 true/yes 表示需要,默认需要', + needImages: '需要显示大图,填写 true/yes 表示需要,默认需要', + }, + features: { + nsfw: true, + }, + radar: [ + { + source: ['e-hentai.org/tag/:tag', 'e-hentai.org/'], + target: '/tag/:tag', + }, + ], + name: '标签', + maintainers: ['nczitzk'], + description: `::: tip +参数 **需要输出种子文件**、**需要显示大图** 的说明同上,以下是一个例子: + +选择浏览 [language:chinese 标签](https://e-hentai.org/tag/language:chinese),并指定 **携带种子文件**,**不显示大图**。由于 [language:chinese 标签](https://e-hentai.org/tag/language:chinese) 的 URL \`https://e-hentai.org/tag/language:chinese\` 中对应标签字段为 \`language:chinese\`,所以对应路由为 [\`/e-hentai/tag/language:chinese/true/false\`](https://rsshub.app/e-hentai/tag/language:chinese/true/false) +:::`, + handler, +}; diff --git a/lib/routes/elsevier/issue.ts b/lib/routes/elsevier/issue.ts index 08b89c6fbbf8..46804f2d7a11 100644 --- a/lib/routes/elsevier/issue.ts +++ b/lib/routes/elsevier/issue.ts @@ -10,15 +10,21 @@ import { renderDescription } from './templates/description'; const cookieJar = new CookieJar(); export const route: Route = { - path: ['/:journal/vol/:issue', '/:journal/:issue'], + path: '/:journal/:issue', + categories: ['journal'], + example: '/elsevier/signal-processing/192', + parameters: { + journal: 'Journal Name, the part of the URL after `/journal/`', + issue: 'Release Number, the number in the URL after `/vol/` (If both Volume and Issue exist, must use the `Volume-Issue` form, e.g., `/elsevier/aace-clinical-case-reports/7-6`)', + }, radar: [ { - source: ['www.sciencedirect.com/journal/:journal/*'], - target: '/:journal', + source: ['www.sciencedirect.com/journal/:journal/vol/:issue'], + target: '/:journal/:issue', }, ], - name: 'Unknown', - maintainers: [], + name: 'Special Issue', + maintainers: ['Derekmini', 'sunwolf-swb'], handler, }; diff --git a/lib/routes/elsevier/journal.ts b/lib/routes/elsevier/journal.ts index c78999647c5c..622b7d3e4ed1 100644 --- a/lib/routes/elsevier/journal.ts +++ b/lib/routes/elsevier/journal.ts @@ -10,15 +10,20 @@ import { renderDescription } from './templates/description'; const cookieJar = new CookieJar(); export const route: Route = { - path: ['/:journal/latest', '/:journal'], + path: '/:journal', + categories: ['journal'], + example: '/elsevier/signal-processing', + parameters: { + journal: 'Journal Name, the part of the URL after `/journal/`', + }, radar: [ { source: ['www.sciencedirect.com/journal/:journal/*'], target: '/:journal', }, ], - name: 'Unknown', - maintainers: [], + name: 'Journal', + maintainers: ['Derekmini', 'sunwolf-swb'], handler, }; diff --git a/lib/routes/embassy/index.ts b/lib/routes/embassy/index.ts index e881792ec262..d8aaee4f3235 100644 --- a/lib/routes/embassy/index.ts +++ b/lib/routes/embassy/index.ts @@ -9,7 +9,13 @@ import supportedList from './supported-list'; export const route: Route = { path: '/:country/:city?', - name: 'Unknown', + categories: ['government'], + example: '/embassy/us/chicago', + parameters: { + country: '国家短代码, 见支持国家列表', + city: '城市, 对应国家列表下的`领事馆城市列表`,不填则为大使馆', + }, + name: '使领馆重要通知', maintainers: ['HenryQW'], handler, }; diff --git a/lib/routes/fda/cdrh.ts b/lib/routes/fda/cdrh.ts index 32c1b4d1d60d..8cdafc455d0d 100644 --- a/lib/routes/fda/cdrh.ts +++ b/lib/routes/fda/cdrh.ts @@ -7,14 +7,17 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/cdrh/:titleOnly?', + categories: ['government'], + example: '/fda/cdrh', + parameters: { titleOnly: 'Title only, empty by default which includes the full text, any other value shows the title only' }, radar: [ { source: ['fda.gov/medical-devices/news-events-medical-devices/cdrhnew-news-and-updates', 'fda.gov/'], target: '/cdrh/:titleOnly', }, ], - name: 'Unknown', - maintainers: [], + name: 'CDRHNew', + maintainers: ['nczitzk'], handler, url: 'fda.gov/medical-devices/news-events-medical-devices/cdrhnew-news-and-updates', }; diff --git a/lib/routes/firefox/release.ts b/lib/routes/firefox/release.ts index 96b5c14954b6..232c2c891dda 100644 --- a/lib/routes/firefox/release.ts +++ b/lib/routes/firefox/release.ts @@ -14,8 +14,14 @@ const platformSlugs = { export const route: Route = { path: '/release/:platform?', - name: 'Unknown', - maintainers: [], + categories: ['program-update'], + example: '/firefox/release/desktop', + parameters: { platform: 'the platform' }, + description: `| Desktop | Android | Beta | Nightly | iOS | +| ------- | ------- | ---- | ------- | --- | +| desktop | android | beta | nightly | ios |`, + name: 'New Release', + maintainers: ['fengkx'], handler, }; diff --git a/lib/routes/fishshell/index.ts b/lib/routes/fishshell/index.ts index 9c6450fd3f60..b9433ba862e4 100644 --- a/lib/routes/fishshell/index.ts +++ b/lib/routes/fishshell/index.ts @@ -8,13 +8,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['program-update'], + example: '/fishshell', radar: [ { source: ['fishshell.com/'], target: '', }, ], - name: 'Unknown', + name: 'Release Notes', maintainers: ['x2cf'], handler, url: 'fishshell.com/', diff --git a/lib/routes/fx-markets/channel.ts b/lib/routes/fx-markets/channel.ts index 9d8f74994168..5a9b19fbc924 100644 --- a/lib/routes/fx-markets/channel.ts +++ b/lib/routes/fx-markets/channel.ts @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: 'Channel', - maintainers: [], + maintainers: ['mikkkee'], handler, description: `| Trading | Infrastructure | Tech and Data | Regulation | | ------- | -------------- | ------------- | ---------- | diff --git a/lib/routes/gamme/category.ts b/lib/routes/gamme/category.ts index c49830f28039..2abbf9898f31 100644 --- a/lib/routes/gamme/category.ts +++ b/lib/routes/gamme/category.ts @@ -9,8 +9,14 @@ import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:domain/:category?', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/gamme/news', + parameters: { + domain: '網站,`news` 為宅宅新聞,`sexynews` 為西斯新聞', + category: '分類名,可在 URL 找到,預設為全部', + }, + name: '分類', + maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/gamme/tag.ts b/lib/routes/gamme/tag.ts index c968f90d0056..8b7234ac16b3 100644 --- a/lib/routes/gamme/tag.ts +++ b/lib/routes/gamme/tag.ts @@ -9,8 +9,14 @@ import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:domain/tag/:tag', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/gamme/news/tag/歐派', + parameters: { + domain: '網站,`news` 為宅宅新聞,`sexynews` 為西斯新聞', + tag: '標籤,可在 URL 找到', + }, + name: '標籤', + maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/gdsrx/index.ts b/lib/routes/gdsrx/index.ts index 81e88bf272d9..4c374da646ad 100644 --- a/lib/routes/gdsrx/index.ts +++ b/lib/routes/gdsrx/index.ts @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: '栏目', - maintainers: [], + maintainers: ['nczitzk'], handler, description: `| 栏目名称 | 栏目 id | | ----------------- | ------- | diff --git a/lib/routes/gelonghui/live.tsx b/lib/routes/gelonghui/live.tsx index 304f62f79574..1c200884c96d 100644 --- a/lib/routes/gelonghui/live.tsx +++ b/lib/routes/gelonghui/live.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: '实时快讯', - maintainers: [], + maintainers: ['TonyRL'], handler, url: 'gelonghui.com/live', }; diff --git a/lib/routes/genossenschaften/index.ts b/lib/routes/genossenschaften/index.ts index ac726a0d9fad..5a823597e77d 100644 --- a/lib/routes/genossenschaften/index.ts +++ b/lib/routes/genossenschaften/index.ts @@ -11,7 +11,7 @@ const PATH_PREFIX = '/genossenschaften/' as const; export const route: Route = { name: 'Immobiliensuche', - path: '*', + path: '/:path{.+}?', maintainers: ['sk22'], categories: ['other'], description: `Note that all parameters are optional and many can be specified multiple times @@ -39,6 +39,7 @@ filters, copy the part of the URL after the \`?\`. '&status=available&status=construction&status=planned' + '&type=residence&type=project', parameters: { + path: 'Query parameters copied from the search URL, without the leading `?`, composed of the parameters below', // labels are in german language because it's the same on the website cost: 'Miete bis (in €, number)', district: 'Bezirk (string, multiple)', @@ -62,7 +63,7 @@ filters, copy the part of the URL after the \`?\`. supportScihub: false, }, async handler(ctx) { - let path = ctx.req.path.slice(PATH_PREFIX.length); + let path = ctx.req.param('path') ?? ''; if (path.startsWith('&')) { // in case request url is something like `/genossenschaften/&cost=…` path = path.slice(1); diff --git a/lib/routes/gesiba/index.ts b/lib/routes/gesiba/index.ts index cebbc8b86171..9c59d0ed0f7c 100644 --- a/lib/routes/gesiba/index.ts +++ b/lib/routes/gesiba/index.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Data, DataItem, Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import ofetch from '@/utils/ofetch'; const FEED_TITLE = 'Wohnungen - Gesiba' as const; @@ -16,7 +15,8 @@ const MAGIC_QUERY_PARAMS = export const route: Route = { name: 'Angebote', example: '/gesiba/verfuegbar=alle&plz[]=1100&plz[]=1120&size-from=45&size-to=80&rooms-from=2&rooms-to=3&betreuung=0', - path: '*', + path: '/:path{.+}?', + parameters: { path: 'Search filter parameters, see the description below' }, maintainers: ['sk22'], categories: ['other'], description: `Note that, on <https://www.gesiba.at/immobilien/wohnungen>, filters are added to @@ -24,7 +24,7 @@ the URL like \`&filter[plz]=1100,1120\`, but the endpoint used here expects it like \`&plz[]=1100&plz[]=1120\`, if multiple values are passed to one parameter`, async handler(ctx) { - let params = getSubPath(ctx).slice(1); + let params = ctx.req.param('path') ?? ''; if (params.startsWith('&')) { params = params.slice(1); } diff --git a/lib/routes/getdr/index.ts b/lib/routes/getdr/index.ts index 296d65b62f93..51fd651847b0 100644 --- a/lib/routes/getdr/index.ts +++ b/lib/routes/getdr/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/getdr', radar: [ { source: ['getdr.com/'], target: '', }, ], - name: 'Unknown', + name: '最新詐騙情報', maintainers: ['nczitzk'], handler, url: 'getdr.com/', diff --git a/lib/routes/getitfree/index.ts b/lib/routes/getitfree/index.ts index fcbf6fd29c30..dd31e6956258 100644 --- a/lib/routes/getitfree/index.ts +++ b/lib/routes/getitfree/index.ts @@ -1,20 +1,40 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { apiSlug, bakeFilterSearchParams, bakeFiltersWithPair, bakeUrl, fetchData, getFilterNameForTitle, getFilterParamsForUrl, parseFilterStr, rootUrl } from './util'; export const route: Route = { - path: '/:filter{.+}?', - name: 'Unknown', - maintainers: [], + path: '/category/:id{.+}?', + categories: ['shopping'], + example: '/getitfree/category/pc', + parameters: { id: '分类,见下表,可在对应分类页中找到,默认为所有类别' }, + radar: [ + { + source: ['getitfree.cn/category/:id'], + target: '/category/:id', + }, + ], + name: '分类', + maintainers: ['sanmmm', 'nczitzk'], handler, + url: 'getitfree.cn', + description: `::: tip +可以叠加使用得到分类结果并集,如 [\`/getitfree/category/pc,android\`](https://rsshub.app/getitfree/category/pc,android) + +亦可与标签组合使用,如 [\`/getitfree/category/pc/tag/ai\`](https://rsshub.app/getitfree/category/pc/tag/ai) +::: + +| 所有类别 | Android | iOS | Mac | PC | UWP | 公告 | 永久免费 | 限时免费 | 正版折扣 | +| -------- | ------- | --- | --- | -- | --- | ------------ | -------- | -------- | -------- | +| | android | ios | mac | pc | uwp | notification | free | giveaway | discount |`, }; -async function handler(ctx) { - const filter = ctx.req.param('filter'); +export async function handler(ctx) { + const filter = getSubPath(ctx).slice(1); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50; const filters = parseFilterStr(filter); diff --git a/lib/routes/getitfree/search.ts b/lib/routes/getitfree/search.ts new file mode 100644 index 000000000000..948b3b908974 --- /dev/null +++ b/lib/routes/getitfree/search.ts @@ -0,0 +1,24 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/search/:keyword', + categories: ['shopping'], + example: '/getitfree/search/windows', + parameters: { keyword: '关键字' }, + radar: [ + { + source: ['getitfree.cn/'], + target: (_, url) => { + const keyword = new URL(url).searchParams.get('s'); + + return `/getitfree/search${keyword ? `/${keyword}` : ''}`; + }, + }, + ], + name: '搜索', + maintainers: ['sanmmm', 'nczitzk'], + handler, + url: 'getitfree.cn', +}; diff --git a/lib/routes/getitfree/tag.ts b/lib/routes/getitfree/tag.ts new file mode 100644 index 000000000000..ea2ce4b6e2ba --- /dev/null +++ b/lib/routes/getitfree/tag.ts @@ -0,0 +1,68 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:id{.+}?', + categories: ['shopping'], + example: '/getitfree/tag/ai', + parameters: { id: '标签,见下表,可在对应标签页中找到,默认为所有标签' }, + radar: [ + { + source: ['getitfree.cn/tag/:id'], + target: '/tag/:id', + }, + ], + name: '标签', + maintainers: ['nczitzk'], + handler, + url: 'getitfree.cn', + description: `::: tip +可以叠加使用得到标签结果并集,如 [\`/getitfree/tag/ai,office\`](https://rsshub.app/getitfree/tag/ai,office) +::: + +| AI | Android | DVD | GIF | iCloud | +| -- | ------- | --- | --- | ------ | + +| ICON | iOS | iPhone | Leawo | Mac | +| ---- | --- | ------ | ----- | --- | + +| Markdown | OCR | Office | Office 办公 | PDF | +| -------- | --- | ------ | ----------- | --- | + +| PDF 转换 | Windows | 下载 | 书签管理 | 健康 | +| -------- | ------- | ---- | -------- | ---- | + +| 全平台 | 办公软件 | 加密保护 | 卸载 / 清理 / 优化 | 卸载清理 | +| ------ | -------- | -------- | ------------------ | -------- | + +| 图像处理 | 图片 / 编辑 / 管理 | 壁纸 | 备份 / 恢复 / 加密 | 备份恢复 | +| -------- | ------------------ | ---- | ------------------ | -------- | + +| 字体 | 学习 | 密码管理 | 导航 | 开始菜单 | +| ---- | ---- | -------- | ---- | -------- | + +| 录屏截图 | 影音 / 刻录 / 转换 | 投屏镜像 | 提醒 | 摄影 | +| -------- | ------------------ | -------- | ---- | ---- | + +| 播放器 | 收藏夹 | 效率工具 | 数据传输 | 数据恢复 | +| ------ | ------ | -------- | -------- | -------- | + +| 数据擦除 | 文件管理 | 文档 / 转换 / 压缩 | 文档扫描 | 日历 | +| -------- | -------- | ------------------ | -------- | ---- | + +| 杀毒防护 | 查重 | 正版特惠 | 水印 | 清理优化 | +| -------- | ---- | -------- | ---- | -------- | + +| 滤镜 | 硬件外设 | 硬盘 / 分区 / 驱动 | 硬盘检测 | 稍后阅读 | +| ---- | -------- | ------------------ | -------- | -------- | + +| 笔记 | 系统工具 | 素材 | 网络 / 安全 / 传输 | 网络安全 | +| ---- | -------- | ---- | ------------------ | -------- | + +| 色彩 | 视频剪辑 | 视频转换 | 记账 | 设计 | +| ---- | -------- | -------- | ---- | ---- | + +| 软件卸载 | 远程控制 | 音乐 | 音频 | 驱动 | +| -------- | -------- | ---- | ---- | ---- |`, +}; diff --git a/lib/routes/globallawreview/index.ts b/lib/routes/globallawreview/index.ts index 35386c6e1173..0a032053beea 100644 --- a/lib/routes/globallawreview/index.ts +++ b/lib/routes/globallawreview/index.ts @@ -5,13 +5,15 @@ import got from '@/utils/got'; export const route: Route = { path: '/', + categories: ['journal'], + example: '/globallawreview', radar: [ { source: ['globallawreview.org/Magazine/GetIssueContentList', 'globallawreview.org/'], target: '', }, ], - name: 'Unknown', + name: '期刊', maintainers: ['nczitzk'], handler, url: 'globallawreview.org/Magazine/GetIssueContentList', diff --git a/lib/routes/gocn/news.ts b/lib/routes/gocn/news.ts index 48cc00fe43da..8dc36f507776 100644 --- a/lib/routes/gocn/news.ts +++ b/lib/routes/gocn/news.ts @@ -5,8 +5,24 @@ import { parseDate } from '@/utils/parse-date'; import { renderHTML } from './utils'; export const route: Route = { - path: ['/', '/news'], - name: 'Unknown', + path: '/news', + categories: ['programming'], + example: '/gocn/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['gocn.vip/'], + }, + ], + name: '最新动态', maintainers: ['AtlanCI', 'CcccFz'], handler, url: 'gocn.vip/', diff --git a/lib/routes/gov/chongqing/gzw.ts b/lib/routes/gov/chongqing/gzw.ts index a3aa1c2b7672..54ff1b105340 100644 --- a/lib/routes/gov/chongqing/gzw.ts +++ b/lib/routes/gov/chongqing/gzw.ts @@ -8,6 +8,8 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/gzw/:category{.+}?', + categories: ['government'], + example: '/gov/chongqing/gzw', parameters: { category: '分类,见下表,默认为通知公告', }, diff --git a/lib/routes/gov/mfa/wjdt.ts b/lib/routes/gov/mfa/wjdt.ts index 0ba270c50c9d..0cdeb13e0c04 100644 --- a/lib/routes/gov/mfa/wjdt.ts +++ b/lib/routes/gov/mfa/wjdt.ts @@ -20,6 +20,11 @@ const categories = { export const route: Route = { path: '/wjdt/:category?', + categories: ['government'], + example: '/gov/mfa/wjdt/fyrbt', + parameters: { + category: '分类,见下表,默认为领导人活动', + }, name: '外交动态', maintainers: ['nicolaszf', 'nczitzk'], handler, diff --git a/lib/routes/gq/news.ts b/lib/routes/gq/news.ts index 67750f05013d..723d58d360a4 100644 --- a/lib/routes/gq/news.ts +++ b/lib/routes/gq/news.ts @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'News', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/grubstreet/index.ts b/lib/routes/grubstreet/index.ts index 890d4865a1ac..08fa6aa6a086 100644 --- a/lib/routes/grubstreet/index.ts +++ b/lib/routes/grubstreet/index.ts @@ -4,13 +4,15 @@ import utils from './utils'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/grubstreet', radar: [ { source: ['grubstreet.com/'], target: '', }, ], - name: 'Unknown', + name: 'Posts', maintainers: ['loganrockmore'], handler, url: 'grubstreet.com/', diff --git a/lib/routes/guancha/topic.ts b/lib/routes/guancha/topic.ts index f2dc57f51742..9bd3327990cc 100644 --- a/lib/routes/guancha/topic.ts +++ b/lib/routes/guancha/topic.ts @@ -7,16 +7,34 @@ import { parseRelativeDate } from '@/utils/parse-date'; export const route: Route = { path: '/topic/:id/:order?', + categories: ['new-media'], + example: '/guancha/topic/110/1', + parameters: { id: '话题 id,可在URL中找到,默认为全部,即为 `0`', order: '排序参数,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['guancha.cn/'], target: '/:category?', }, ], - name: 'Unknown', + name: '风闻话题', maintainers: ['occupy5', 'nczitzk'], handler, url: 'guancha.cn/', + description: `| 最新回复 | 最新发布 | 24 小时最热 | 3 天最热 | 7 天最热 | 3 个月最热 | 专栏文章 | +| -------- | -------- | ----------- | -------- | -------- | ---------- | -------- | +| 1 | 2 | 3 | 6 | 7 | 8 | 5 | + +::: tip +仅在话题 id 为 0,即选择 全部 时,**3 个月最热**、**24 小时最热**、**3 天最热**、**7 天最热** 和 **专栏文章** 参数生效。 +:::`, }; async function handler(ctx) { diff --git a/lib/routes/guanhai/index.ts b/lib/routes/guanhai/index.ts index 46382d7fbcd6..33c6bc2c18cb 100644 --- a/lib/routes/guanhai/index.ts +++ b/lib/routes/guanhai/index.ts @@ -8,13 +8,15 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/guanhai', radar: [ { source: ['guanhai.com.cn/'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['TonyRL'], handler, url: 'guanhai.com.cn/', diff --git a/lib/routes/hackertalk/index.ts b/lib/routes/hackertalk/index.ts index 6078c7badad2..1a70af0bd987 100644 --- a/lib/routes/hackertalk/index.ts +++ b/lib/routes/hackertalk/index.ts @@ -8,13 +8,15 @@ const md = MarkdownIt(); export const route: Route = { path: '/', + categories: ['bbs'], + example: '/hackertalk', radar: [ { source: ['hackertalk.net/'], target: '', }, ], - name: 'Unknown', + name: '最新帖子', maintainers: ['hyoban'], handler, url: 'hackertalk.net/', diff --git a/lib/routes/hackyournews/index.ts b/lib/routes/hackyournews/index.ts index 099825763e12..4f3dbba12f95 100644 --- a/lib/routes/hackyournews/index.ts +++ b/lib/routes/hackyournews/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['programming'], + example: '/hackyournews', radar: [ { source: ['hackyournews.com/'], target: '', }, ], - name: 'Unknown', + name: 'Index', maintainers: ['ftiasch'], handler, url: 'hackyournews.com/', diff --git a/lib/routes/hafu/news.ts b/lib/routes/hafu/news.ts index a384ce3ff62b..762e221b7cfc 100644 --- a/lib/routes/hafu/news.ts +++ b/lib/routes/hafu/news.ts @@ -16,7 +16,7 @@ export const route: Route = { supportScihub: false, }, name: '河南财政金融学院', - maintainers: [], + maintainers: ['deep1nlife'], handler, description: `| 校内公告通知 | 教务处公告通知 | 招生就业处公告通知 | | ------------ | -------------- | ------------------ | diff --git a/lib/routes/hinatazaka46/blog.ts b/lib/routes/hinatazaka46/blog.ts index 9be7b64f7072..ba47f46cdcb4 100644 --- a/lib/routes/hinatazaka46/blog.ts +++ b/lib/routes/hinatazaka46/blog.ts @@ -20,7 +20,7 @@ export const route: Route = { supportScihub: false, }, name: 'Hinatazaka46 Blog 日向坂 46 博客', - maintainers: [], + maintainers: ['yj-qin', 'AkashiGakki'], handler, description: `Member ID diff --git a/lib/routes/hinatazaka46/news.ts b/lib/routes/hinatazaka46/news.ts index f9873fe6d0f8..ad40cb59be99 100644 --- a/lib/routes/hinatazaka46/news.ts +++ b/lib/routes/hinatazaka46/news.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: 'Hinatazaka46 News 日向坂 46 新闻', - maintainers: ['crispgm', 'akashigakki'], + maintainers: ['crispgm', 'AkashiGakki'], handler, url: 'hinatazaka46.com/s/official/news/list', }; diff --git a/lib/routes/hk01/channel.ts b/lib/routes/hk01/channel.ts index 80d28867bf7f..becc5283a2c1 100644 --- a/lib/routes/hk01/channel.ts +++ b/lib/routes/hk01/channel.ts @@ -5,13 +5,16 @@ import { apiRootUrl, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: '/channel/:id?', + categories: ['new-media'], + example: '/hk01/channel/391', + parameters: { id: '子栏目 id, 可在 URL 中找到' }, radar: [ { source: ['hk01.com/channel/:id', 'hk01.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: '子栏目', + maintainers: ['hoilc', 'Fatpandac', 'nczitzk'], handler, }; diff --git a/lib/routes/hk01/issue.ts b/lib/routes/hk01/issue.ts index 01aadcc3804c..6d95b3b47f93 100644 --- a/lib/routes/hk01/issue.ts +++ b/lib/routes/hk01/issue.ts @@ -5,13 +5,16 @@ import { apiRootUrl, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: '/issue/:id?', + categories: ['new-media'], + example: '/hk01/issue/649', + parameters: { id: '专题 id, 可在 URL 中找到' }, radar: [ { source: ['hk01.com/issue/:id', 'hk01.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: '专题', + maintainers: ['hoilc', 'Fatpandac', 'nczitzk'], handler, }; diff --git a/lib/routes/hk01/tag.ts b/lib/routes/hk01/tag.ts index 34b7bed106c9..5bb4d3bf7ab1 100644 --- a/lib/routes/hk01/tag.ts +++ b/lib/routes/hk01/tag.ts @@ -5,13 +5,16 @@ import { apiRootUrl, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: '/tag/:id?', + categories: ['new-media'], + example: '/hk01/tag/2787', + parameters: { id: '标签 id, 可在 URL 中找到' }, radar: [ { source: ['hk01.com/tag/:id', 'hk01.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: '标签', + maintainers: ['hoilc', 'Fatpandac', 'nczitzk'], handler, }; diff --git a/lib/routes/hk01/zone.ts b/lib/routes/hk01/zone.ts index 04bfc7f70bdd..0f6628b588c0 100644 --- a/lib/routes/hk01/zone.ts +++ b/lib/routes/hk01/zone.ts @@ -5,13 +5,16 @@ import { apiRootUrl, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: '/zone/:id?', + categories: ['new-media'], + example: '/hk01/zone/11', + parameters: { id: '栏目 id, 可在 URL 中找到' }, radar: [ { source: ['hk01.com/zone/:id', 'hk01.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: '栏目', + maintainers: ['hoilc', 'Fatpandac', 'nczitzk'], handler, }; diff --git a/lib/routes/hkjunkcall/index.ts b/lib/routes/hkjunkcall/index.ts index b8b1f9eae90d..5646b135e26c 100644 --- a/lib/routes/hkjunkcall/index.ts +++ b/lib/routes/hkjunkcall/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/hkjunkcall', radar: [ { source: ['hkjunkcall.com/'], target: '', }, ], - name: 'Unknown', + name: '近期資訊', maintainers: ['nczitzk'], handler, url: 'hkjunkcall.com/', diff --git a/lib/routes/hongkong/chp.ts b/lib/routes/hongkong/chp.ts index 28a0f6ad7b04..792298fb1ec0 100644 --- a/lib/routes/hongkong/chp.ts +++ b/lib/routes/hongkong/chp.ts @@ -40,15 +40,29 @@ const titles = { export const route: Route = { path: '/chp/:category?/:language?', + categories: ['government'], + example: '/hongkong/chp', + parameters: { category: 'Category, see below, Important Topics by default', language: 'Language, see below, zh_tw by default' }, radar: [ { source: ['dh.gov.hk/'], }, ], - name: 'Unknown', + name: 'Category', maintainers: ['nczitzk'], handler, url: 'dh.gov.hk/', + description: `Category + +| Important Topics | Press Releases | Response Level | Periodicals & Publications | Health Notice | +| ---------------- | ------------------ | -------------- | -------------------------- | ------------- | +| important\\_ft | press\\_data\\_index | ResponseLevel | publication | HealthAlert | + +Language + +| English | 中文简体 | 中文繁體 | +| ------- | -------- | -------- | +| en | zh\\_cn | zh\\_tw |`, }; async function handler(ctx) { diff --git a/lib/routes/hostmonit/cloudflareyesv6.ts b/lib/routes/hostmonit/cloudflareyesv6.ts deleted file mode 100644 index ca54d6dc5276..000000000000 --- a/lib/routes/hostmonit/cloudflareyesv6.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Route } from '@/types'; - -export const route: Route = { - path: '/cloudflareyesv6', - name: 'Unknown', - maintainers: [], - handler, -}; - -function handler(ctx) { - ctx.set('redirect', '/hostmonit/cloudflareyes/v6'); -} diff --git a/lib/routes/hrbeu/gx/card.ts b/lib/routes/hrbeu/gx/card.ts index 4788b3841357..100fe8b18bb3 100644 --- a/lib/routes/hrbeu/gx/card.ts +++ b/lib/routes/hrbeu/gx/card.ts @@ -8,8 +8,14 @@ const rootUrl = 'http://news.hrbeu.edu.cn'; export const route: Route = { path: '/gx/card/:column/:id?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/hrbeu/gx/card/xw/ztch', + parameters: { + column: '主栏,如 `新闻:xw`,由 `URL` 中获取;', + id: '次栏,如 `要闻:yw`,如果次栏存在,则为必选,由 `URL` 中获取。', + }, + name: '工学新闻 - 卡片页面', + maintainers: ['Derekmini', 'XYenon'], handler, }; diff --git a/lib/routes/hrbeu/gx/list.ts b/lib/routes/hrbeu/gx/list.ts index 8e00e8332c4e..38e1ddd71c81 100644 --- a/lib/routes/hrbeu/gx/list.ts +++ b/lib/routes/hrbeu/gx/list.ts @@ -9,8 +9,14 @@ const rootUrl = 'http://news.hrbeu.edu.cn'; export const route: Route = { path: '/gx/list/:column/:id?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/hrbeu/gx/list/xw/yw', + parameters: { + column: '主栏,如 `新闻:xw`,由 `URL` 中获取;', + id: '次栏,如 `要闻:yw`,如果次栏存在,则为必选,由 `URL` 中获取。', + }, + name: '工学新闻 - 列表页面', + maintainers: ['Derekmini', 'XYenon'], handler, }; diff --git a/lib/routes/hrbeu/uae/news.ts b/lib/routes/hrbeu/uae/news.ts index 10c95560932d..0bf761fb0533 100644 --- a/lib/routes/hrbeu/uae/news.ts +++ b/lib/routes/hrbeu/uae/news.ts @@ -25,7 +25,7 @@ export const route: Route = { }, ], name: '水声工程学院', - maintainers: [], + maintainers: ['Derekmini'], handler, description: `| 新闻动态 | 通知公告 | 科学研究 / 科研动态 | | :------: | :------: | :-----------------: | diff --git a/lib/routes/hunau/gfxy/index.ts b/lib/routes/hunau/gfxy/index.ts index c5ab543db9ef..bd92b7ce3e9c 100644 --- a/lib/routes/hunau/gfxy/index.ts +++ b/lib/routes/hunau/gfxy/index.ts @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '公共管理与法学学院', - maintainers: [], + maintainers: ['lcandy2'], handler, url: 'xky.hunau.edu.cn/', description: `| 分类 | 通知公告 | 学院新闻 | 其他分类通知... | diff --git a/lib/routes/hunau/jwc.ts b/lib/routes/hunau/jwc.ts index 9a6142490128..6ea970bc9107 100644 --- a/lib/routes/hunau/jwc.ts +++ b/lib/routes/hunau/jwc.ts @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '教务处', - maintainers: [], + maintainers: ['lcandy2'], handler, url: 'xky.hunau.edu.cn/', description: `| 分类 | 通知公告 | 教务动态 | 其他教务通知... | diff --git a/lib/routes/hunau/xky/index.ts b/lib/routes/hunau/xky/index.ts index f7466042860f..a299c954a4d6 100644 --- a/lib/routes/hunau/xky/index.ts +++ b/lib/routes/hunau/xky/index.ts @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '信息与智能科学学院', - maintainers: [], + maintainers: ['lcandy2'], handler, url: 'xky.hunau.edu.cn/', description: `| 分类 | 通知公告 | 学院新闻 | 其他分类通知... | diff --git a/lib/routes/ielts/index.ts b/lib/routes/ielts/index.ts index 52ca7ed59b5d..d177968d55e0 100644 --- a/lib/routes/ielts/index.ts +++ b/lib/routes/ielts/index.ts @@ -12,13 +12,15 @@ const targetUrl = 'https://ielts.neea.cn/allnews?locale=zh_CN'; export const route: Route = { path: '/', + categories: ['study'], + example: '/ielts', radar: [ { source: ['ielts.neea.cn/allnews'], target: '', }, ], - name: 'Unknown', + name: '最新消息', maintainers: ['zenxds'], handler, url: 'ielts.neea.cn/allnews', diff --git a/lib/routes/ifeng/news.tsx b/lib/routes/ifeng/news.tsx index a509209f7059..b3e2205a2652 100644 --- a/lib/routes/ifeng/news.tsx +++ b/lib/routes/ifeng/news.tsx @@ -4,23 +4,31 @@ import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/news/*', - name: 'Unknown', - maintainers: [], + path: '/news/:path{.+}?', + categories: ['new-media'], + example: '/ifeng/news', + parameters: { path: '路径,对应分类资讯页 URL 路径,默认为空' }, + name: '资讯', + maintainers: ['nczitzk'], handler, + description: `::: tip +路径处填写对应页面 URL 中 \`https://news.ifeng.com/\` 后的字段。下面是一个例子。 + +若订阅 [大湾区\\_资讯\\_凤凰网](https://news.ifeng.com/shanklist/3-305565-) 则将对应页面 URL \`https://news.ifeng.com/shanklist/3-305565-\` 中 \`https://news.ifeng.com/\` 后的字段 \`shanklist/3-305565-\` 作为路径填入。此时路由为 [\`/ifeng/news/shanklist/3-305565-\`](https://rsshub.app/ifeng/news/shanklist/3-305565-) +:::`, }; async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; + const path = ctx.req.param('path'); const rootUrl = 'https://news.ifeng.com'; - const currentUrl = `${rootUrl}${getSubPath(ctx).replace(/^\/news/, '')}`; + const currentUrl = `${rootUrl}${path ? `/${path}` : ''}`; const response = await got({ method: 'get', diff --git a/lib/routes/ifi-audio/download.ts b/lib/routes/ifi-audio/download.ts index d6b61b991c40..0e15228185f8 100644 --- a/lib/routes/ifi-audio/download.ts +++ b/lib/routes/ifi-audio/download.ts @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: 'Download Hub', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: `::: warning diff --git a/lib/routes/iiilab/index.ts b/lib/routes/iiilab/index.ts index ac3235e07a97..0c773ea29497 100644 --- a/lib/routes/iiilab/index.ts +++ b/lib/routes/iiilab/index.ts @@ -5,13 +5,15 @@ const baseUrl = 'https://www.iiilab.com/'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/iiilab', radar: [ { source: ['www.iiilab.com/'], target: '', }, ], - name: 'Unknown', + name: '发现', maintainers: ['Joey'], handler, url: 'www.iiilab.com/', diff --git a/lib/routes/inoreader/index.ts b/lib/routes/inoreader/index.ts index 205f462482b3..bb49e64f3357 100644 --- a/lib/routes/inoreader/index.ts +++ b/lib/routes/inoreader/index.ts @@ -8,10 +8,11 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/html_clip/:user/:tag', example: '/inoreader/html_clip/1005137674/user-favorites', + parameters: { user: 'User id, can be found in the HTML clip URL', tag: 'Tag name, can be found in the HTML clip URL' }, categories: ['reading'], view: ViewType.Articles, name: 'HTML Clip', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/inoreader/rss.ts b/lib/routes/inoreader/rss.ts index c19c420a564b..ee02e90b1382 100644 --- a/lib/routes/inoreader/rss.ts +++ b/lib/routes/inoreader/rss.ts @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: 'RSS', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/iqilu/program.ts b/lib/routes/iqilu/program.ts index 47d794d10ac5..bb87acbc45f5 100644 --- a/lib/routes/iqilu/program.ts +++ b/lib/routes/iqilu/program.ts @@ -9,8 +9,33 @@ import { renderDescription } from './templates/description'; export const route: Route = { path: '/v/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['traditional-media'], + example: '/iqilu/v/sdws/sdxwlb', + parameters: { + category: '节目 id,可在对应节目页 URL 中找到,见下表,默认为 `sdws/sdxwlb`,即山东新闻联播', + }, + features: { + supportPodcast: true, + }, + name: '电视节目', + maintainers: ['nczitzk'], + description: `| 节目名称 | 节目 id | +| ---------------- | -------------- | +| 山东新闻联播 | sdws/sdxwlb | +| 闪电大视野 | ggpd/sddsy | +| 山东三农新闻联播 | nkpd/snxw | +| 每日新闻 | qlpd/mrxw | +| 新闻午班车 | ggpd/xwwbc | +| 戏宇宙 | sdws/xyz/ | +| 中国礼 中国乐 | qlpd/zglzgy | +| 超级语文课 | sdws/cjywk | +| 文物里的山东 | yspd/wwldsd | +| 拉呱 | qlpd/l0 | +| 生活帮 | shpd/shb | +| 快乐大赢家 | zypd/kldyj | +| 乡村季风 | nkpd/xcjf | +| 健康是 1 | ggpd/jks1 | +| 此时此刻 | sdws/cishicike |`, handler, }; diff --git a/lib/routes/itch/index.ts b/lib/routes/itch/index.ts index 9ca5eb3ca050..c225ccc0dd0a 100644 --- a/lib/routes/itch/index.ts +++ b/lib/routes/itch/index.ts @@ -2,21 +2,30 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['game'], + example: '/itch/games/new-and-popular/featured', + parameters: { path: 'Params' }, + name: 'Browse', + maintainers: ['nczitzk'], + description: `The path is the field after \`itch.io\` in the URL of the corresponding page, e.g. the URL of [Top Rated Games tagged Singleplayer](https://itch.io/games/top-rated/tag-singleplayer) is \`https://itch.io/games/top-rated/tag-singleplayer\`, where the field after \`itch.io\` is \`/games/top-rated/tag-singleplayer\`. + +So the route is [\`/itch/games/top-rated/tag-singleplayer\`](https://rsshub.app/itch/games/top-rated/tag-singleplayer). + +::: tip +You can browse all the tags [here](https://itch.io/tags). +:::`, handler, }; async function handler(ctx) { const rootUrl = 'https://itch.io'; - const currentUrl = `${rootUrl}${getSubPath(ctx)}`; + const currentUrl = `${rootUrl}/${ctx.req.param('path') ?? ''}`; const response = await got({ method: 'get', diff --git a/lib/routes/javdb/lists.ts b/lib/routes/javdb/lists.ts index 83a09d64f158..972122360795 100644 --- a/lib/routes/javdb/lists.ts +++ b/lib/routes/javdb/lists.ts @@ -4,16 +4,34 @@ import utils from './utils'; export const route: Route = { path: '/lists/:id/:filter?/:sort?', + categories: ['multimedia'], + example: '/javdb/lists/2GPgB', + parameters: { + id: '编号,可在清单页 URL 中找到', + filter: '过滤,见下表,默认为 `全部`,需要占位时可设置为 `none`', + sort: '排序,见下表,默认为 `加入时间排序`', + }, radar: [ { source: ['javdb.com/'], target: '', }, ], - name: 'Unknown', + name: '清单', maintainers: ['dddepg'], handler, url: 'javdb.com/', + description: `过滤 + +| 全部 | 占位 | 可播放 | 單體作品 | 含磁链 | 含字幕 | 預覽圖 | +| ---- | ---- | -------- | -------- | -------- | ------ | ------- | +| | none | playable | single | download | cnsub | preview | + +排序 + +| 加入时间排序 | 发布时间排序 | +| ------------ | ------------ | +| 0 | 1 |`, features: { nsfw: true, }, diff --git a/lib/routes/javlibrary/bestrated.ts b/lib/routes/javlibrary/bestrated.ts index 482f1c9bbccf..e8dcaf34b981 100644 --- a/lib/routes/javlibrary/bestrated.ts +++ b/lib/routes/javlibrary/bestrated.ts @@ -4,8 +4,11 @@ import { defaultLanguage, defaultMode, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/videos/bestrated/:language?/:mode?', '/bestrated/:language?/:mode?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/bestrated/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, Last Month by default, as `1`' }, + name: 'Best Rated Videos', + maintainers: ['nczitzk'], handler, description: `| Last Month | All Time | | ---------- | -------- | diff --git a/lib/routes/javlibrary/genre.ts b/lib/routes/javlibrary/genre.ts index 4077bfa934bc..e4037f02ec26 100644 --- a/lib/routes/javlibrary/genre.ts +++ b/lib/routes/javlibrary/genre.ts @@ -4,8 +4,11 @@ import { defaultGenre, defaultLanguage, defaultMode, ProcessItems, rootUrl } fro export const route: Route = { path: ['/videos/genre/:genre?/:language?/:mode?', '/genre/:genre?/:language?/:mode?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/genre/amjq/en', + parameters: { genre: 'Category, Acme · Orgasm by default, as `amjq`', language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, videos with comments (by date) by default, as `1`' }, + name: 'Videos by categories', + maintainers: ['nczitzk'], handler, description: `| videos with comments (by date) | everything (by date) | | ------------------------------ | -------------------- | diff --git a/lib/routes/javlibrary/maker.ts b/lib/routes/javlibrary/maker.ts index e11e07c7b847..ee0bcb9fa1df 100644 --- a/lib/routes/javlibrary/maker.ts +++ b/lib/routes/javlibrary/maker.ts @@ -17,7 +17,7 @@ export const route: Route = { nsfw: true, }, name: 'Videos by makers', - maintainers: [], + maintainers: ['Huzhixin00'], handler, description: `| videos with comments (by date) | everything (by date) | | ------------------------------ | -------------------- | diff --git a/lib/routes/javlibrary/mostwanted.ts b/lib/routes/javlibrary/mostwanted.ts index 69bba16e06c9..dfb43c86f3f4 100644 --- a/lib/routes/javlibrary/mostwanted.ts +++ b/lib/routes/javlibrary/mostwanted.ts @@ -4,8 +4,11 @@ import { defaultLanguage, defaultMode, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/videos/mostwanted/:language?/:mode?', '/mostwanted/:language?/:mode?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/mostwanted/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, Last Month by default, as `1`' }, + name: 'Most Wanted Videos', + maintainers: ['nczitzk'], handler, description: `| Last Month | All Time | | ---------- | -------- | diff --git a/lib/routes/javlibrary/newentries.ts b/lib/routes/javlibrary/newentries.ts index 40ec2e36c32f..305666fe5232 100644 --- a/lib/routes/javlibrary/newentries.ts +++ b/lib/routes/javlibrary/newentries.ts @@ -4,8 +4,11 @@ import { defaultLanguage, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/videos/newentries/:language?', '/newentries/:language?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/newentries/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`' }, + name: 'Recently Inserted Videos', + maintainers: ['nczitzk'], handler, features: { nsfw: true, diff --git a/lib/routes/javlibrary/newrelease.ts b/lib/routes/javlibrary/newrelease.ts index def7570066c7..d19d39046fa4 100644 --- a/lib/routes/javlibrary/newrelease.ts +++ b/lib/routes/javlibrary/newrelease.ts @@ -4,8 +4,11 @@ import { defaultLanguage, defaultMode, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/videos/newrelease/:language?/:mode?', '/newrelease/:language?/:mode?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/newrelease/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, videos with comments (by date) by default, as `1`' }, + name: 'New Releases', + maintainers: ['nczitzk'], handler, description: `| videos with comments (by date) | everything (by date) | | ------------------------------ | -------------------- | diff --git a/lib/routes/javlibrary/update.ts b/lib/routes/javlibrary/update.ts index 4079aea21094..c5b524314a0e 100644 --- a/lib/routes/javlibrary/update.ts +++ b/lib/routes/javlibrary/update.ts @@ -4,8 +4,11 @@ import { defaultLanguage, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/videos/update/:language?', '/update/:language?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/update/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`' }, + name: 'Recently Discussed Videos', + maintainers: ['nczitzk'], handler, features: { nsfw: true, diff --git a/lib/routes/javlibrary/user.ts b/lib/routes/javlibrary/user.ts index d1d834151009..dee7f534aeeb 100644 --- a/lib/routes/javlibrary/user.ts +++ b/lib/routes/javlibrary/user.ts @@ -4,8 +4,11 @@ import { defaultLanguage, ProcessItems, rootUrl } from './utils'; export const route: Route = { path: ['/users/:id/:type/:language?', '/:type/:id/:language?'], - name: 'Unknown', - maintainers: [], + categories: ['multimedia'], + example: '/javlibrary/userwatched/mangudai/en', + parameters: { type: 'Type, see below', id: 'User id, can be found in URL', language: 'Language, see below, Japanese by default, as `ja`' }, + name: 'Videos by user', + maintainers: ['nczitzk', 'DIYgod', 'junfengP'], handler, description: `| Wanted | Watched | Owned | | ---------- | ----------- | --------- | diff --git a/lib/routes/jiaoliudao/index.ts b/lib/routes/jiaoliudao/index.ts index 7aa0e6423757..91d3cd2a1e2f 100644 --- a/lib/routes/jiaoliudao/index.ts +++ b/lib/routes/jiaoliudao/index.ts @@ -4,13 +4,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['blog'], + example: '/jiaoliudao', radar: [ { source: ['jiaoliudao.com/'], target: '', }, ], - name: 'Unknown', + name: '最新文章', maintainers: ['TonyRL'], handler, url: 'jiaoliudao.com/', diff --git a/lib/routes/jseea/namespace.ts b/lib/routes/jseea/namespace.ts index 93129b017a48..560e6a41ed83 100644 --- a/lib/routes/jseea/namespace.ts +++ b/lib/routes/jseea/namespace.ts @@ -1,7 +1,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { - name: 'Unknown', + name: '江苏省教育考试院', url: 'jseea.cn', lang: 'zh-CN', }; diff --git a/lib/routes/jseea/news.ts b/lib/routes/jseea/news.ts index aba745372b20..57b20e07b541 100644 --- a/lib/routes/jseea/news.ts +++ b/lib/routes/jseea/news.ts @@ -26,15 +26,21 @@ async function loadContent(link) { export const route: Route = { path: '/news/:type?', + categories: ['government'], + example: '/jseea/news/zkyw', + parameters: { type: '分类,默认为 `zkyw`,具体参数见下表' }, radar: [ { source: ['jseea.cn/webfile/news/:type'], target: '/news/:type', }, ], - name: 'Unknown', + name: '新闻中心', maintainers: ['schen1024'], handler, + description: `| 招考要闻 | 教育动态 | 招考信息 | 政策文件 | 院校动态 | +| :------: | :------: | :------: | :------: | :------: | +| zkyw | jydt | zkxx | zcwj | yxdt |`, }; async function handler(ctx) { diff --git a/lib/routes/jsu/cxzx.ts b/lib/routes/jsu/cxzx.ts index 9b65d748d675..78ac3ea13b92 100644 --- a/lib/routes/jsu/cxzx.ts +++ b/lib/routes/jsu/cxzx.ts @@ -10,9 +10,23 @@ import { getPageItemAndDate } from './utils/index'; export const route: Route = { path: '/cxzx/:types?', - name: 'Unknown', + categories: ['university'], + example: '/jsu/cxzx/xkjs', + parameters: { types: '通知分类 默认为`xkjs`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '创新中心', maintainers: ['wenjia03'], handler, + description: `| 通知公告 | 学科竞赛公告 | 创新项目公告 | 竞赛新闻 | 竞赛通知 | +| -------- | ------------ | ------------ | -------- | -------- | +| tzgg | xkjs | cxtz | jsxw | jstz |`, }; async function handler(ctx) { diff --git a/lib/routes/kantarworldpanel/index.tsx b/lib/routes/kantarworldpanel/index.tsx index 76158837cf52..4634109d41c2 100644 --- a/lib/routes/kantarworldpanel/index.tsx +++ b/lib/routes/kantarworldpanel/index.tsx @@ -9,9 +9,82 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:region?/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/kantarworldpanel/cn-en/news', + parameters: { region: 'Region id, see below, Chinese Mainland English by default', category: 'Category, can be found in URL, News by default' }, + name: 'News Centre', + maintainers: ['nczitzk'], handler, + description: `| Region | id | +| ----------- | ----- | +| China Eng | cn-en | +| China 中文 | cn | +| Indonesia | id | +| Korea | kr | +| Malaysia | my | +| Philippines | ph | +| Taiwan | tw | +| Thailand | th | +| Vietnam | vn | + +<details> + <summary>More categories</summary> + +#### China Eng + +| News | Retail Snapshot | Publications | In the media | +| ---- | --------------- | -------------------- | ------------ | +| news | publications | publications/Reports | In-the-media | + +#### China 中文 + +| 新闻发布 | 零售市场快报 | 市场报告 | 媒体报道 | +| -------- | ------------ | --------------------------- | -------------- | +| news | publications | publications/China-Insights | press-releases | + +#### Indonesia + +| News | Kantar Scoop | Video Series | Podcast | Ready, Steady, Shop! | Asia Pulse | +| ---- | ----------------------------- | ----------------- | ------------ | ------------------------ | --------------- | +| News | News/Kantar-Worldpanel-Series | News/video-series | News/podcast | News/asia-shopper-series | News/Asia-Pulse | + +#### Korea + +| News | Insight Reports | In the Media | +| ---- | --------------- | -------------- | +| news | publications | press-releases | + +#### Malaysia + +| News | +| ---- | +| news | + +#### Philippines + +| Latest Insights | In the Media | Events | +| --------------- | ------------ | ------ | +| Latest-Insights | In-the-Media | events | + +#### Taiwan + +| 聚焦台灣 | WOW SPOT | 市場報告 | 媒體報導 | 活動 | +| ------------------------ | ------------ | ------------ | -------------- | ------ | +| news/spotlight-on-taiwan | news/wowspot | publications | press-releases | events | + +#### Thailand + +| News | +| ---- | +| news | + +#### Vietnam + +| Insights | FMCG Monitor | Ready, Steady, Shop! | Asia Pulse | IN THE MEDIA | +| -------- | ----------------- | ---------------------- | --------------- | ------------ | +| news | news/FMCG-Monitor | news/ready-steady-shop | news/asia-pulse | In-the-media | + +</details>`, }; async function handler(ctx) { diff --git a/lib/routes/keepass/news.ts b/lib/routes/keepass/news.ts index 834e38f6d453..ef6ca4b443d3 100644 --- a/lib/routes/keepass/news.ts +++ b/lib/routes/keepass/news.ts @@ -7,7 +7,9 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', - name: 'Unknown', + categories: ['program-update'], + example: '/keepass', + name: 'News', maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/layoffs/index.ts b/lib/routes/layoffs/index.ts index f99efff0b190..36c3c296f142 100644 --- a/lib/routes/layoffs/index.ts +++ b/lib/routes/layoffs/index.ts @@ -43,6 +43,8 @@ const getMappings = function (obj) { export const route: Route = { path: '/', + categories: ['other'], + example: '/layoffs', radar: [ { source: ['layoffs.fyi/'], diff --git a/lib/routes/leetcode/dailyquestion-cn.ts b/lib/routes/leetcode/dailyquestion-cn.ts index d7c27389a51c..7075c9281cd9 100644 --- a/lib/routes/leetcode/dailyquestion-cn.ts +++ b/lib/routes/leetcode/dailyquestion-cn.ts @@ -7,13 +7,15 @@ const host = 'https://leetcode.cn'; export const route: Route = { path: '/dailyquestion/cn', + categories: ['programming'], + example: '/leetcode/dailyquestion/cn', radar: [ { source: ['leetcode.cn/'], }, ], - name: 'Unknown', - maintainers: [], + name: '每日一题', + maintainers: ['IvanWng97'], handler, url: 'leetcode.cn/', }; diff --git a/lib/routes/leetcode/dailyquestion-en.ts b/lib/routes/leetcode/dailyquestion-en.ts index 9d6df03ae008..7f004f1a58ea 100644 --- a/lib/routes/leetcode/dailyquestion-en.ts +++ b/lib/routes/leetcode/dailyquestion-en.ts @@ -7,13 +7,15 @@ const host = 'https://leetcode.com'; export const route: Route = { path: '/dailyquestion/en', + categories: ['programming'], + example: '/leetcode/dailyquestion/en', radar: [ { source: ['leetcode.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'Daily Question', + maintainers: ['IvanWng97'], handler, url: 'leetcode.com/', }; diff --git a/lib/routes/leetcode/dailyquestion-solution-cn.ts b/lib/routes/leetcode/dailyquestion-solution-cn.ts index bde5bd107b72..28500ed2ef9b 100644 --- a/lib/routes/leetcode/dailyquestion-solution-cn.ts +++ b/lib/routes/leetcode/dailyquestion-solution-cn.ts @@ -12,13 +12,15 @@ const md = MarkdownIt({ export const route: Route = { path: '/dailyquestion/solution/cn', + categories: ['programming'], + example: '/leetcode/dailyquestion/solution/cn', radar: [ { source: ['leetcode.cn/'], }, ], - name: 'Unknown', - maintainers: [], + name: '每日一题题解', + maintainers: ['woaidouya123'], handler, url: 'leetcode.cn/', }; diff --git a/lib/routes/leetcode/dailyquestion-solution-en.ts b/lib/routes/leetcode/dailyquestion-solution-en.ts index 8da7680a46db..279da4836d3b 100644 --- a/lib/routes/leetcode/dailyquestion-solution-en.ts +++ b/lib/routes/leetcode/dailyquestion-solution-en.ts @@ -13,13 +13,15 @@ const md = MarkdownIt({ }); export const route: Route = { path: '/dailyquestion/solution/en', + categories: ['programming'], + example: '/leetcode/dailyquestion/solution/en', radar: [ { source: ['leetcode.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'Daily Question Solution', + maintainers: ['woaidouya123'], handler, url: 'leetcode.com/', }; diff --git a/lib/routes/leiphone/category.ts b/lib/routes/leiphone/category.ts new file mode 100644 index 000000000000..8481126f90a9 --- /dev/null +++ b/lib/routes/leiphone/category.ts @@ -0,0 +1,100 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; + +import utils from './utils'; + +export const route: Route = { + path: '/category/:catname', + categories: ['new-media'], + example: '/leiphone/category/industrynews', + parameters: { catname: '网站顶部分类栏目' }, + description: `- 主栏目 + +| 业界 | 人工智能 | 智能驾驶 | 数智化 | 金融科技 | 医疗科技 | 芯片 | 政企安全 | 智慧城市 | 行业云 | 工业互联网 | AIoT | +| ------------ | -------- | -------------- | --------------- | -------- | -------- | ----- | ---------- | --------- | ------------- | ------------------ | ---- | +| industrynews | ai | transportation | digitalindustry | fintech | aihealth | chips | gbsecurity | smartcity | industrycloud | IndustrialInternet | iot | + +- 子栏目 + +- 人工智能 + +| 学术 | 开发者 | +| -------- | -------- | +| academic | yanxishe | + +- 数智化 + +| 零售数智化 | 金融数智化 | 工业数智化 | 医疗数智化 | 城市数智化 | +| ---------- | ---------- | ---------- | ---------- | ----------- | +| redigital | findigital | mandigital | medigital | citydigital | + +- 金融科技 + +| 科技巨头 | 银行 AI | 金融云 | 风控与安全 | +| -------- | ------- | ------------ | ------------ | +| BigTech | bank | FinanceCloud | DataSecurity | + +- 医疗科技 + +| 医疗 AI | 投融资 | 医疗器械 | 互联网医疗 | 生物医药 | 健康险 | +| -------- | ------ | -------- | ---------------- | ------------ | ------------ | +| healthai | touzi | qixie | hulianwangyiliao | shengwuyiyao | jiankangxian | + +- 芯片 + +| 材料设备 | 芯片设计 | 晶圆代工 | 封装测试 | +| --------- | ---------- | ------------- | --------- | +| materials | chipdesign | manufacturing | packaging | + +- 智慧城市 + +| 智慧安防 | 智慧教育 | 智慧交通 | 智慧社区 | 智慧零售 | 智慧政务 | 智慧地产 | +| ------------- | -------------- | ------------------- | -------------- | -------------- | --------------- | -------- | +| smartsecurity | smarteducation | smarttransportation | smartcommunity | smartretailing | smartgovernment | proptech | + +- 工业互联网 + +| 工业软件 | 工业安全 | 5G 工业互联网 | 工业转型实践 | +| ---------- | -------- | ------------- | ------------ | +| gysoftware | gysafety | 5ggy | gypratice | + +- AIoT + +| 物联网 | 智能硬件 | 机器人 | 智能家居 | +| ------ | -------- | ------ | --------- | +| 5G | arvr | robot | smarthome |`, + radar: [ + { + source: ['leiphone.com/category/:catname'], + target: '/category/:catname', + }, + ], + name: '栏目', + maintainers: ['vlcheng'], + handler, + url: 'leiphone.com/', +}; + +export async function handler(ctx) { + const catname = ctx.req.param('catname'); + const rootUrl = 'https://www.leiphone.com'; + const url = catname ? `${rootUrl}/category/${catname}` : rootUrl; + const res = await got.get(url); + const $ = load(res.data); + + const list = $('.word > h3 > a') + .slice(0, 10) + .toArray() + .map((e) => $(e).attr('href')); + const items = await utils.ProcessFeed(list, cache); + + return { + title: `雷峰网${catname ? ` ${catname}` : ''}`, + description: '雷峰网 - 读懂智能&未来', + link: url, + item: items, + }; +} diff --git a/lib/routes/leiphone/index.ts b/lib/routes/leiphone/index.ts index 07c0262f20f6..6b1e2ed74f13 100644 --- a/lib/routes/leiphone/index.ts +++ b/lib/routes/leiphone/index.ts @@ -1,43 +1,19 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; -import utils from './utils'; +import { handler } from './category'; export const route: Route = { - path: '/:do?/:keyword?', + path: '/', + categories: ['new-media'], + example: '/leiphone', radar: [ { source: ['leiphone.com/'], target: '', }, ], - name: 'Unknown', - maintainers: [], + name: '最新文章', + maintainers: ['vlcheng'], handler, url: 'leiphone.com/', }; - -async function handler(ctx) { - const todo = ctx.req.param('do') ?? ''; - const keyword = ctx.req.param('keyword') ?? ''; - const rootUrl = 'https://www.leiphone.com'; - const url = `${rootUrl}/${todo}/${keyword}`; - const res = await got.get(url); - const $ = load(res.data); - - const list = $('.word > h3 > a') - .slice(0, 10) - .toArray() - .map((e) => $(e).attr('href')); - const items = await utils.ProcessFeed(list, cache); - - return { - title: `雷峰网${todo === 'category' ? ` ${keyword}` : ''}`, - description: '雷峰网 - 读懂智能&未来', - link: url, - item: items, - }; -} diff --git a/lib/routes/leiphone/newsflash.ts b/lib/routes/leiphone/newsflash.ts index 0b8467e391d6..16967e56ed44 100644 --- a/lib/routes/leiphone/newsflash.ts +++ b/lib/routes/leiphone/newsflash.ts @@ -21,10 +21,11 @@ export const route: Route = { radar: [ { source: ['leiphone.com/'], + target: '/newsflash', }, ], name: '业界资讯', - maintainers: [], + maintainers: ['vlcheng'], handler, url: 'leiphone.com/', }; diff --git a/lib/routes/lfsyd/tag.ts b/lib/routes/lfsyd/tag.ts index 05ba52a51de6..1ced3fe46cec 100644 --- a/lib/routes/lfsyd/tag.ts +++ b/lib/routes/lfsyd/tag.ts @@ -7,15 +7,26 @@ import { ProcessFeed, ProcessForm } from './utils'; export const route: Route = { path: '/tag/:tagId?', + categories: ['game'], + example: '/lfsyd/tag/17', + parameters: { tagId: '订阅分区类型' }, + description: `| 炉石传说 | 万智牌 | 游戏王 | 昆特牌 | 影之诗 | 符文之地传奇 | 阴阳师百闻牌 | +| :------: | :----: | :----: | :----: | :----: | :----------: | :----------: | +| 17 | 18 | 16 | 19 | 20 | 329 | 221 | + +| 英雄联盟 | 电子游戏 | 桌面游戏 | 卡牌游戏 | 玩家杂谈 | 二次元 | +| :------: | :------: | :------: | :------: | :------: | :----: | +| 112 | 389 | 24 | 102 | 23 | 117 |`, radar: [ { source: ['mob.iyingdi.com/fine/:tagId'], target: '/tag/:tagId', }, ], - name: 'Unknown', + name: '分区', maintainers: ['auto-bot-ty'], handler, + url: 'www.iyingdi.com/', }; async function handler(ctx) { diff --git a/lib/routes/lfsyd/user.ts b/lib/routes/lfsyd/user.ts index 16a335a0effa..99f29d3b250d 100644 --- a/lib/routes/lfsyd/user.ts +++ b/lib/routes/lfsyd/user.ts @@ -7,15 +7,22 @@ import { ProcessFeed, ProcessForm } from './utils'; export const route: Route = { path: '/user/:id?', + categories: ['game'], + example: '/lfsyd/user/55547', + parameters: { id: '用户 id' }, + description: `可以在用户主页的 URL 中找到 + +Example:\`https://www.iyingdi.com/tz/people/55547\` ,id 是 \`55547\``, radar: [ { source: ['www.iyingdi.com/tz/people/:id', 'www.iyingdi.com/tz/people/:id/*'], target: '/user/:id', }, ], - name: 'Unknown', + name: '用户的帖子', maintainers: ['auto-bot-ty'], handler, + url: 'www.iyingdi.com/', }; async function handler(ctx) { diff --git a/lib/routes/lifeweek/channel.ts b/lib/routes/lifeweek/channel.ts index fec9ddf07aab..459ebc0d87ba 100644 --- a/lib/routes/lifeweek/channel.ts +++ b/lib/routes/lifeweek/channel.ts @@ -10,14 +10,22 @@ const articleRootUrl = 'https://www.lifeweek.com.cn/article'; export const route: Route = { path: '/channel/:id', + categories: ['traditional-media'], + example: '/lifeweek/channel/9', + parameters: { id: '栏目 ID' }, + description: `提取文章全文,获得更好的阅读体验。支持所有频道,频道名称见 [杂志栏目](https://www.lifeweek.com.cn/classify?type=2)。例如 [调查栏目](https://www.lifeweek.com.cn/column/9) URL 最后的数字为栏目 ID + +| 调查 | 热点 | 人物 | 社会 | 经济 | 文化 | +| ---- | ---- | ---- | ---- | ---- | ---- | +| 9 | 6 | 10 | 2 | 3 | 4 |`, radar: [ { source: ['lifeweek.com.cn/column/:channel'], target: '/channel/:channel', }, ], - name: 'Unknown', - maintainers: [], + name: '栏目', + maintainers: ['changren-wcr'], handler, }; diff --git a/lib/routes/lifeweek/tag.ts b/lib/routes/lifeweek/tag.ts index fc18896c28ed..886a0025d90c 100644 --- a/lib/routes/lifeweek/tag.ts +++ b/lib/routes/lifeweek/tag.ts @@ -10,14 +10,22 @@ const articleRootUrl = 'https://www.lifeweek.com.cn/article'; export const route: Route = { path: '/tag/:id', + categories: ['traditional-media'], + example: '/lifeweek/tag/122', + parameters: { id: '标签 ID' }, + description: `提取文章全文,获得更好的阅读体验。支持所有标签,标签名称见 [全部标签](https://www.lifeweek.com.cn/classify?type=1)。例如 [社会调查标签](https://www.lifeweek.com.cn/articleList/122) URL 最后的数字为标签 ID + +| 社会调查 | 社会 | 经济 | 理财 | 热点 | +| -------- | ---- | ---- | ---- | ---- | +| 122 | 21 | 73 | 74 | 123 |`, radar: [ { source: ['lifeweek.com.cn/articleList/:tag'], target: '/tag/:tag', }, ], - name: 'Unknown', - maintainers: [], + name: '标签', + maintainers: ['changren-wcr'], handler, }; diff --git a/lib/routes/lightnovel/light-novel.ts b/lib/routes/lightnovel/light-novel.ts index ea154ee98457..9c23fece2042 100644 --- a/lib/routes/lightnovel/light-novel.ts +++ b/lib/routes/lightnovel/light-novel.ts @@ -8,13 +8,19 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:keywords/:security_key?', + categories: ['anime'], + example: '/lightnovel/歡迎來到實力至上主義的教室/3cfc2dc63f3575ee42e12823188ad1b5:1709125:0', + parameters: { + keywords: '关键字,可以模糊匹配,但最好精确匹配', + security_key: 'cookie,由于文章有防爬,所以必须携带cookie请求。route中的cookie优先级高于环境变量cookie,取token中的security_key值', + }, radar: [ { source: ['lightNovel.us/'], target: '/:keywords/:security_key', }, ], - name: 'Unknown', + name: '文章更新阅读', maintainers: ['nightmare-mio'], handler, url: 'lightNovel.us/', diff --git a/lib/routes/literotica/category.ts b/lib/routes/literotica/category.ts index dc4ade89eeeb..6ced9ddee362 100644 --- a/lib/routes/literotica/category.ts +++ b/lib/routes/literotica/category.ts @@ -7,12 +7,17 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/category/:category', + categories: ['reading'], + example: '/literotica/category/anal-sex-stories', + parameters: { + category: 'Category, can be found in URL', + }, radar: [ { source: ['literotica.com/c/:category', 'literotica.com/'], }, ], - name: 'Unknown', + name: 'Category', maintainers: ['nczitzk'], handler, features: { diff --git a/lib/routes/liulinblog/index.ts b/lib/routes/liulinblog/index.ts index 440e6f2daf08..3b80463cc28f 100644 --- a/lib/routes/liulinblog/index.ts +++ b/lib/routes/liulinblog/index.ts @@ -2,22 +2,55 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/:params{.+}?', - name: 'Unknown', - maintainers: [], + path: '/:channel?', + categories: ['new-media'], + example: '/liulinblog', + parameters: { channel: '频道 id,可在对应频道页 URL 中找到,见下表,默认为最新' }, + radar: [ + { + source: ['liulinblog.com/:channel', 'liulinblog.com/'], + target: '/:channel', + }, + ], + name: '频道', + maintainers: ['nczitzk'], handler, + description: `| 最新 | 60 秒读懂世界 | 精品资源 | 视频资源 | 音频资源 | +| ---- | ------------- | -------- | -------- | -------- | +| | kuaixun | ziyuan | video | yinpin | + +| 绝版资源 | 实用文档 | PPT 素材 | 后期素材 | 技能教程 | +| -------- | -------- | --------- | -------- | --------- | +| jueban | wendang | ppt-sucai | sucai | jiaocheng | + +| 创业副业 | 单机游戏 | 冒险解谜 | 竞技格斗 | 赛车竞技 | +| -------- | -------- | -------- | ----------- | -------- | +| money | game | mxjm | jingjigedou | saiche | + +| 模拟经营 | 角色扮演 | 飞行游戏 | 塔防策略 | 射击游戏 | +| -------- | -------- | -------- | -------- | -------- | +| moni | jiaose | feixing | tafang | sheji | + +| 恐怖冒险 | 策略生存 | 动作冒险 | 电商运营 | 互联网早报 | +| -------- | -------- | -------- | --------- | ---------- | +| kongbu | celve | dongzuo | dianshang | internet | + +| 站长圈 | 自媒体运营 | 短视频 | +| ------ | ---------- | ----------- | +| seo | zimeiti | duan-shipin |`, }; -async function handler(ctx) { - const params = ctx.req.param('params'); +export async function handler(ctx) { + const subPath = getSubPath(ctx); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://www.liulinblog.com'; - const currentUrl = params ? new URL(params, rootUrl).href : rootUrl; + const currentUrl = subPath === '/' ? rootUrl : new URL(subPath, rootUrl).href; const { data: response } = await got(currentUrl); @@ -94,7 +127,7 @@ async function handler(ctx) { return { item: items, - title: `${title} - ${params ? $('h1.term-title').text().split('搜索到', 1)[0] : '最新'}`, + title: `${title} - ${subPath === '/' ? '最新' : $('h1.term-title').text().split('搜索到', 1)[0]}`, link: currentUrl, description: $('meta[name="description"]').prop('content'), language: 'zh-cn', diff --git a/lib/routes/liulinblog/itnews.ts b/lib/routes/liulinblog/itnews.ts index 649e4adde6e2..8f9953e0d4ae 100644 --- a/lib/routes/liulinblog/itnews.ts +++ b/lib/routes/liulinblog/itnews.ts @@ -2,9 +2,15 @@ import type { Route } from '@/types'; export const route: Route = { path: '/itnews/:channel', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/liulinblog/itnews/seo', + parameters: { channel: '频道,见下表' }, + name: '网络营销', + maintainers: ['Fatpandac', 'nczitzk'], handler, + description: `| 网络营销 | 电商运营 | 互联网早报 | 站长圈 | +| -------- | --------- | ---------- | ------ | +| | dianshang | internet | seo |`, }; function handler(ctx) { diff --git a/lib/routes/liulinblog/kuaixun.ts b/lib/routes/liulinblog/kuaixun.ts new file mode 100644 index 000000000000..aaa8e146536b --- /dev/null +++ b/lib/routes/liulinblog/kuaixun.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/kuaixun', + categories: ['new-media'], + example: '/liulinblog/kuaixun', + radar: [ + { + source: ['liulinblog.com/kuaixun', 'liulinblog.com/'], + target: '/kuaixun', + }, + ], + name: '60 秒读懂世界', + maintainers: ['Fatpandac', 'nczitzk'], + handler, +}; diff --git a/lib/routes/liulinblog/search.ts b/lib/routes/liulinblog/search.ts new file mode 100644 index 000000000000..c3dc7333bafc --- /dev/null +++ b/lib/routes/liulinblog/search.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/search/:keyword', + categories: ['new-media'], + example: '/liulinblog/search/单机游戏', + parameters: { keyword: '关键字' }, + radar: [ + { + source: ['liulinblog.com/search/:keyword', 'liulinblog.com/'], + target: '/search/:keyword', + }, + ], + name: '搜索', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/liulinblog/series.ts b/lib/routes/liulinblog/series.ts new file mode 100644 index 000000000000..04bb582e4e19 --- /dev/null +++ b/lib/routes/liulinblog/series.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/series/:id', + categories: ['new-media'], + example: '/liulinblog/series/xunlei', + parameters: { id: '专题 id,可在对应标签页 URL 中找到,见下表' }, + radar: [ + { + source: ['liulinblog.com/series/:id', 'liulinblog.com/'], + target: '/series/:id', + }, + ], + name: '专题', + maintainers: ['nczitzk'], + handler, + description: `| 【免费速存】迅雷资源合集 | 直播带货教程 | 电商培训课程 | 拼多多运营培训 | 小红书运营 | 抖音运营 | 闲鱼运营 | 短视频运营 | +| ------------------------ | ------------ | --------------- | -------------- | ----------- | ------------- | ------------- | ----------------- | +| xunlei | zhibodaihuo | dianshangpeixun | pinduoduo | xiaohongshu | douyinyunying | xianyuyunying | duanshipinyunying |`, +}; diff --git a/lib/routes/liulinblog/tag.ts b/lib/routes/liulinblog/tag.ts new file mode 100644 index 000000000000..105f06b7e43c --- /dev/null +++ b/lib/routes/liulinblog/tag.ts @@ -0,0 +1,42 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:id', + categories: ['new-media'], + example: '/liulinblog/tag/qukuailian', + parameters: { id: '标签 id,可在对应标签页 URL 中找到,见下表' }, + radar: [ + { + source: ['liulinblog.com/tag/:id', 'liulinblog.com/'], + target: '/tag/:id', + }, + ], + name: '标签', + maintainers: ['nczitzk'], + handler, + description: `| 区块链 | 小红书 | 小说项目 | 微信公众号 | 微信营销 | +| ---------- | ----------- | -------- | ---------- | -------- | +| qukuailian | xiaohongshu | xiaoshuo | 微信公众号 | we-chat | + +| 抖音 | 抖音直播 | 拼多多 | 支付宝 | 教育 | +| ---- | -------- | --------- | ------ | ---- | +| 抖音 | 抖音直播 | pinduoduo | alipay | 教育 | + +| chrome 插件 | galgame 汉化游戏 | honeyselect 汉化游戏 | PSD 笔刷素材 | ps 插件 | +| ----------- | ---------------- | -------------------- | ------------ | ---------- | +| chrome 插件 | galgame | honey-select | psd-bishua | ps-chajian | + +| vip 视频 | windows 实用技巧 | 下载软件 | 丝袜玉足 | 免费字体下载 | +| ---------- | ---------------- | -------- | -------- | ------------ | +| vip-shipin | computer | download | siwa | ziti | + +| 二战游戏下载 | 冒险解谜游戏 | 动作游戏下载 | 安卓游戏 | 策略游戏 | +| ------------ | ------------ | ------------ | ------------ | ---------- | +| war-games | 冒险解谜游戏 | 动作游戏下载 | android-game | game-celve | + +| Pr 插件 | Python | seo 优化 | VLOG | wordpress | word 技巧 | +| ------- | ------ | -------- | ---- | --------- | --------- | +| pr 插件 | python | seo | vlog | wordpress | word |`, +}; diff --git a/lib/routes/lkong/forum.ts b/lib/routes/lkong/forum.ts index e41245903637..58c63f156f16 100644 --- a/lib/routes/lkong/forum.ts +++ b/lib/routes/lkong/forum.ts @@ -8,12 +8,18 @@ import { renderContent } from './templates/content'; export const route: Route = { path: '/forum/:id?/:digest?', + categories: ['bbs'], + example: '/lkong/forum/60', + parameters: { + id: '分区 id, 可在分区的URL里找到', + digest: '默认获取全部主题,任意值则只获取精华主题', + }, radar: [ { source: ['lkong.com/forum/:id', 'lkong.com/'], }, ], - name: 'Unknown', + name: '分区', maintainers: ['nczitzk', 'ma6254'], handler, }; diff --git a/lib/routes/lkong/thread.tsx b/lib/routes/lkong/thread.tsx index f85d6e255f96..127ee186fef8 100644 --- a/lib/routes/lkong/thread.tsx +++ b/lib/routes/lkong/thread.tsx @@ -10,12 +10,17 @@ import { renderContent } from './templates/content'; export const route: Route = { path: '/thread/:id', + categories: ['bbs'], + example: '/lkong/thread/3100275', + parameters: { + id: '帖子 id, 可在帖子的URL里找到', + }, radar: [ { source: ['lkong.com/thread/:id', 'lkong.com/'], }, ], - name: 'Unknown', + name: '帖子', maintainers: ['nczitzk', 'ma6254'], handler, }; diff --git a/lib/routes/logclub/columnist.ts b/lib/routes/logclub/columnist.ts new file mode 100644 index 000000000000..8fa009b18132 --- /dev/null +++ b/lib/routes/logclub/columnist.ts @@ -0,0 +1,106 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/columnist/articleList/:id?', + categories: ['new-media'], + example: '/logclub/columnist/articleList/27', + parameters: { id: '专家 id,见下表,可在对应企业页 URL 中找到' }, + radar: [ + { + source: ['logclub.com/columnist/articleList/:id'], + target: '/columnist/articleList/:id', + }, + ], + name: '专家说', + maintainers: ['nczitzk'], + handler, + description: `#### 精选专家 + +| 潘永刚 | Tracy | 唐隆基 | 褚建新 | +| ------ | ----- | ------ | ------ | +| 27 | 157 | 91 | 9749 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/experts) 查看更多专家 + +#### 资深作者 + +| 物流麻将胡 | 小周伯通 | 郭嘉 | 周艳青 | +| ---------- | -------- | ---- | ------ | +| 10 | 19 | 7 | 559 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/author) 查看更多专家 + +#### 综合物流 + +| 韩雪峰 | 李赛赛 | 陈晓曦 | 李长宏 | +| ------ | ------ | ------ | ------ | +| 41 | 12378 | 1495 | 110 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/integrated_logistics) 查看更多专家 + +#### 数字化 + +| 秦愉 | 冯雷 | 卢立新 | 段琰 | +| ---- | ---- | ------ | ---- | +| 160 | 147 | 95 | 284 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/digitization) 查看更多专家 + +#### 智能化 + +| 曾志宏 | 亦橙 | 马荣 | 陈晓春 | +| ------ | ---- | ---- | ------ | +| 34 | 201 | 130 | 123 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/intellectualization) 查看更多专家 + +#### 快运 + +| 王坚 | 王拥军 | 靖晟 | 廖文明 | +| ---- | ------ | ---- | ------ | +| 172 | 252 | 84 | 50 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/express_transportation) 查看更多专家 + +#### 合同物流 + +| 非红 | 王鹏飞 | 周海 | 王伟 | +| ---- | ------ | ---- | ---- | +| 40 | 2274 | 168 | 158 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/contract_logistics) 查看更多专家 + +#### 供应链 + +| 黄尧笛 | 卓弘毅 | 胡珉 | 雷文军 Jason | +| ------ | ------ | ---- | ------------ | +| 26 | 35 | 188 | 303 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/supply_chain) 查看更多专家 + +#### 快递 + +| 致快递 | 中通之声 | 科技中通 | 明兴 | +| ------ | -------- | -------- | ---- | +| 9633 | 618 | 385 | 265 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/express) 查看更多专家 + +#### 城配 + +| 张春鑫 (荡漾哥) | 梁佳 | 赵波 | 王行广 | +| --------------- | ---- | ---- | ------ | +| 1527 | 3374 | 49 | 75 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/urban_distribution) 查看更多专家 + +#### 仓储 + +| 叶剑 | 木棉 | 陈艺 | 冯银川 | +| ---- | ---- | ---- | ------ | +| 1881 | 59 | 1637 | 215 | + +前往 [更多](https://www.logclub.com/columnist/authorMore/storage) 查看更多专家`, +}; diff --git a/lib/routes/logclub/company.ts b/lib/routes/logclub/company.ts new file mode 100644 index 000000000000..4a618f98e3ec --- /dev/null +++ b/lib/routes/logclub/company.ts @@ -0,0 +1,145 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/company/:id', + categories: ['new-media'], + example: '/logclub/company/14', + parameters: { id: '企业 id,见下表,可在对应企业页 URL 中找到' }, + features: { + antiCrawler: true, + }, + radar: [ + { + source: ['logclub.com/company/:id'], + target: '/company/:id', + }, + ], + name: '大企业', + maintainers: ['nczitzk'], + handler, + description: `#### 明星企业 + +| 顺丰 | 菜鸟 | 京东物流 | 德邦快递 / 德邦股份 | +| ---- | ---- | -------- | ------------------- | +| 14 | 56 | 453 | 145 | + +| 百世集团 | 中国物流集团 | 极兔速递 | 中通快递 | +| -------- | ------------ | -------- | -------- | +| 116 | 6807 | 3246 | 132 | + +前往 [更多](https://www.logclub.com/columnist/more/star) 查看更多企业 + +#### 综合物流 / 供应链企业 + +| 锐特信息 | 中国物流与采购网 | 则一 | 普路通 | +| -------- | ---------------- | ---- | ------ | +| 716 | 525 | 524 | 464 | + +| 安得智联 | 集保中国 | 上汽安吉物流 | 苏宁物流 | +| -------- | -------- | ------------ | -------- | +| 215 | 147 | 15 | 12 | + +| 准时达 | 深国际 | 益邦控股 | 卓志跨境供应链 | +| ------ | ------ | -------- | -------------- | +| 158 | 167 | 758 | 130 | + +| 日日顺 | 传化智联 | CJ 荣庆物流 | 江苏飞力达 | +| ------ | -------- | ----------- | ---------- | +| 179 | 166 | 770 | 548 | + +前往 [更多](https://www.logclub.com/columnist/more/integrated_logistics) 查看更多企业 + +#### 快递 / 快运企业 + +| 盛丰物流 | 跨越速运 | 顺心捷达 | 中国邮政 | +| -------- | -------- | -------- | -------- | +| 819 | 372 | 327 | 134 | + +| 韵达 | 申通快递 | 圆通 | 壹米滴答集团 | +| ---- | -------- | ---- | ------------ | +| 113 | 137 | 143 | 97 | + +| 安能物流 | 联邦快递 FedEx | UPS | DHL | +| -------- | -------------- | --- | --- | +| 151 | 104 | 388 | 219 | + +| 优速快递 | 中铁快运 | 德坤物流 | 商桥物流 | +| -------- | -------- | -------- | -------- | +| 1243 | 1015 | 2254 | 99 | + +前往 [更多](https://www.logclub.com/columnist/more/express) 查看更多企业 + +#### 系统 / 智能平台 / 智能硬件 / 设施企业 + +| 甲子光年 | 炬星科技 | 斑马智行 | 智行者 | +| -------- | -------- | -------- | ------ | +| 700 | 665 | 540 | 510 | + +| 纵行科技 | 行深智能 | 地上铁 | 图森未来 | +| -------- | -------- | ------ | -------- | +| 509 | 508 | 500 | 445 | + +| oTMS 运输管理云平台 | 聚龄供应链 | 博科资讯 | 富勒 FLUX | +| ------------------- | ---------- | -------- | --------- | +| 53 | 5806 | 6870 | 122 | + +| 科箭软件 | 通天晓软件 | 嬴彻科技 | 货车宝 | +| -------- | ---------- | -------- | ------ | +| 180 | 503 | 462 | 129 | + +前往 [更多](https://www.logclub.com/columnist/more/intellectualization) 查看更多企业 + +#### 货运 / 配送平台 / 仓储 / 地产企业 + +| 卡力互联 | 想乐送 | 宝湾物流 | 卡车之家 | +| -------- | ------ | -------- | -------- | +| 1660 | 1558 | 1031 | 365 | + +| 罗宾升 | 好多车 | 饿了么 | 闪送 | +| ------ | ------ | ------ | ---- | +| 308 | 269 | 172 | 80 | + +| 宏昌物流园 | 路歌互联网物流平台 | 福佑卡车 | 普洛斯 | +| ---------- | ------------------ | -------- | ------ | +| 6816 | 182 | 18 | 150 | + +| 菜鸟速递 | 达达集团 | 满帮集团 | 货拉拉 | +| -------- | -------- | -------- | ------ | +| 1972 | 748 | 193 | 79 | + +前往 [更多](https://www.logclub.com/columnist/more/freight_transport) 查看更多企业 + +#### 证券交运 / 咨询机构 + +| 罗戈网 | 物流沙龙 | 罗戈研究 | 招商证券 | +| ------ | -------- | -------- | -------- | +| 17 | 21 | 26 | 51 | + +| 兴业证券 | 环球物流咨询规划 | 华创证券 | 艾瑞咨询 | +| -------- | ---------------- | -------- | -------- | +| 161 | 29 | 1287 | 58 | + +| 安信证券 | 天风证券 | 方正证券 | 中信证券 | +| -------- | -------- | -------- | -------- | +| 1295 | 1136 | 1169 | 469 | + +| 申万宏源证券 | 国海证券 | 华泰证券 | 东方证券 | +| ------------ | -------- | -------- | -------- | +| 1296 | 6381 | 2119 | 1772 | + +前往 [更多](https://www.logclub.com/columnist/more/securities_delivery) 查看更多企业 + +#### 资本 + +| 源码资本 | 华兴资本 | IDG 资本 | 元赋资本 | +| -------- | -------- | -------- | -------- | +| 1037 | 931 | 787 | 393 | + +| 钟鼎资本 | 红杉资本 | +| -------- | -------- | +| 159 | 5265 | + +前往 [更多](https://www.logclub.com/columnist/more/capital) 查看更多企业`, +}; diff --git a/lib/routes/logclub/index.ts b/lib/routes/logclub/index.ts index 708a46a93f0e..19361bc22ea7 100644 --- a/lib/routes/logclub/index.ts +++ b/lib/routes/logclub/index.ts @@ -2,24 +2,44 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '/:category{.+}?', - name: 'Unknown', - maintainers: [], + path: '/news/:id?', + categories: ['new-media'], + example: '/logclub/news', + parameters: { id: '资讯 id,见下表,可在对应资讯页 URL 中找到,默认为全部' }, + radar: [ + { + source: ['logclub.com/news'], + target: '/news', + }, + { + source: ['logclub.com/news/:id'], + target: '/news/:id', + }, + ], + name: '资讯', + maintainers: ['nczitzk'], handler, + description: `| 供应链 | 快递 | 快运 / 运输 | 仓储 / 地产 | 物流综合 | 国际与跨境物流 | 科技创新 | +| ------ | ---- | ----------- | ----------- | -------- | -------------- | -------- | +| 10-16 | 11 | 30 | 9 | 32 | 114 | 107 | + +| 绿色供应链 | 低碳物流 | 碳中和碳达峰 | +| ---------- | -------- | ------------ | +| 213 | 214 | 215 |`, }; -async function handler(ctx) { - const { category = 'news' } = ctx.req.param(); +export async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 11; const rootUrl = 'https://www.logclub.com'; - const currentUrl = new URL(category, rootUrl).href; + const currentUrl = new URL(getSubPath(ctx), rootUrl).href; const { data: response } = await got(currentUrl); diff --git a/lib/routes/logclub/original.ts b/lib/routes/logclub/original.ts new file mode 100644 index 000000000000..1d5bea157133 --- /dev/null +++ b/lib/routes/logclub/original.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/original', + categories: ['new-media'], + example: '/logclub/original', + radar: [ + { + source: ['logclub.com/original'], + target: '/original', + }, + ], + name: '原创', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/logclub/recruit.ts b/lib/routes/logclub/recruit.ts new file mode 100644 index 000000000000..dcfcc2eac550 --- /dev/null +++ b/lib/routes/logclub/recruit.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/recruit', + categories: ['new-media'], + example: '/logclub/recruit', + radar: [ + { + source: ['logclub.com/recruit'], + target: '/recruit', + }, + ], + name: '招聘', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/logclub/report.ts b/lib/routes/logclub/report.ts index 6e9d3c97183d..e293364e4ce8 100644 --- a/lib/routes/logclub/report.ts +++ b/lib/routes/logclub/report.ts @@ -9,7 +9,7 @@ import timezone from '@/utils/timezone'; import { renderDescription } from './templates/description'; export const route: Route = { - path: ['/lc_report/:id?', '/report/:id?'], + path: '/lc_report/:id?', categories: ['new-media'], example: '/logclub/lc_report', parameters: { id: '报告 id,见下表,默认为罗戈研究出品' }, @@ -21,6 +21,27 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['logclub.com/lc_report'], + target: '/lc_report', + }, + { + title: '报告 - 罗戈研究出品', + source: ['logclub.com/lc_report'], + target: '/lc_report/Report', + }, + { + title: '报告 - 物流报告', + source: ['logclub.com/lc_report'], + target: '/lc_report/IndustryReport', + }, + { + title: '报告 - 绿色双碳报告', + source: ['logclub.com/lc_report'], + target: '/lc_report/GreenDualCarbonReport', + }, + ], name: '报告', maintainers: ['nczitzk'], handler, diff --git a/lib/routes/logclub/tender.ts b/lib/routes/logclub/tender.ts new file mode 100644 index 000000000000..ccc87fbbbae8 --- /dev/null +++ b/lib/routes/logclub/tender.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tender', + categories: ['new-media'], + example: '/logclub/tender', + radar: [ + { + source: ['logclub.com/tender'], + target: '/tender', + }, + ], + name: '招投标', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/logonews/category.ts b/lib/routes/logonews/category.ts new file mode 100644 index 000000000000..4987da8d0618 --- /dev/null +++ b/lib/routes/logonews/category.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/category/:category/:type', + categories: ['design'], + example: '/logonews/category/news/newsletter', + parameters: { category: '分类,可在对应分类页 URL 中找到', type: '类型,可在对应分类页 URL 中找到' }, + radar: [ + { + source: ['logonews.cn/category/:category/:type?'], + target: '/category/:category/:type?', + }, + ], + name: '文章分类', + maintainers: ['nczitzk'], + handler, + url: 'logonews.cn/', + description: + '如 [简讯 - 标志情报局](https://www.logonews.cn/category/news/newsletter) 的 URL 为 `https://www.logonews.cn/category/news/newsletter`,可得路由为 [`/logonews/category/news/newsletter`](https://rsshub.app/logonews/category/news/newsletter)。', +}; diff --git a/lib/routes/logonews/index.tsx b/lib/routes/logonews/index.tsx index 5e228c0d37af..879c12122d87 100644 --- a/lib/routes/logonews/index.tsx +++ b/lib/routes/logonews/index.tsx @@ -9,25 +9,27 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: ['/work/tags/:tag', '/tag/:tag', '*'], + path: '/', + categories: ['design'], + example: '/logonews', radar: [ { - source: ['logonews.cn/work/tags/:tag'], + source: ['logonews.cn/'], + target: '/', }, ], - name: 'Unknown', + name: '首页', maintainers: ['nczitzk'], handler, url: 'logonews.cn/', - description: '如 [中国 - 标志情报局](https://www.logonews.cn/tag/china) 的 URL 为 `https://www.logonews.cn/tag/china`,可得路由为 [`/logonews/tag/china`](https://rsshub.app/logonews/tag/china)。', }; -async function handler(ctx) { - const params = getSubPath(ctx); - const isWork = params.indexOf('/work') === 0; +export async function handler(ctx) { + const subPath = getSubPath(ctx); + const isWork = subPath.startsWith('/work'); const rootUrl = 'https://www.logonews.cn'; - const currentUrl = `${rootUrl}${params === '/' ? '' : params}`; + const currentUrl = subPath === '/' ? rootUrl : `${rootUrl}${subPath}`; const response = await got({ method: 'get', diff --git a/lib/routes/logonews/tag.ts b/lib/routes/logonews/tag.ts new file mode 100644 index 000000000000..aa8c62d23e96 --- /dev/null +++ b/lib/routes/logonews/tag.ts @@ -0,0 +1,21 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:tag', + categories: ['design'], + example: '/logonews/tag/china', + parameters: { tag: '标签,可在对应标签页 URL 中找到' }, + radar: [ + { + source: ['logonews.cn/tag/:tag'], + target: '/tag/:tag', + }, + ], + name: '文章标签', + maintainers: ['nczitzk'], + handler, + url: 'logonews.cn/', + description: '如 [中国 - 标志情报局](https://www.logonews.cn/tag/china) 的 URL 为 `https://www.logonews.cn/tag/china`,可得路由为 [`/logonews/tag/china`](https://rsshub.app/logonews/tag/china)。', +}; diff --git a/lib/routes/logonews/work-category.ts b/lib/routes/logonews/work-category.ts new file mode 100644 index 000000000000..6d3b98ac6e0c --- /dev/null +++ b/lib/routes/logonews/work-category.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/work/categorys/:category', + categories: ['design'], + example: '/logonews/work/categorys/hotel-catering', + parameters: { category: '分类,可在对应分类页 URL 中找到' }, + radar: [ + { + source: ['logonews.cn/work/categorys/:category'], + target: '/work/categorys/:category', + }, + ], + name: '作品分类', + maintainers: ['nczitzk'], + handler, + url: 'logonews.cn/', + description: + '如 [LOGO 作品分类:酒店餐饮 - 标志情报局](https://www.logonews.cn/work/categorys/hotel-catering) 的 URL 为 `https://www.logonews.cn/work/categorys/hotel-catering`,可得路由为 [`/logonews/work/categorys/hotel-catering`](https://rsshub.app/logonews/work/categorys/hotel-catering)。', +}; diff --git a/lib/routes/logonews/work-tag.ts b/lib/routes/logonews/work-tag.ts new file mode 100644 index 000000000000..89deb9cea46c --- /dev/null +++ b/lib/routes/logonews/work-tag.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/work/tags/:tag?', + categories: ['design'], + example: '/logonews/work/tags/旅游', + parameters: { tag: '标签,可在对应标签页 URL 中找到' }, + radar: [ + { + source: ['logonews.cn/work/tags/:tag'], + target: '/work/tags/:tag', + }, + ], + name: '作品标签', + maintainers: ['nczitzk'], + handler, + url: 'logonews.cn/', + description: + '如 [LOGO 标签:旅游 - 标志情报局](https://www.logonews.cn/work/tags/旅游) 的 URL 为 [https://www.logonews.cn/work/tags/ 旅游](https://www.logonews.cn/work/tags/旅游),可得路由为 [`/logonews/work/tags/旅游`](https://rsshub.app/logonews/work/tags/旅游)。', +}; diff --git a/lib/routes/logonews/work.ts b/lib/routes/logonews/work.ts new file mode 100644 index 000000000000..7af9fb85e617 --- /dev/null +++ b/lib/routes/logonews/work.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/work', + categories: ['design'], + example: '/logonews/work', + radar: [ + { + source: ['logonews.cn/work'], + target: '/work', + }, + ], + name: '作品', + maintainers: ['nczitzk'], + handler, + url: 'logonews.cn/', +}; diff --git a/lib/routes/m4/index.ts b/lib/routes/m4/index.ts index 9dc97233b526..4f42b5686503 100644 --- a/lib/routes/m4/index.ts +++ b/lib/routes/m4/index.ts @@ -1,31 +1,45 @@ import { load } from 'cheerio'; -import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '/:id?/:category{.+}?', - name: 'Unknown', - maintainers: [], + path: '/news/:category?', + categories: ['new-media'], + example: '/m4/news/china', + parameters: { category: '分类,见下表,默认为国内新闻' }, + description: `| 分类 | ID | +| ------------------------------------- | ---------- | +| [国内新闻](http://news.m4.cn/china/) | china | +| [国际新闻](http://news.m4.cn/world/) | world | +| [民生](http://news.m4.cn/livelihood/) | livelihood | +| [社会](http://news.m4.cn/society/) | society | +| [财经](http://news.m4.cn/finance/) | finance | +| [科技](http://news.m4.cn/tech/) | tech |`, + radar: [ + { + source: ['news.m4.cn/:category', 'news.m4.cn/'], + target: '/news/:category', + }, + ], + name: '要闻', + maintainers: ['nczitzk'], handler, + url: 'news.m4.cn', }; -async function handler(ctx) { - const { id = 'news', category = 'china' } = ctx.req.param(); - if (!isValidHost(id)) { - throw new InvalidParameterError('Invalid id'); - } +export async function handler(ctx) { + const [id, category = 'china'] = getSubPath(ctx).split('/').filter(Boolean); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = `http://${id}.m4.cn`; - const currentUrl = new URL(category ? `/${category.replace(/\/$/, '')}/` : '/', rootUrl).href; + const currentUrl = new URL(`/${category}/`, rootUrl).href; const { data: response } = await got(currentUrl); diff --git a/lib/routes/m4/mil.ts b/lib/routes/m4/mil.ts new file mode 100644 index 000000000000..b46690537a9e --- /dev/null +++ b/lib/routes/m4/mil.ts @@ -0,0 +1,28 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/mil/:category?', + categories: ['new-media'], + example: '/m4/mil/china', + parameters: { category: '分类,见下表,默认为中国军情' }, + description: `| 分类 | ID | +| ------------------------------------- | ------- | +| [中国军情](http://mil.m4.cn/china/) | china | +| [国际军情](http://mil.m4.cn/world/) | world | +| [军事评论](http://mil.m4.cn/views/) | views | +| [军事历史](http://mil.m4.cn/history/) | history | +| [军迷说](http://mil.m4.cn/talk/) | talk | +| [武器库](http://mil.m4.cn/arms/) | arms |`, + radar: [ + { + source: ['mil.m4.cn/:category', 'mil.m4.cn/'], + target: '/mil/:category', + }, + ], + name: '军事', + maintainers: ['nczitzk'], + handler, + url: 'mil.m4.cn', +}; diff --git a/lib/routes/magazinelib/latest-magazine.tsx b/lib/routes/magazinelib/latest-magazine.tsx index a6eaee459458..195e3a9f1cbc 100644 --- a/lib/routes/magazinelib/latest-magazine.tsx +++ b/lib/routes/magazinelib/latest-magazine.tsx @@ -25,7 +25,7 @@ export const route: Route = { supportScihub: false, }, name: 'Latest Magazine', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: 'For instance, when doing search at <https://magazinelib.com> and you get url `https://magazinelib.com/?s=new+yorker`, the query is `new+yorker`', }; diff --git a/lib/routes/magnumphotos/magazine.ts b/lib/routes/magnumphotos/magazine.ts index 8d822668c28b..5f366c907a39 100644 --- a/lib/routes/magnumphotos/magazine.ts +++ b/lib/routes/magnumphotos/magazine.ts @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'Magazine', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'magnumphotos.com/', }; diff --git a/lib/routes/mail/imap.ts b/lib/routes/mail/imap.ts index c8cbdf9e9913..f202958bd293 100644 --- a/lib/routes/mail/imap.ts +++ b/lib/routes/mail/imap.ts @@ -10,8 +10,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/imap/:email/:folder{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['other'], + example: '/mail/imap/rss@rsshub.app', + parameters: { + email: 'Email account', + folder: 'Inbox name, `INBOX` by default', + }, + description: 'Only support IMAP protocol, email password and other settings refer to [Route-specific Configurations](https://docs.rsshub.app/deploy/config#route-specific-configurations)', + name: 'Inbox', + maintainers: ['kt286'], handler, }; diff --git a/lib/routes/medieval-china/post.ts b/lib/routes/medieval-china/post.ts index 2933232b842c..459716b3c081 100644 --- a/lib/routes/medieval-china/post.ts +++ b/lib/routes/medieval-china/post.ts @@ -8,13 +8,15 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/', + categories: ['reading'], + example: '/medieval-china', radar: [ { source: ['medieval-china.club/'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['artefaritaKuniklo'], handler, url: 'medieval-china.club/', diff --git a/lib/routes/metacritic/index.tsx b/lib/routes/metacritic/index.tsx index 5b89cd63047b..358f31a4d2e7 100644 --- a/lib/routes/metacritic/index.tsx +++ b/lib/routes/metacritic/index.tsx @@ -2,6 +2,7 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -26,14 +27,36 @@ const renderDescription = (image, description, score) => ); export const route: Route = { - path: '/:type?/:sort?/:filter?', - name: 'Unknown', - maintainers: [], + path: '/game/:sort?/:filter?', + categories: ['new-media'], + example: '/metacritic/game', + parameters: { + sort: 'Sort, see below, `new` for Newest Releases by default', + filter: 'Filter', + }, + description: `| Metascore | User Score | Most Popular | Newest Releases | +| --------- | ---------- | ------------ | --------------- | +| metascore | userscore | popular | new | + +::: tip +The Filter parameter comes from the corresponding page URL. The following is an example: + +The URL of [Action Games to Play on PS5](https://www.metacritic.com/browse/game/all/all/all-time/new/?platform=ps5\\&genre=action) is \`https://www.metacritic.com/browse/game/all/all/all-time/new/?platform=ps5&genre=action\`. The Filter parameter is \`platform=ps5&genre=action\` and the route is [\`/metacritic/game/new/platform=ps5&genre=action\`](https://rsshub.app/metacritic/game/new/platform=ps5\\&genre=action) +:::`, + radar: [ + { + source: ['metacritic.com/browse/game/*'], + target: '/game', + }, + ], + name: 'Games', + maintainers: ['HenryQW', 'nczitzk'], handler, }; -async function handler(ctx) { - const { type = 'game', sort = 'new', filter } = ctx.req.param(); +export async function handler(ctx) { + const type = getSubPath(ctx).split('/', 2)[1]; + const { sort = 'new', filter } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50; const rootUrl = 'https://www.metacritic.com'; diff --git a/lib/routes/metacritic/movie.ts b/lib/routes/metacritic/movie.ts new file mode 100644 index 000000000000..6e36519452d5 --- /dev/null +++ b/lib/routes/metacritic/movie.ts @@ -0,0 +1,31 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/movie/:sort?/:filter?', + categories: ['new-media'], + example: '/metacritic/movie', + parameters: { + sort: 'Sort, see below, `new` for Newest Releases by default', + filter: 'Filter', + }, + description: `| Metascore | User Score | Most Popular | Newest Releases | +| --------- | ---------- | ------------ | --------------- | +| metascore | userscore | popular | new | + +::: tip +The Filter parameter comes from the corresponding page URL. The following is an example: + +The URL of [Action Movies to Watch on Netflix](https://www.metacritic.com/browse/movie/all/all/all-time/new/?network=netflix\\&genre=action) is \`https://www.metacritic.com/browse/movie/all/all/all-time/new/?network=netflix&genre=action\`. The Filter parameter is \`network=netflix&genre=action\` and the route is [\`/metacritic/movie/new/network=netflix&genre=action\`](https://rsshub.app/metacritic/movie/new/network=netflix\\&genre=action) +:::`, + radar: [ + { + source: ['metacritic.com/browse/movie/*'], + target: '/movie', + }, + ], + name: 'Movies', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/metacritic/tv.ts b/lib/routes/metacritic/tv.ts new file mode 100644 index 000000000000..a538c9d94573 --- /dev/null +++ b/lib/routes/metacritic/tv.ts @@ -0,0 +1,31 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tv/:sort?/:filter?', + categories: ['new-media'], + example: '/metacritic/tv', + parameters: { + sort: 'Sort, see below, `new` for Newest Releases by default', + filter: 'Filter', + }, + description: `| Metascore | User Score | Most Popular | Newest Releases | +| --------- | ---------- | ------------ | --------------- | +| metascore | userscore | popular | new | + +::: tip +The Filter parameter comes from the corresponding page URL. The following is an example: + +The URL of [Documentary TV Shows to Watch on Prime Video](https://www.metacritic.com/browse/tv/all/all/all-time/new/?network=prime-video\\&genre=documentary) is \`https://www.metacritic.com/browse/tv/all/all/all-time/new/?network=prime-video&genre=documentary\`. The Filter parameter is \`network=prime-video&genre=documentary\` and the route is [\`/metacritic/tv/new/network=prime-video&genre=documentary\`](https://rsshub.app/metacritic/tv/new/network=prime-video\\&genre=documentary) +:::`, + radar: [ + { + source: ['metacritic.com/browse/tv/*'], + target: '/tv', + }, + ], + name: 'TV Shows', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/metmuseum/exhibitions.ts b/lib/routes/metmuseum/exhibitions.ts index 7f1253552b76..5ca4e215e4d4 100644 --- a/lib/routes/metmuseum/exhibitions.ts +++ b/lib/routes/metmuseum/exhibitions.ts @@ -14,8 +14,19 @@ function generateExhibitionItem(result) { export const route: Route = { path: '/exhibitions/:state?', - name: 'Unknown', - maintainers: [], + categories: ['travel'], + example: '/metmuseum/exhibitions', + parameters: { state: '展览进行的状态:`current` 对应展览当前正在进行,`past` 对应过去的展览,`upcoming` 对应即将举办的展览,默认为 `current`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Exhibitions', + maintainers: ['chazeon'], handler, }; diff --git a/lib/routes/nature/highlight.ts b/lib/routes/nature/highlight.ts index 1a3cdbbb50b8..2295abe1d6f6 100644 --- a/lib/routes/nature/highlight.ts +++ b/lib/routes/nature/highlight.ts @@ -25,7 +25,7 @@ export const route: Route = { }, ], name: 'Research Highlight', - maintainers: [], + maintainers: ['y9c', 'TonyRL'], handler, description: `::: warning Only some journals are supported. diff --git a/lib/routes/nature/news-and-comment.ts b/lib/routes/nature/news-and-comment.ts index 71c6d3ff0880..6908d0b558cf 100644 --- a/lib/routes/nature/news-and-comment.ts +++ b/lib/routes/nature/news-and-comment.ts @@ -20,15 +20,39 @@ import { baseUrl, cookieJar, getArticle, getArticleList } from './utils'; export const route: Route = { path: '/news-and-comment/:journal?', + categories: ['journal'], + example: '/nature/news-and-comment/ng', + parameters: { journal: 'short name for a journal' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, radar: [ { source: ['nature.com/latest-news', 'nature.com/news', 'nature.com/'], target: '/news', }, ], - name: 'Unknown', + name: 'News & Comment', maintainers: ['y9c', 'TonyRL'], handler, + description: `| \`:journal\` | Full Name of the Journal | Route | +| :-----------: | :-------------------------: | -------------------------------------------------------------------------------------------------- | +| nbt | Nature Biotechnology | [/nature/news-and-comment/nbt](https://rsshub.app/nature/news-and-comment/nbt) | +| neuro | Nature Neuroscience | [/nature/news-and-comment/neuro](https://rsshub.app/nature/news-and-comment/neuro) | +| ng | Nature Genetics | [/nature/news-and-comment/ng](https://rsshub.app/nature/news-and-comment/ng) | +| ni | Nature Immunology | [/nature/news-and-comment/ni](https://rsshub.app/nature/news-and-comment/ni) | +| nmeth | Nature Method | [/nature/news-and-comment/nmeth](https://rsshub.app/nature/news-and-comment/nmeth) | +| nchem | Nature Chemistry | [/nature/news-and-comment/nchem](https://rsshub.app/nature/news-and-comment/nchem) | +| nmat | Nature Materials | [/nature/news-and-comment/nmat](https://rsshub.app/nature/news-and-comment/nmat) | +| natmachintell | Nature Machine Intelligence | [/nature/news-and-comment/natmachintell](https://rsshub.app/nature/news-and-comment/natmachintell) | + +- Using router (\`/nature/research/\` + "short name for a journal") to query latest research paper for a certain journal of Nature Publishing Group. +- The journals from NPG are run by different group of people, and the website of may not be consitent for all the journals`, url: 'nature.com/latest-news', }; diff --git a/lib/routes/ncwu/notice.ts b/lib/routes/ncwu/notice.ts index b2c5f62429b4..e66bcb29206f 100644 --- a/lib/routes/ncwu/notice.ts +++ b/lib/routes/ncwu/notice.ts @@ -25,7 +25,7 @@ export const route: Route = { }, ], name: '学校通知', - maintainers: [], + maintainers: ['vuhe'], handler, url: 'ncwu.edu.cn/xxtz.htm', }; diff --git a/lib/routes/nenu/sohac.ts b/lib/routes/nenu/sohac.ts index b9eb34cbf3cd..557d62fc5f6a 100644 --- a/lib/routes/nenu/sohac.ts +++ b/lib/routes/nenu/sohac.ts @@ -2,24 +2,31 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/sohac/*', - name: 'Unknown', - maintainers: [], + path: '/sohac/:path{.+}?', + categories: ['university'], + example: '/nenu/sohac', + parameters: { path: '路径,默认为通知公告' }, + name: '历史文化学院', + maintainers: ['nczitzk'], handler, + description: `::: tip +若订阅 [通知公告](https://sohac.nenu.edu.cn/index/tzgg.htm),网址为 \`https://sohac.nenu.edu.cn/index/tzgg.htm\`。截取 \`https://sohac.nenu.edu.cn/\` 到末尾 \`.htm\` 的部分 \`index/tzgg\` 作为参数,此时路由为 [\`/nenu/sohac/index/tzgg\`](https://rsshub.app/nenu/sohac/index/tzgg)。 + +若订阅 [学院信息](https://sohac.nenu.edu.cn/index/xyxx.htm),网址为 \`https://sohac.nenu.edu.cn/index/xyxx.htm\`。截取 \`https://sohac.nenu.edu.cn/\` 到末尾 \`.htm\` 的部分 \`index/xyxx\` 作为参数,此时路由为 [\`/nenu/sohac/index/xyxx\`](https://rsshub.app/nenu/sohac/index/xyxx)。 +:::`, }; async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; - const path = getSubPath(ctx) === '/sohac' ? '/index/tzgg' : getSubPath(ctx).replace(/^\/sohac/, ''); + const path = ctx.req.param('path') ?? 'index/tzgg'; const rootUrl = 'https://sohac.nenu.edu.cn'; - const currentUrl = `${rootUrl}${path}.htm`; + const currentUrl = `${rootUrl}/${path}.htm`; const response = await got({ method: 'get', diff --git a/lib/routes/nenu/yjsy.ts b/lib/routes/nenu/yjsy.ts index 8b60a548d288..8f6996f7053b 100644 --- a/lib/routes/nenu/yjsy.ts +++ b/lib/routes/nenu/yjsy.ts @@ -2,24 +2,31 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/yjsy/*', - name: 'Unknown', - maintainers: [], + path: '/yjsy/:path{.+}?', + categories: ['university'], + example: '/nenu/yjsy', + parameters: { path: '路径,默认为通知公告' }, + name: '研究生院', + maintainers: ['nczitzk'], handler, + description: `::: tip +若订阅 [通知公告](https://yjsy.nenu.edu.cn/tzgg.htm),网址为 \`https://yjsy.nenu.edu.cn/tzgg.htm\`。截取 \`https://yjsy.nenu.edu.cn/\` 到末尾 \`.htm\` 的部分 \`tzgg\` 作为参数,此时路由为 [\`/nenu/yjsy/tzgg\`](https://rsshub.app/nenu/yjsy/tzgg)。 + +若订阅 [校内新闻](https://yjsy.nenu.edu.cn/xwdt/xnxw.htm),网址为 \`https://yjsy.nenu.edu.cn/xwdt/xnxw.htm\`。截取 \`https://yjsy.nenu.edu.cn/\` 到末尾 \`.htm\` 的部分 \`xwdt/xnxw\` 作为参数,此时路由为 [\`/nenu/yjsy/xwdt/xnxw\`](https://rsshub.app/nenu/yjsy/xwdt/xnxw)。 +:::`, }; async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; - const path = getSubPath(ctx) === '/yjsy' ? '/tzgg' : getSubPath(ctx).replace(/^\/yjsy/, ''); + const path = ctx.req.param('path') ?? 'tzgg'; const rootUrl = 'https://yjsy.nenu.edu.cn'; - const currentUrl = `${rootUrl}${path}.htm`; + const currentUrl = `${rootUrl}/${path}.htm`; const response = await got({ method: 'get', diff --git a/lib/routes/netflav/index.tsx b/lib/routes/netflav/index.tsx index 048f9dbcf8c1..93470c383b29 100644 --- a/lib/routes/netflav/index.tsx +++ b/lib/routes/netflav/index.tsx @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['multimedia'], + example: '/netflav', radar: [ { source: ['netflav.com/'], target: '', }, ], - name: 'Unknown', + name: 'Index', maintainers: ['TonyRL'], handler, url: 'netflav.com/', diff --git a/lib/routes/newyorker/news.ts b/lib/routes/newyorker/news.ts index 60ce2b651684..156bab252601 100644 --- a/lib/routes/newyorker/news.ts +++ b/lib/routes/newyorker/news.ts @@ -26,7 +26,7 @@ export const route: Route = { }, ], name: 'Articles', - maintainers: ['EthanWng97', 'pseudoyu'], + maintainers: ['IvanWng97', 'pseudoyu'], handler, }; diff --git a/lib/routes/niaogebiji/index.ts b/lib/routes/niaogebiji/index.ts index 121418e7ea40..d54b0c433990 100644 --- a/lib/routes/niaogebiji/index.ts +++ b/lib/routes/niaogebiji/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/niaogebiji', radar: [ { source: ['niaogebiji.com/', 'niaogebiji.com/bulletin'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['WenryXu'], handler, url: 'niaogebiji.com/', diff --git a/lib/routes/nintendo/eshop-cn.ts b/lib/routes/nintendo/eshop-cn.ts index cebe08c06b7e..1b26032ad165 100644 --- a/lib/routes/nintendo/eshop-cn.ts +++ b/lib/routes/nintendo/eshop-cn.ts @@ -10,13 +10,15 @@ const software_url = 'https://www.nintendoswitch.com.cn/software/'; export const route: Route = { path: '/eshop/cn', + categories: ['game'], + example: '/nintendo/eshop/cn', radar: [ { source: ['nintendoswitch.com.cn/software', 'nintendoswitch.com.cn/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'eShop New Game Releases (CN)', + maintainers: ['HFO4'], handler, url: 'nintendoswitch.com.cn/software', }; diff --git a/lib/routes/nintendo/eshop-hk.ts b/lib/routes/nintendo/eshop-hk.ts index 4b4fd80f9e91..cc7222c517e7 100644 --- a/lib/routes/nintendo/eshop-hk.ts +++ b/lib/routes/nintendo/eshop-hk.ts @@ -9,13 +9,15 @@ import { renderEshopHkDescription } from './templates/eshop-hk'; export const route: Route = { path: '/eshop/hk', + categories: ['game'], + example: '/nintendo/eshop/hk', radar: [ { source: ['nintendo.com.hk/software/switch', 'nintendo.com.hk/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'eShop New Game Releases (HK)', + maintainers: ['HFO4'], handler, url: 'nintendo.com.hk/software/switch', }; diff --git a/lib/routes/nintendo/eshop-jp.ts b/lib/routes/nintendo/eshop-jp.ts index 308387927491..feabe3a130e1 100644 --- a/lib/routes/nintendo/eshop-jp.ts +++ b/lib/routes/nintendo/eshop-jp.ts @@ -6,13 +6,15 @@ import { renderEshopJpDescription } from './templates/eshop-jp'; export const route: Route = { path: '/eshop/jp', + categories: ['game'], + example: '/nintendo/eshop/jp', radar: [ { source: ['nintendo.co.jp/software/switch/index.html', 'nintendo.co.jp/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'eShop New Game Releases (JP)', + maintainers: ['HFO4'], handler, url: 'nintendo.co.jp/software/switch/index.html', }; diff --git a/lib/routes/nintendo/eshop-us.ts b/lib/routes/nintendo/eshop-us.ts index 2fedf8e5f1c7..a0e9684f45b5 100644 --- a/lib/routes/nintendo/eshop-us.ts +++ b/lib/routes/nintendo/eshop-us.ts @@ -5,13 +5,15 @@ import { renderEshopUsDescription } from './templates/eshop-us'; export const route: Route = { path: '/eshop/us', + categories: ['game'], + example: '/nintendo/eshop/us', radar: [ { source: ['nintendo.com/store/games', 'nintendo.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: 'eShop New Game Releases (US)', + maintainers: ['HFO4'], handler, url: 'nintendo.com/store/games', }; diff --git a/lib/routes/nju/exchangesys.ts b/lib/routes/nju/exchangesys.ts index f4a2d2c74443..18737a36f3b9 100644 --- a/lib/routes/nju/exchangesys.ts +++ b/lib/routes/nju/exchangesys.ts @@ -17,7 +17,7 @@ export const route: Route = { supportScihub: false, }, name: '本科生交换生系统', - maintainers: [], + maintainers: ['cqjjjzr'], handler, description: `| 新闻通知 | 交换生项目 | | -------- | ---------- | diff --git a/lib/routes/nogizaka46/blog.ts b/lib/routes/nogizaka46/blog.ts index 394a81c3715d..d4d64001cea4 100644 --- a/lib/routes/nogizaka46/blog.ts +++ b/lib/routes/nogizaka46/blog.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: 'Nogizaka46 Blog 乃木坂 46 博客', - maintainers: ['Kasper4649', 'akashigakki'], + maintainers: ['Kasper4649', 'AkashiGakki'], handler, url: 'blog.nogizaka46.com/s/n46/diary/MEMBER', description: `Member ID diff --git a/lib/routes/nuaa/college/cae.ts b/lib/routes/nuaa/college/cae.ts index a9af61060c00..91a1f4f533d9 100644 --- a/lib/routes/nuaa/college/cae.ts +++ b/lib/routes/nuaa/college/cae.ts @@ -23,9 +23,15 @@ const map = new Map([ export const route: Route = { path: '/cae/:type/:getDescription?', - name: 'Unknown', + categories: ['university'], + example: '/nuaa/cae/zhxw', + parameters: { type: '分类名,见下表', getDescription: '是否获取全文' }, + name: '自动化学院', maintainers: ['Xm798'], handler, + description: `| 综合新闻 | 党委行政 | 人事 / 合作 | 研究生培养 | 本科生培养 | 学生工作 | 通知公告 | 学术信息 | 答辩公告 | +| -------- | -------- | ----------- | ---------- | ---------- | -------- | -------- | -------- | -------- | +| zhxw | dwxz | rshz | yjs | bks | xsgz | tzgg | xsxx | dbgg |`, }; async function handler(ctx) { diff --git a/lib/routes/nuist/library/lib.ts b/lib/routes/nuist/library/lib.ts index d037faeb85bd..919cc432e7b0 100644 --- a/lib/routes/nuist/library/lib.ts +++ b/lib/routes/nuist/library/lib.ts @@ -10,12 +10,14 @@ const baseUrl = 'https://lib.nuist.edu.cn'; export const route: Route = { path: '/lib', + categories: ['university'], + example: '/nuist/lib', radar: [ { source: ['lib.nuist.edu.cn/', 'lib.nuist.edu.cn/index/tzgg.htm'], }, ], - name: 'Unknown', + name: '图书馆', maintainers: ['gylidian'], handler, url: 'lib.nuist.edu.cn/', diff --git a/lib/routes/nuist/yjs.ts b/lib/routes/nuist/yjs.ts index 3862be9755ae..c57678bbc03a 100644 --- a/lib/routes/nuist/yjs.ts +++ b/lib/routes/nuist/yjs.ts @@ -2,20 +2,31 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/yjs/*', - name: 'Unknown', - maintainers: [], + path: '/yjs/:path{.+}?', + categories: ['university'], + example: '/nuist/yjs/index/tzgg', + parameters: { path: '默认为通知公告' }, + description: `路径字段处填写的是对应南京信息工程大学研究生院学科建设处分类页网址中介于 **<https://yjs.nuist.edu.cn/>** 和 **.htm** 中间的一段。 + +如 [南京信息工程大学研究生院学科建设处工作动态](https://yjs.nuist.edu.cn/index/gzdt.htm) 的网址为 <https://yjs.nuist.edu.cn/index/gzdt.htm,其中介于> **<https://yjs.nuist.edu.cn/>** 和 **.htm** 中间的一段为 \`index/gzdt\`。可以得到路由为 [\`/nuist/yjs/index/gzdt\`](https://rsshub.app/nuist/yjs/index/gzdt) + +以下为部分分类: + +| 工作动态 | 通知公告 | 招生工作 | 培养与学位 | 学生工作 | +| ---------- | ---------- | --------- | ---------- | ---------- | +| index/gzdt | index/tzgg | zsgz/sszs | xwgz/xwtz | xsgz1/xzfc |`, + name: '研究生院学科建设处', + maintainers: ['gylidian', 'nczitzk'], handler, }; async function handler(ctx) { - const path = getSubPath(ctx) === '/yjs/' ? 'index/tzgg' : getSubPath(ctx).replace(/^\/yjs\//, ''); + const path = ctx.req.param('path') ?? 'index/tzgg'; const rootUrl = 'https://yjs.nuist.edu.cn'; const currentUrl = `${rootUrl}/${path}.htm`; diff --git a/lib/routes/oceanengine/arithmetic-index-toutiao.ts b/lib/routes/oceanengine/arithmetic-index-toutiao.ts new file mode 100644 index 000000000000..9089f1d6a03a --- /dev/null +++ b/lib/routes/oceanengine/arithmetic-index-toutiao.ts @@ -0,0 +1,20 @@ +import type { Route } from '@/types'; + +import { handler } from './arithmetic-index'; + +export const route: Route = { + path: '/index/:keyword/toutiao', + categories: ['other'], + example: '/oceanengine/index/教材/toutiao', + parameters: { + keyword: '热点关键词', + }, + description: '爬取巨量算数近 6 个月的头条指数,解密后提取指数波峰当日的热门搜索关键词,生成为 RSS。可用于追踪新闻热点事件。', + features: { + requirePuppeteer: true, + antiCrawler: true, + }, + name: '头条指数波峰', + maintainers: ['Jkker'], + handler, +}; diff --git a/lib/routes/oceanengine/arithmetic-index.tsx b/lib/routes/oceanengine/arithmetic-index.tsx index df1323fe283e..e32d6352e848 100644 --- a/lib/routes/oceanengine/arithmetic-index.tsx +++ b/lib/routes/oceanengine/arithmetic-index.tsx @@ -3,6 +3,7 @@ import { createDecipheriv } from 'node:crypto'; import dayjs from 'dayjs'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; +import { routePath } from 'hono/route'; import { config } from '@/config'; import InvalidParameterError from '@/errors/types/invalid-parameter'; @@ -85,13 +86,23 @@ const createContent = (keyword, queryList, queryListText) => ); export const route: Route = { - path: '/index/:keyword/:channel?', - name: 'Unknown', + path: '/index/:keyword', + categories: ['other'], + example: '/oceanengine/index/教材', + parameters: { + keyword: '热点关键词', + }, + description: '爬取巨量算数近 6 个月的抖音指数,解密后提取指数波峰当日的热门搜索关键词,生成为 RSS。可用于追踪新闻热点事件。', + features: { + requirePuppeteer: true, + antiCrawler: true, + }, + name: '抖音指数波峰', maintainers: ['Jkker'], handler, }; -async function handler(ctx) { +export async function handler(ctx) { const now = dayjs(); const start_date = now.subtract(DEFAULT_FETCH_DURATION_MONTH, 'month').format('YYYYMMDD'); const end_date = now.format('YYYYMMDD'); @@ -99,12 +110,10 @@ async function handler(ctx) { if (!keyword) { throw new InvalidParameterError('Invalid keyword'); } - if (ctx.req.param('channel') && !['douyin', 'toutiao'].includes(ctx.req.param('channel'))) { - throw new InvalidParameterError('Invalid channel。 Only support `douyin` or `toutiao`'); - } + const isToutiao = routePath(ctx) === '/oceanengine/index/:keyword/toutiao'; - const channel = ctx.req.param('channel') === 'toutiao' ? 'toutiao' : 'aweme'; // default channel is `douyin` - const channelName = ctx.req.param('channel') === 'toutiao' ? '头条' : '抖音'; + const channel = isToutiao ? 'toutiao' : 'aweme'; + const channelName = isToutiao ? '头条' : '抖音'; const link = `https://trendinsight.oceanengine.com/arithmetic-index/analysis?keyword=${keyword}&appName=${channel}`; diff --git a/lib/routes/oeeee/app/channel.ts b/lib/routes/oeeee/app/channel.ts index 12453f86e17e..a7557ab0681c 100644 --- a/lib/routes/oeeee/app/channel.ts +++ b/lib/routes/oeeee/app/channel.ts @@ -8,9 +8,21 @@ import { parseArticle } from '../utils'; export const route: Route = { path: '/app/channel/:id', - name: 'Unknown', + categories: ['traditional-media'], + example: '/oeeee/app/channel/50', + parameters: { id: '南都号 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '南都客户端(按南都号 ID)', maintainers: ['TimWu007'], handler, + description: '南都号的 UID 可通过 `m.mp.oeeee.com` 下的文章页面获取。点击文章上方的南都号头像,进入该南都号的个人主页,即可从 url 中获取。', }; async function handler(ctx) { diff --git a/lib/routes/oesw/index.ts b/lib/routes/oesw/index.ts index b49e6a0765bb..c6d0a1c759d4 100644 --- a/lib/routes/oesw/index.ts +++ b/lib/routes/oesw/index.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Data, DataItem, Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import ofetch from '@/utils/ofetch'; const FEED_LANGUAGE = 'de' as const; @@ -12,17 +11,18 @@ const BASE_URL = `${SITE_URL}/immobilienangebot/` as const; export const route: Route = { name: 'Immobilienangebot', example: '/oesw/sofort-verfuegbar/objectType=1&financingType=2®ion=1020', - path: '*', + path: '/:path{.+}?', + parameters: { path: 'Listing page (`immobiliensuche` by default), optionally followed by the query parameters, see the description below' }, maintainers: ['sk22'], categories: ['other'], description: `Get your parameters on ${SITE_URL} under "Immobilienangebot". Make sure to remove the \`?\` at the beginning from the query parameters!`, async handler(ctx) { - // ['', 'sofort-verfuegbar', 'objectType=1®ion=1010'] - const parts = getSubPath(ctx).split('/'); - const listingPage = parts[1] || 'immobiliensuche'; - let params = parts[2] || ''; + // ['sofort-verfuegbar', 'objectType=1®ion=1010'] + const parts = (ctx.req.param('path') ?? '').split('/'); + const listingPage = parts[0] || 'immobiliensuche'; + let params = parts[1] || ''; if (params.startsWith('&')) { params = params.slice(1); } diff --git a/lib/routes/onehu/common.ts b/lib/routes/onehu/common.ts index e5c3899d9466..ddc5e3091ecf 100644 --- a/lib/routes/onehu/common.ts +++ b/lib/routes/onehu/common.ts @@ -7,7 +7,9 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/', - name: 'Unknown', + categories: ['new-media'], + example: '/onehu', + name: '首页', maintainers: ['ruoshui9527'], handler, }; diff --git a/lib/routes/openwrt/releases.ts b/lib/routes/openwrt/releases.ts index f20223a9d74b..8c8532aeb0bd 100644 --- a/lib/routes/openwrt/releases.ts +++ b/lib/routes/openwrt/releases.ts @@ -5,14 +5,20 @@ import got from '@/utils/got'; export const route: Route = { path: '/releases/:brand/:model', + categories: ['program-update'], + example: '/openwrt/releases/xiaomi/xiaomi_redmi_router_ac2100', + parameters: { + brand: 'Device Model, can be found in url of `Table of Hardware` -> `Device Page`', + model: 'Same as above', + }, radar: [ { source: ['openwrt.org/toh/:band/:model'], target: '/releases/:model', }, ], - name: 'Unknown', - maintainers: [], + name: 'Releases', + maintainers: ['DIYgod'], handler, }; diff --git a/lib/routes/patagonia/new-arrivals.tsx b/lib/routes/patagonia/new-arrivals.tsx index 1254bffdb5af..feb54f457ddd 100644 --- a/lib/routes/patagonia/new-arrivals.tsx +++ b/lib/routes/patagonia/new-arrivals.tsx @@ -31,7 +31,7 @@ export const route: Route = { supportScihub: false, }, name: 'New Arrivals', - maintainers: [], + maintainers: ['IvanWng97'], handler, description: `| Men's | Women's | Kids' & Baby | Packs & Gear | | ----- | ------- | ------------ | ------------ | diff --git a/lib/routes/people/xjpjh.ts b/lib/routes/people/xjpjh.ts index b34d6a497aff..1f5b8ec3cbff 100644 --- a/lib/routes/people/xjpjh.ts +++ b/lib/routes/people/xjpjh.ts @@ -26,7 +26,7 @@ export const route: Route = { }, ], name: '习近平系列重要讲话', - maintainers: [], + maintainers: ['LogicJake'], handler, url: 'people.com.cn/', }; diff --git a/lib/routes/peopo/topic.ts b/lib/routes/peopo/topic.ts index 332a3047d500..bed66d868605 100644 --- a/lib/routes/peopo/topic.ts +++ b/lib/routes/peopo/topic.ts @@ -28,7 +28,7 @@ export const route: Route = { }, ], name: '新聞分類', - maintainers: [], + maintainers: ['TonyRL'], handler, description: `| 分類 | ID | | -------- | --- | diff --git a/lib/routes/pianyuan/search.ts b/lib/routes/pianyuan/search.ts index 7262811a4deb..db563ad1c69d 100644 --- a/lib/routes/pianyuan/search.ts +++ b/lib/routes/pianyuan/search.ts @@ -7,16 +7,19 @@ import utils from './utils'; export const route: Route = { path: '/indexers/pianyuan/results/search/api', + categories: ['multimedia'], + example: '/pianyuan/indexers/pianyuan/results/search/api?t=test&q=长津湖', radar: [ { source: ['pianyuan.org/'], target: '/index', }, ], - name: 'Unknown', + name: '搜索', maintainers: ['jerry1119'], handler, url: 'pianyuan.org/', + description: '搜索路由模仿 jackett 的搜索 api, 以提供给 nastools 使用,填写在 nastools 配置 indexer 中', }; async function handler(ctx) { diff --git a/lib/routes/pikabu/community.ts b/lib/routes/pikabu/community.ts index da009163d13b..df481f08f916 100644 --- a/lib/routes/pikabu/community.ts +++ b/lib/routes/pikabu/community.ts @@ -1,20 +1,31 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, fixImage, fixVideo } from './utils'; export const route: Route = { - path: '/:type/:name', - name: 'Unknown', - maintainers: [], + path: '/community/:name', + categories: ['bbs'], + example: '/pikabu/community/real_true_story', + parameters: { name: 'Community name' }, + radar: [ + { + source: ['pikabu.ru/community/:name'], + target: '/community/:name', + }, + ], + name: 'Community', + maintainers: ['TonyRL'], handler, }; -async function handler(ctx) { - const { type, name, sort = 'new' } = ctx.req.param(); +export async function handler(ctx) { + const { name, sort = 'new' } = ctx.req.param(); + const type = getSubPath(ctx).split('/', 2)[1]; const sortString = sort === 'default' || type === 'tag' ? '' : `/${sort}`; const { data: response } = await got(`${baseUrl}/ajax/${type}/${name}${sortString}`); diff --git a/lib/routes/pikabu/tag.ts b/lib/routes/pikabu/tag.ts new file mode 100644 index 000000000000..e3dcd77fe65e --- /dev/null +++ b/lib/routes/pikabu/tag.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './community'; + +export const route: Route = { + path: '/tag/:name', + categories: ['bbs'], + example: '/pikabu/tag/Metallica', + parameters: { name: 'Tag name' }, + radar: [ + { + source: ['pikabu.ru/tag/:name'], + target: '/tag/:name', + }, + ], + name: 'Tag', + maintainers: ['TonyRL'], + handler, +}; diff --git a/lib/routes/pincong/topic.ts b/lib/routes/pincong/topic.ts index 2fa0b2af1611..904c7cec4be2 100644 --- a/lib/routes/pincong/topic.ts +++ b/lib/routes/pincong/topic.ts @@ -8,12 +8,23 @@ import { baseUrl, playwrightGet } from './utils'; export const route: Route = { path: '/topic/:topic', + categories: ['bbs'], + example: '/pincong/topic/美国', + parameters: { topic: '话题,可在官网获取' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['pincong.rocks/topic/:topic'], }, ], - name: 'Unknown', + name: '话题', maintainers: ['zphw'], handler, }; diff --git a/lib/routes/pku/ss/notice.ts b/lib/routes/pku/ss/notice.ts index 38fdb5c01e95..b84a092f4a01 100644 --- a/lib/routes/pku/ss/notice.ts +++ b/lib/routes/pku/ss/notice.ts @@ -6,12 +6,23 @@ const host = `${baseUrl}/newscenter/notice/`; export const route: Route = { path: '/ss/notice', + categories: ['university'], + example: '/pku/ss/notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['ss.pku.edu.cn/index.php/newscenter/notice', 'ss.pku.edu.cn/'], }, ], - name: 'Unknown', + name: '软件与微电子学院 - 通知公告', maintainers: ['legr4ndk'], handler, url: 'ss.pku.edu.cn/index.php/newscenter/notice', diff --git a/lib/routes/pnas/index.tsx b/lib/routes/pnas/index.tsx index 101e0a22a37a..da79b94e33f4 100644 --- a/lib/routes/pnas/index.tsx +++ b/lib/routes/pnas/index.tsx @@ -13,16 +13,29 @@ import { setCookies } from '@/utils/playwright-utils'; export const route: Route = { path: '/:topicPath{.+}?', + categories: ['journal'], + example: '/pnas/latest', + parameters: { + topicPath: 'Topic path, support **Featured Topics**, **Articles By Topic** and [**Collected Papers**](https://www.pnas.org/about/collected-papers), `latest` by default', + }, + features: { + requirePuppeteer: true, + antiCrawler: true, + supportScihub: true, + }, radar: [ { source: ['pnas.org/*topicPath'], target: '/:topicPath', }, ], - name: 'Unknown', - maintainers: [], + name: 'Journal', + maintainers: ['emdoe', 'HenryQW', 'y9c'], handler, url: 'pnas.org/*topicPath', + description: `::: tip +Some topics require adding \`topic/\` to \`topicPath\` like [\`/pnas/topic/app-math\`](https://rsshub.app/pnas/topic/app-math) and some don't like [\`/pnas/biophysics-and-computational-biology\`](https://rsshub.app/pnas/biophysics-and-computational-biology) +:::`, }; async function handler(ctx) { diff --git a/lib/routes/pts/category.ts b/lib/routes/pts/category.ts new file mode 100644 index 000000000000..6a746b03b7d9 --- /dev/null +++ b/lib/routes/pts/category.ts @@ -0,0 +1,38 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/category/:id', + categories: ['traditional-media'], + example: '/pts/category/9', + parameters: { id: '分類 id,见下表,可在对应分類页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.pts.org.tw/category/:id', 'news.pts.org.tw/'], + }, + ], + description: `| 名称 | 编号 | +| -------- | ---- | +| 政治 | 1 | +| 社會 | 7 | +| 全球 | 4 | +| 生活 | 5 | +| 兩岸 | 9 | +| 地方 | 11 | +| 產經 | 10 | +| 文教科技 | 6 | +| 環境 | 3 | +| 社福人權 | 12 |`, + name: '分類', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/pts/index.ts b/lib/routes/pts/index.ts index fb07c1626592..da6dad56d8bb 100644 --- a/lib/routes/pts/index.ts +++ b/lib/routes/pts/index.ts @@ -10,15 +10,32 @@ import timezone from '@/utils/timezone'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/dailynews', + categories: ['traditional-media'], + example: '/pts/dailynews', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.pts.org.tw/dailynews', 'news.pts.org.tw/'], + }, + ], + name: '即時新聞', + maintainers: ['nczitzk'], handler, + url: 'news.pts.org.tw/dailynews', }; -async function handler(ctx) { +export async function handler(ctx) { const rootUrl = 'https://news.pts.org.tw'; - const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '/dailynews' : getSubPath(ctx)}`; + const currentUrl = `${rootUrl}${getSubPath(ctx)}`; const response = await got({ method: 'get', diff --git a/lib/routes/pts/live.ts b/lib/routes/pts/live.ts index ac9deb5eee85..7d2f4b1cbebb 100644 --- a/lib/routes/pts/live.ts +++ b/lib/routes/pts/live.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: '整理報導', - maintainers: [], + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/pts/opinion.ts b/lib/routes/pts/opinion.ts new file mode 100644 index 000000000000..3ddfd9ddf1e7 --- /dev/null +++ b/lib/routes/pts/opinion.ts @@ -0,0 +1,27 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/opinion', + categories: ['traditional-media'], + example: '/pts/opinion', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.pts.org.tw/opinion', 'news.pts.org.tw/'], + }, + ], + name: '觀點', + maintainers: ['nczitzk'], + handler, + url: 'news.pts.org.tw/opinion', +}; diff --git a/lib/routes/pts/report.ts b/lib/routes/pts/report.ts new file mode 100644 index 000000000000..dcb0dd080bc2 --- /dev/null +++ b/lib/routes/pts/report.ts @@ -0,0 +1,27 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/report', + categories: ['traditional-media'], + example: '/pts/report', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.pts.org.tw/report', 'news.pts.org.tw/'], + }, + ], + name: '深度報導', + maintainers: ['nczitzk'], + handler, + url: 'news.pts.org.tw/report', +}; diff --git a/lib/routes/pts/tag.ts b/lib/routes/pts/tag.ts new file mode 100644 index 000000000000..9b18313c8522 --- /dev/null +++ b/lib/routes/pts/tag.ts @@ -0,0 +1,26 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:id', + categories: ['traditional-media'], + example: '/pts/tag/230', + parameters: { id: '標籤 id,可在对应標籤页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.pts.org.tw/tag/:id', 'news.pts.org.tw/'], + }, + ], + name: '標籤', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/pubmed/trending.tsx b/lib/routes/pubmed/trending.tsx index 8897a7aa342b..16ab269bbac4 100644 --- a/lib/routes/pubmed/trending.tsx +++ b/lib/routes/pubmed/trending.tsx @@ -9,9 +9,17 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/trending/:filters?', - name: 'Unknown', - maintainers: ['nczitzk'], + categories: ['journal'], + example: '/pubmed/trending', + parameters: { filters: 'Filters, can be found in URL' }, + name: 'Trending articles', + maintainers: ['y9c', 'nczitzk'], handler, + description: `::: tip +For the parameter **filter**, the \`filter\` parameter in the URL should be split into a string by \`,\`, here is an example. + +In \`https://pubmed.ncbi.nlm.nih.gov/trending/?filter=simsearch1.fha&filter=pubt.clinicaltrial&filter=pubt.randomizedcontrolledtrial\`, the filter parameters are \`simsearch1.fha\`, \`pubt.clinicaltrial\`, and \`pubt.randomizedcontrolledtrial\`. Therefore, the filter corresponding to the route should be filled with \`simsearch1.fha,pubt.clinicaltrial,pubt.randomizedcontrolledtrial\`, and the route is [\`/pubmed/trending/simsearch1.fha,pubt.clinicaltrial,pubt.randomizedcontrolledtrial\`](https://rsshub.app/pubmed/trending/simsearch1.fha,pubt.clinicaltrial,pubt.randomizedcontrolledtrial) +:::`, }; async function handler(ctx) { diff --git a/lib/routes/qianp/news.ts b/lib/routes/qianp/news.ts index 5efe5b1cd2ff..bd0593ef08ca 100644 --- a/lib/routes/qianp/news.ts +++ b/lib/routes/qianp/news.ts @@ -9,8 +9,11 @@ import { getTokenAndSecret } from './utils'; export const route: Route = { path: '/news/:path{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['new-media'], + example: '/qianp/news', + parameters: { path: '路径,可在URL中找到,默认为 `news/recommend`' }, + name: '知识库/资讯', + maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/qiyoujiage/price.ts b/lib/routes/qiyoujiage/price.ts index 10cdc51e3ebe..c7afcb2f1a69 100644 --- a/lib/routes/qiyoujiage/price.ts +++ b/lib/routes/qiyoujiage/price.ts @@ -6,8 +6,16 @@ import md5 from '@/utils/md5'; export const route: Route = { path: '/:path{.+}', - name: 'Unknown', - maintainers: [], + categories: ['other'], + example: '/qiyoujiage/shanghai', + parameters: { path: '路径' }, + description: `::: tip +路径处填写对应页面 URL 中 \`http://www.qiyoujiage.com/\` 和 \`.shtml\` 之间的字段。下面是一个例子。 + +若订阅 [福建漳州龙海今日油价](http://www.qiyoujiage.com/fujian/zhangzhou/longhai.shtml) 则将对应页面 URL <http://www.qiyoujiage.com/fujian/zhangzhou/longhai.shtml> 中 \`http://www.qiyoujiage.com/\` 和 \`.shtml\` 之间的字段 \`fujian/zhangzhou/longhai\` 作为路径填入。此时路由为 [\`/qiyoujiage/fujian/zhangzhou/longhai\`](https://rsshub.app/qiyoujiage/fujian/zhangzhou/longhai) +:::`, + name: '今日油价查询', + maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/qoo-app/notes/topic.ts b/lib/routes/qoo-app/notes/topic.ts index 178011db15e8..c60ebd190ca8 100644 --- a/lib/routes/qoo-app/notes/topic.ts +++ b/lib/routes/qoo-app/notes/topic.ts @@ -7,7 +7,18 @@ import { extractNotes, notesUrl } from '../utils'; export const route: Route = { path: '/notes/:lang?/topic/:topic', - name: 'Unknown', + categories: ['anime'], + example: '/qoo-app/notes/en/topic/QooAppGacha', + parameters: { lang: 'Language, see the table above, empty means `中文`', topic: 'Hashtag name without `#`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Hot Hashtags', maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/qq/ac/comic.ts b/lib/routes/qq/ac/comic.ts index ceaccc0c312a..be30a37e38cb 100644 --- a/lib/routes/qq/ac/comic.ts +++ b/lib/routes/qq/ac/comic.ts @@ -7,14 +7,19 @@ import { mobileRootUrl, rootUrl } from './utils'; export const route: Route = { path: '/ac/comic/:id?', + categories: ['anime'], + example: '/qq/ac/comic/531490', + parameters: { + id: '编号,可在对应页 URL 中找到', + }, radar: [ { source: ['ac.qq.com/Comic/ComicInfo/id/:id', 'ac.qq.com/'], target: '/ac/comic/:id', }, ], - name: 'Unknown', - maintainers: [], + name: '漫画', + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/quicker/versions.ts b/lib/routes/quicker/versions.ts index 9cf62a0e391a..03146ff2d8b4 100644 --- a/lib/routes/quicker/versions.ts +++ b/lib/routes/quicker/versions.ts @@ -5,8 +5,10 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: ['/update', '/versions'], - name: 'Unknown', + path: '/versions', + categories: ['program-update'], + example: '/quicker/versions', + name: '版本更新', maintainers: ['Cesaryuan', 'nczitzk'], handler, url: 'getquicker.net/Help/Versions', diff --git a/lib/routes/rarehistoricalphotos/index.ts b/lib/routes/rarehistoricalphotos/index.ts index f569de4c9639..c26a83b560ca 100644 --- a/lib/routes/rarehistoricalphotos/index.ts +++ b/lib/routes/rarehistoricalphotos/index.ts @@ -6,13 +6,15 @@ const baseUrl = 'https://rarehistoricalphotos.com'; export const route: Route = { path: '/', + categories: ['picture'], + example: '/rarehistoricalphotos', radar: [ { source: ['rarehistoricalphotos.com/'], target: '', }, ], - name: 'Unknown', + name: 'Home', maintainers: ['TonyRL'], handler, url: 'rarehistoricalphotos.com/', diff --git a/lib/routes/reactnewsletter/reactnewsletter.ts b/lib/routes/reactnewsletter/reactnewsletter.ts index e8d908771f67..dcf09a00370e 100644 --- a/lib/routes/reactnewsletter/reactnewsletter.ts +++ b/lib/routes/reactnewsletter/reactnewsletter.ts @@ -8,13 +8,16 @@ const currentURL = 'https://reactnewsletter.com/issues'; export const route: Route = { path: '/', + categories: ['programming'], + example: '/reactnewsletter', radar: [ { source: ['bytes.dev/issues', 'bytes.dev/'], target: '', }, ], - name: 'Unknown', + name: 'React Newsletter', + description: 'Stay up to date on the latest React news, tutorials, resources, and more. Delivered every Tuesday, for free.', maintainers: ['meixger'], handler, url: 'bytes.dev/issues', diff --git a/lib/routes/researchgate/publications.ts b/lib/routes/researchgate/publications.ts index 3d34f586f323..d654e45a82e3 100644 --- a/lib/routes/researchgate/publications.ts +++ b/lib/routes/researchgate/publications.ts @@ -7,14 +7,21 @@ import playwright from '@/utils/playwright'; export const route: Route = { path: '/publications/:id', + categories: ['study'], + example: '/researchgate/publications/Somsak-Panha', + parameters: { id: 'Username, can be found in URL' }, + features: { + requirePuppeteer: true, + antiCrawler: true, + }, radar: [ { source: ['researchgate.net/profile/:username'], target: '/publications/:username', }, ], - name: 'Unknown', - maintainers: [], + name: 'Publications', + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/routledge/book-series.tsx b/lib/routes/routledge/book-series.tsx index 2385837a7631..1aa5e17b582d 100644 --- a/lib/routes/routledge/book-series.tsx +++ b/lib/routes/routledge/book-series.tsx @@ -9,12 +9,18 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/:bookName/book-series/:bookId', + categories: ['journal'], + example: '/routledge/A-Colour-Atlas/book-series/CRCACOLOATLA', + parameters: { + bookName: 'Book name, can be found in URL', + bookId: 'Book ID, can be found in URL', + }, radar: [ { source: ['routledge.com/:bookName/book-series/:bookId'], }, ], - name: 'Unknown', + name: 'Book Series', maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/rsshub/transform/sitemap.ts b/lib/routes/rsshub/transform/sitemap.ts index d10d856a2a31..bba12872a33c 100644 --- a/lib/routes/rsshub/transform/sitemap.ts +++ b/lib/routes/rsshub/transform/sitemap.ts @@ -7,8 +7,29 @@ import got from '@/utils/got'; export const route: Route = { path: '/transform/sitemap/:url/:routeParams?', - name: 'Unknown', + categories: ['other'], + example: '/rsshub/transform/sitemap/https%3A%2F%2Fwww.sitemaps.org%2Fsitemap.xml', + parameters: { url: '`encodeURIComponent`ed URL address', routeParams: 'Transformation rules, requires URL encode' }, + features: { + requireConfig: [ + { + name: 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN', + description: '', + }, + ], + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Transformation - Sitemap', maintainers: ['flrngel'], + description: `Specify options (in the format of query string) in parameter \`routeParams\` parameter to extract data from Sitemap. (Follows Sitemap Protocol 0.9) + +| Key | Meaning | Accepted Values | Default | +| ------- | -------------------- | --------------- | -------------------------------- | +| \`title\` | The title of the RSS | \`string\` | The first \`<loc>\` in the sitemap |`, handler, }; diff --git a/lib/routes/ruancan/category.ts b/lib/routes/ruancan/category.ts index dbee93f0429b..f9abc94d7363 100644 --- a/lib/routes/ruancan/category.ts +++ b/lib/routes/ruancan/category.ts @@ -6,7 +6,7 @@ export const route: Route = { path: '/category/:category?', categories: ['new-media'], example: '/ruancan/category/news', - parameters: { category: '分类 id,可在对应分类页 URL 中找到,默认为业界' }, + parameters: { category: '分类 id,可在对应分类页 URL 中找到' }, features: { requireConfig: false, requirePuppeteer: false, @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '分类', - maintainers: [], + maintainers: ['nczitzk'], handler, url: 'ruancan.com/', }; diff --git a/lib/routes/ruancan/index.ts b/lib/routes/ruancan/index.ts index 9647f0a7741b..5310eb9cdce7 100644 --- a/lib/routes/ruancan/index.ts +++ b/lib/routes/ruancan/index.ts @@ -4,14 +4,16 @@ import { fetchFeed } from './utils'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/ruancan', radar: [ { source: ['ruancan.com/'], target: '', }, ], - name: 'Unknown', - maintainers: [], + name: '首页', + maintainers: ['nczitzk'], handler, url: 'ruancan.com/', }; diff --git a/lib/routes/ruancan/search.ts b/lib/routes/ruancan/search.ts index 7a5ffe4d7ce8..00b6827e5cbd 100644 --- a/lib/routes/ruancan/search.ts +++ b/lib/routes/ruancan/search.ts @@ -6,7 +6,7 @@ export const route: Route = { path: '/search/:keyword?', categories: ['new-media'], example: '/ruancan/search/Windows', - parameters: { keyword: '关键字,默认为空' }, + parameters: { keyword: '关键字' }, features: { requireConfig: false, requirePuppeteer: false, @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '搜索', - maintainers: [], + maintainers: ['nczitzk'], handler, url: 'ruancan.com/', }; diff --git a/lib/routes/ruancan/user.ts b/lib/routes/ruancan/user.ts index 6ed3d3f08251..2b1697cc29e1 100644 --- a/lib/routes/ruancan/user.ts +++ b/lib/routes/ruancan/user.ts @@ -4,13 +4,16 @@ import { fetchFeed } from './utils'; export const route: Route = { path: '/user/:id', + categories: ['new-media'], + example: '/ruancan/user/72', + parameters: { id: '用户 id,可在对应用户页 URL 中找到' }, radar: [ { source: ['ruancan.com/i/:id', 'ruancan.com/'], }, ], - name: 'Unknown', - maintainers: [], + name: '用户文章', + maintainers: ['nczitzk'], handler, url: 'ruancan.com/', }; diff --git a/lib/routes/sakurazaka46/blog.ts b/lib/routes/sakurazaka46/blog.ts index 186940281d2c..e5a08dab5155 100644 --- a/lib/routes/sakurazaka46/blog.ts +++ b/lib/routes/sakurazaka46/blog.ts @@ -20,7 +20,7 @@ export const route: Route = { supportScihub: false, }, name: 'Sakurazaka46 Blog 櫻坂 46 博客', - maintainers: ['victor21813', 'nczitzk', 'akashigakki'], + maintainers: ['victor21813', 'nczitzk', 'AkashiGakki'], handler, description: `Member ID diff --git a/lib/routes/sec-in/index.ts b/lib/routes/sec-in/index.ts index fc0d3078b64a..1343da426c34 100644 --- a/lib/routes/sec-in/index.ts +++ b/lib/routes/sec-in/index.ts @@ -4,7 +4,9 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', - name: 'Unknown', + categories: ['bbs'], + example: '/sec-in', + name: '最新文章', maintainers: ['p7e4'], handler, }; diff --git a/lib/routes/secretsanfrancisco/rss.tsx b/lib/routes/secretsanfrancisco/rss.tsx index 20d069784ef2..e4e1510a339b 100644 --- a/lib/routes/secretsanfrancisco/rss.tsx +++ b/lib/routes/secretsanfrancisco/rss.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'Category', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/sehuatang/index.ts b/lib/routes/sehuatang/index.ts index fe0e975c594a..1d2f8c095d55 100644 --- a/lib/routes/sehuatang/index.ts +++ b/lib/routes/sehuatang/index.ts @@ -41,6 +41,12 @@ const forumIdMaps = { export const route: Route = { path: ['/bt/:subforumid?', '/picture/:subforumid', '/:subforumid?/:type?', '/:subforumid?', ''], + categories: ['multimedia'], + example: '/sehuatang/36/368', + parameters: { + subforumid: '版块 id 或板块名称(见下表), 为空默认高清中文字幕', + type: '类型 id, 可在分区类型过滤后的 URL 中找到', + }, name: 'Forum', maintainers: ['qiwihui', 'junfengP', 'nczitzk'], handler, diff --git a/lib/routes/shu/jwb.ts b/lib/routes/shu/jwb.ts index ab5bd7601fbc..3e29242bfeff 100644 --- a/lib/routes/shu/jwb.ts +++ b/lib/routes/shu/jwb.ts @@ -14,6 +14,9 @@ const alias = new Map([ export const route: Route = { path: '/jwb/:type?', + categories: ['university'], + example: '/shu/jwb/notice', + parameters: { type: '消息类型,默认为`notice`' }, radar: [ { source: ['www.shu.edu.cn/index'], diff --git a/lib/routes/shuiguopai/index.tsx b/lib/routes/shuiguopai/index.tsx index d881478a414a..056097189303 100644 --- a/lib/routes/shuiguopai/index.tsx +++ b/lib/routes/shuiguopai/index.tsx @@ -10,13 +10,15 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/shuiguopai', radar: [ { source: ['shuiguopai.com/'], target: '', }, ], - name: 'Unknown', + name: '首页', maintainers: ['nczitzk'], handler, url: 'shuiguopai.com/', diff --git a/lib/routes/sinchew/category.ts b/lib/routes/sinchew/category.ts new file mode 100644 index 000000000000..4234f001d037 --- /dev/null +++ b/lib/routes/sinchew/category.ts @@ -0,0 +1,27 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/category/:category{.+}?', + categories: ['traditional-media'], + example: '/sinchew/category/头条', + parameters: { category: '分类,见下表,亦可以在对应分类页 URL 中找到' }, + radar: [ + { + source: ['sinchew.com.my/category/:category', 'sinchew.com.my/'], + target: '/category/:category', + }, + ], + name: '分类', + maintainers: ['nczitzk'], + description: `| 头条 | 国内 | 国际 | 言路 | 财经 | 地方 | 副刊 | 娱乐 | 体育 | 百格 | 星角攝 | 好运来 | +| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ------ | ------ | + +::: tip +若订阅单级分类 [头条](https://www.sinchew.com.my/category/头条),其 URL 为 [https://www.sinchew.com.my/category/ 头条](https://www.sinchew.com.my/category/头条),则路由为 [\`/sinchew/category/头条\`](https://rsshub.app/sinchew/category/头条)。 + +若订阅多级分类 [国际 > 天下事](https://www.sinchew.com.my/category/国际/天下事),其 URL 为 [https://www.sinchew.com.my/category/ 国际 / 天下事](https://www.sinchew.com.my/category/国际/天下事),则路由为 [\`/sinchew/category/国际/天下事\`](https://rsshub.app/sinchew/category/国际/天下事)。 +:::`, + handler, +}; diff --git a/lib/routes/sinchew/index.tsx b/lib/routes/sinchew/index.tsx index e34462f1b814..c0996abf71ce 100644 --- a/lib/routes/sinchew/index.tsx +++ b/lib/routes/sinchew/index.tsx @@ -9,22 +9,26 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '*', + path: '/', + categories: ['traditional-media'], + example: '/sinchew', radar: [ { source: ['sinchew.com.my/'], - target: '', + target: '/', }, ], - name: 'Unknown', - maintainers: [], + name: '首页', + maintainers: ['nczitzk'], handler, url: 'sinchew.com.my/', }; -async function handler(ctx) { +export async function handler(ctx) { + const subPath = getSubPath(ctx); + const rootUrl = 'https://www.sinchew.com.my'; - const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '' : getSubPath(ctx)}`; + const currentUrl = `${rootUrl}${subPath === '/' ? '' : subPath}`; const response = await got({ method: 'get', diff --git a/lib/routes/sinchew/latest.ts b/lib/routes/sinchew/latest.ts new file mode 100644 index 000000000000..479c6172cbfb --- /dev/null +++ b/lib/routes/sinchew/latest.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/latest', + categories: ['traditional-media'], + example: '/sinchew/latest', + radar: [ + { + source: ['sinchew.com.my/latest', 'sinchew.com.my/'], + target: '/latest', + }, + ], + name: '最新', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/snowpeak/us-new-arrivals.tsx b/lib/routes/snowpeak/us-new-arrivals.tsx index 93f700df5b3b..7d87abd2d20b 100644 --- a/lib/routes/snowpeak/us-new-arrivals.tsx +++ b/lib/routes/snowpeak/us-new-arrivals.tsx @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: 'New Arrivals(USA)', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'snowpeak.com/collections/new-arrivals', }; diff --git a/lib/routes/sony/downloads.ts b/lib/routes/sony/downloads.ts index f8cd5f114652..222cdf2a38c4 100644 --- a/lib/routes/sony/downloads.ts +++ b/lib/routes/sony/downloads.ts @@ -23,7 +23,7 @@ export const route: Route = { }, ], name: 'Software Downloads', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: `::: tip Open \`https://www.sony.com/electronics/support\` and search for the corresponding product, such as \`Sony A7M4\`, the website corresponding to which is \`https://www.sony.com/electronics/support/e-mount-body-ilce-7-series/ilce-7m4/downloads\`, where \`productType\` is \`e-mount-body-ilce-7-series\` and \`productId\` is \`ilce-7m4\`. diff --git a/lib/routes/sspu/jwc.ts b/lib/routes/sspu/jwc.ts index b6a61a19ad9b..a9101dbe1519 100644 --- a/lib/routes/sspu/jwc.ts +++ b/lib/routes/sspu/jwc.ts @@ -8,14 +8,20 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/jwc/:listId', + categories: ['university'], + example: '/sspu/jwc/897', + parameters: { listId: '专栏 ID,见下表' }, radar: [ { source: ['jwc.sspu.edu.cn/jwc/:listId/list.htm'], }, ], - name: 'Unknown', + name: '教务处', maintainers: ['TonyRL'], handler, + description: `| 学生专栏 | 教师专栏 | +| -------- | -------- | +| 897 | 898 |`, }; async function handler(ctx) { diff --git a/lib/routes/sspu/pe.ts b/lib/routes/sspu/pe.ts index 317e5ec67dd3..267abd63d64d 100644 --- a/lib/routes/sspu/pe.ts +++ b/lib/routes/sspu/pe.ts @@ -7,15 +7,62 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/pe/:id?', + categories: ['university'], + example: '/sspu/pe', + parameters: { id: '栏目 id,见下表,默认为通知公告' }, radar: [ { source: ['pe2016.sspu.edu.cn/:id/list.htm'], target: '/pe/:id', }, ], - name: 'Unknown', + name: '体育部', maintainers: ['nczitzk'], handler, + description: `| 通知公告 | 体育新闻 | 场馆管理 | 相关下载 | +| -------- | -------- | -------- | -------- | +| 342 | 343 | 324 | 325 | + +<details> + <summary>更多栏目</summary> + +#### [部门概况](https://pe2016.sspu.edu.cn/318/list.htm) + +| [部门简介](https://pe2016.sspu.edu.cn/327/list.htm) | [师资介绍](https://pe2016.sspu.edu.cn/328/list.htm) | [机构设置](https://pe2016.sspu.edu.cn/329/list.htm) | [团队建设](https://pe2016.sspu.edu.cn/330/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | +| 327 | 328 | 329 | 330 | + +#### [教育教学](https://pe2016.sspu.edu.cn/319/list.htm) + +| [课程介绍](https://pe2016.sspu.edu.cn/331/list.htm) | [教学管理](https://pe2016.sspu.edu.cn/332/list.htm) | [教学成果](https://pe2016.sspu.edu.cn/333/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | +| 331 | 332 | 333 | + +#### [学科研究](https://pe2016.sspu.edu.cn/320/list.htm) + +| [学术交流](https://pe2016.sspu.edu.cn/334/list.htm) | [科研工作](https://pe2016.sspu.edu.cn/335/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | +| 334 | 335 | + +#### [运动竞赛](https://pe2016.sspu.edu.cn/321/list.htm) + +| [竞赛管理](https://pe2016.sspu.edu.cn/336/list.htm) | [竞赛成绩](https://pe2016.sspu.edu.cn/337/list.htm) | [特色项目](https://pe2016.sspu.edu.cn/338/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | +| 336 | 337 | 338 | + +#### [群体活动](https://pe2016.sspu.edu.cn/322/list.htm) + +| [阳光体育](https://pe2016.sspu.edu.cn/345/list.htm) | [体育社团](https://pe2016.sspu.edu.cn/346/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | +| 345 | 346 | + +#### [党群工作](https://pe2016.sspu.edu.cn/323/list.htm) + +| [党务公开](https://pe2016.sspu.edu.cn/339/list.htm) | [精神文明](https://pe2016.sspu.edu.cn/340/list.htm) | [教工之家](https://pe2016.sspu.edu.cn/341/list.htm) | +| --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | +| 339 | 340 | 341 | + +</details>`, }; async function handler(ctx) { diff --git a/lib/routes/stratechery/index.ts b/lib/routes/stratechery/index.ts index 613192c951d1..21cdfba0857d 100644 --- a/lib/routes/stratechery/index.ts +++ b/lib/routes/stratechery/index.ts @@ -3,7 +3,9 @@ import buildData from '@/utils/common-config'; export const route: Route = { path: '/', - name: 'Unknown', + categories: ['blog'], + example: '/stratechery', + name: 'Blog', maintainers: ['chazeon'], handler, }; diff --git a/lib/routes/subhd/index.ts b/lib/routes/subhd/index.ts index b836ff7172db..a6706d2d06d0 100644 --- a/lib/routes/subhd/index.ts +++ b/lib/routes/subhd/index.ts @@ -2,37 +2,41 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -const config = { - sub: { - title: '字幕', - category: 'new', - }, - zu: { - title: '字幕组', - category: '14', - }, - newest: { - category: 'for backwards compatibility', - }, +const defaultCategories = { + sub: 'new', + zu: '14', }; export const route: Route = { - path: '/:type?/:category?', - name: 'Unknown', - maintainers: [], + path: '/sub/:category?', + categories: ['multimedia'], + example: '/subhd/sub/new', + parameters: { category: '分类,见下表,默认为最新' }, + radar: [ + { + source: ['subhd.tv/sub/:category', 'subhd.tv/'], + target: '/sub/:category?', + }, + ], + name: '字幕', + description: `| 最新字幕 | 热门字幕 | 剧集字幕 | 电影字幕 | +| -------- | -------- | -------- | -------- | +| new | top | tv | movie |`, + maintainers: ['laampui', 'nczitzk'], handler, }; -async function handler(ctx) { - const type = ctx.req.param('type') ?? 'sub'; - const category = ctx.req.param('category') ?? config[type].category; +export async function handler(ctx) { + const type = getSubPath(ctx).split('/', 2)[1]; + const category = ctx.req.param('category') ?? defaultCategories[type]; const rootUrl = 'https://subhd.tv'; - const currentUrl = `${rootUrl}/${type === 'newest' ? 'sub/new' : `${type}/${category}${type === 'zu' ? '/l' : ''}`}`; + const currentUrl = `${rootUrl}/${type}/${category}${type === 'zu' ? '/l' : ''}`; const response = await got({ method: 'get', diff --git a/lib/routes/subhd/zu.ts b/lib/routes/subhd/zu.ts new file mode 100644 index 000000000000..84a9397ca1cc --- /dev/null +++ b/lib/routes/subhd/zu.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/zu/:category?', + categories: ['multimedia'], + example: '/subhd/zu/14', + parameters: { category: '字幕组,见下表,默认为 YYeTs字幕组' }, + radar: [ + { + source: ['subhd.tv/zu/:category', 'subhd.tv/'], + target: '/zu/:category?', + }, + ], + name: '字幕组', + description: `| YYeTs 字幕组 | F.I.X 字幕侠 | 深影字幕组 | 擦枪字幕组 | 哒哒字幕组 | 迪幻字幕组 | 伊甸园字幕组 | H-SGDK 字幕组 | 蓝血字幕组 | GA 字幕组 | CC 标准电影字幕组 | NEW 字幕组 | Orange 字幕组 | 圣城家园 SCG 字幕组 | 纪录片之家字幕组 | +| ------------ | ------------ | ---------- | ---------- | ---------- | ---------- | ------------ | ------------- | ---------- | --------- | ----------------- | ---------- | ------------- | ------------------- | ---------------- | +| 14 | 28 | 2 | 118 | 132 | 20 | 1 | 18 | 71 | 11 | 75 | 130 | 66 | 19 | 10 |`, + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/supchina/index.ts b/lib/routes/supchina/index.ts index 984ac98d5e04..2bcc155f0ce8 100644 --- a/lib/routes/supchina/index.ts +++ b/lib/routes/supchina/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['new-media'], + example: '/supchina', radar: [ { source: ['supchina.com/feed', 'supchina.com/'], target: '', }, ], - name: 'Unknown', + name: 'Feed', maintainers: ['nczitzk'], handler, url: 'supchina.com/feed', diff --git a/lib/routes/sysu/cse.ts b/lib/routes/sysu/cse.ts index 39ca483dbc9f..f2fb55f4f0c6 100644 --- a/lib/routes/sysu/cse.ts +++ b/lib/routes/sysu/cse.ts @@ -22,7 +22,7 @@ export const route: Route = { }, ], name: '数据科学与计算机学院动态', - maintainers: [], + maintainers: ['MegrezZhu', 'Neutrino3316', 'nczitzk'], handler, url: 'cse.sysu.edu.cn/', }; diff --git a/lib/routes/tableau/viz-of-the-day.ts b/lib/routes/tableau/viz-of-the-day.ts index fc7976fb2846..08d54c5532d9 100644 --- a/lib/routes/tableau/viz-of-the-day.ts +++ b/lib/routes/tableau/viz-of-the-day.ts @@ -17,7 +17,7 @@ export const route: Route = { supportScihub: false, }, name: 'Viz of the day', - maintainers: [], + maintainers: ['KaiyoungYu'], handler, }; diff --git a/lib/routes/techcrunch/news.ts b/lib/routes/techcrunch/news.ts index ab5dad34a30b..0ee3006dae81 100644 --- a/lib/routes/techcrunch/news.ts +++ b/lib/routes/techcrunch/news.ts @@ -26,7 +26,7 @@ export const route: Route = { }, ], name: 'News', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, url: 'techcrunch.com/', }; diff --git a/lib/routes/tencent/news/coronavirus/data.tsx b/lib/routes/tencent/news/coronavirus/data.tsx index 392a04c89d18..00b9b655894c 100644 --- a/lib/routes/tencent/news/coronavirus/data.tsx +++ b/lib/routes/tencent/news/coronavirus/data.tsx @@ -8,7 +8,13 @@ import { getData } from './utils'; export const route: Route = { path: '/news/coronavirus/data/:province?/:city?', - name: 'Unknown', + categories: ['other'], + example: '/tencent/news/coronavirus/data/湖北/武汉', + parameters: { + province: '省/直辖市名,缺省则返回国内数据', + city: '城市名,缺省则返回全省数据。直辖市请使用区/县名。', + }, + name: '新型冠状病毒肺炎疫情实时追踪 - 省市疫情数据', maintainers: ['CaoMeiYouRen'], handler, }; diff --git a/lib/routes/tencent/news/coronavirus/total.tsx b/lib/routes/tencent/news/coronavirus/total.tsx index e516209a0e7a..8d889c7af0b1 100644 --- a/lib/routes/tencent/news/coronavirus/total.tsx +++ b/lib/routes/tencent/news/coronavirus/total.tsx @@ -7,12 +7,14 @@ import { getData } from './utils'; export const route: Route = { path: '/news/coronavirus/total', + categories: ['other'], + example: '/tencent/news/coronavirus/total', radar: [ { source: ['new.qq.com/zt2020/page/feiyan.htm'], }, ], - name: 'Unknown', + name: '新型冠状病毒肺炎疫情实时追踪 - 中国本土数据统计', maintainers: ['CaoMeiYouRen'], handler, url: 'new.qq.com/zt2020/page/feiyan.htm', diff --git a/lib/routes/theatlantic/news.ts b/lib/routes/theatlantic/news.ts index 0fb9ab83296f..8bdfb81d4aab 100644 --- a/lib/routes/theatlantic/news.ts +++ b/lib/routes/theatlantic/news.ts @@ -24,7 +24,7 @@ export const route: Route = { }, ], name: 'News', - maintainers: ['EthanWng97', 'pseudoyu'], + maintainers: ['IvanWng97', 'pseudoyu'], handler, description: `| Popular | Latest | Politics | Technology | Business | | ------------ | ------ | -------- | ---------- | -------- | diff --git a/lib/routes/thegadgetflow/rss.tsx b/lib/routes/thegadgetflow/rss.tsx index 36f80174ce21..f2dc55636fb9 100644 --- a/lib/routes/thegadgetflow/rss.tsx +++ b/lib/routes/thegadgetflow/rss.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'Category', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, }; diff --git a/lib/routes/thenewslens/author.ts b/lib/routes/thenewslens/author.ts new file mode 100644 index 000000000000..b501d6b89b4d --- /dev/null +++ b/lib/routes/thenewslens/author.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/author/:id/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/author/BBC', + parameters: { id: '作者 id,可在对应作者页 URL 中找到', sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/author/:id/:sort?', 'thenewslens.com/'], + target: '/author/:id/:sort?', + }, + ], + name: '作者', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/category.ts b/lib/routes/thenewslens/category.ts new file mode 100644 index 000000000000..5437138ae8c1 --- /dev/null +++ b/lib/routes/thenewslens/category.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/category/:id/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/category/politics', + parameters: { id: '分类 id,可在对应分类页 URL 中找到', sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/category/:id/:sort?', 'thenewslens.com/'], + target: '/category/:id/:sort?', + }, + ], + name: '分类', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/channel.ts b/lib/routes/thenewslens/channel.ts new file mode 100644 index 000000000000..40736e896930 --- /dev/null +++ b/lib/routes/thenewslens/channel.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/channel/:id/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/channel/hk', + parameters: { id: '标签 id,可在对应标签页 URL 中找到', sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/channel/:id/:sort?', 'thenewslens.com/'], + target: '/channel/:id/:sort?', + }, + ], + name: '频道', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/index.ts b/lib/routes/thenewslens/index.ts index 7d37877a012e..ce307439fabf 100644 --- a/lib/routes/thenewslens/index.ts +++ b/lib/routes/thenewslens/index.ts @@ -9,13 +9,25 @@ import { parseDate } from '@/utils/parse-date'; import { renderDescription } from './templates/description'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/latest-article/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/latest-article', + parameters: { sort: '排序方式,见下表,可在对应排序页 URL 中找到' }, + description: `| 最新文章 | 最多觀看 | 最多分享 | 本日 | 本週 | 本月 | 今年 | 去年 | 有史以來 | +| -------- | -------- | -------- | --------- | -------- | --------- | -------- | ------------ | ----------- | +| | hot | social | hot/today | hot/week | hot/month | hot/year | hot/lastYear | hot/history |`, + radar: [ + { + source: ['thenewslens.com/latest-article/:sort?', 'thenewslens.com/'], + target: '/latest-article/:sort?', + }, + ], + name: '最新', + maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { +export async function handler(ctx) { const rootUrl = 'https://www.thenewslens.com'; const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '/latest-article' : getSubPath(ctx)}`; diff --git a/lib/routes/thenewslens/news.ts b/lib/routes/thenewslens/news.ts new file mode 100644 index 000000000000..a73df3f4e0c4 --- /dev/null +++ b/lib/routes/thenewslens/news.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/news/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/news', + parameters: { sort: '排序方式,见下表,可在对应排序页 URL 中找到' }, + description: `| 最新文章 | 最多觀看 | 最多分享 | +| -------- | -------- | -------- | +| | hot | social |`, + radar: [ + { + source: ['thenewslens.com/news/:sort?', 'thenewslens.com/'], + target: '/news/:sort?', + }, + ], + name: '新闻', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/review.ts b/lib/routes/thenewslens/review.ts new file mode 100644 index 000000000000..39d285093c79 --- /dev/null +++ b/lib/routes/thenewslens/review.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/review/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/review', + parameters: { sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/review/:sort?', 'thenewslens.com/'], + target: '/review/:sort?', + }, + ], + name: '评论', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/tag.ts b/lib/routes/thenewslens/tag.ts new file mode 100644 index 000000000000..ffd23e4dbcf9 --- /dev/null +++ b/lib/routes/thenewslens/tag.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/tag/:id/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/tag/中國', + parameters: { id: '标签 id,可在对应标签页 URL 中找到', sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/tag/:id/:sort?', 'thenewslens.com/'], + target: '/tag/:id/:sort?', + }, + ], + name: '标签', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thenewslens/videos.ts b/lib/routes/thenewslens/videos.ts new file mode 100644 index 000000000000..ad2809218dbd --- /dev/null +++ b/lib/routes/thenewslens/videos.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/videos/Projects/:sort{.+}?', + categories: ['new-media'], + example: '/thenewslens/videos/Projects', + parameters: { sort: '排序方式,同上表,可在对应排序页 URL 中找到' }, + radar: [ + { + source: ['thenewslens.com/videos/Projects/:sort?'], + target: '/videos/Projects/:sort?', + }, + ], + name: '影音', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/thepaper/839studio/category.ts b/lib/routes/thepaper/839studio/category.ts index f58ec4409121..7791bfe9f0ae 100644 --- a/lib/routes/thepaper/839studio/category.ts +++ b/lib/routes/thepaper/839studio/category.ts @@ -5,6 +5,12 @@ import got from '@/utils/got'; export const route: Route = { path: '/839studio/:id', + categories: ['traditional-media'], + example: '/thepaper/839studio/2', + parameters: { id: '分类 id,默认订阅全部分类' }, + description: `| 视频 | 交互 | 信息图 | 数据故事 | +| ---- | ---- | ------ | -------- | +| 2 | 4 | 3 | 453 |`, radar: [ { source: ['thepaper.cn/'], diff --git a/lib/routes/thepaper/839studio/studio.ts b/lib/routes/thepaper/839studio/studio.ts index 76500f97868d..d97e5abe04f8 100644 --- a/lib/routes/thepaper/839studio/studio.ts +++ b/lib/routes/thepaper/839studio/studio.ts @@ -5,6 +5,8 @@ import got from '@/utils/got'; export const route: Route = { path: '/839studio', + categories: ['traditional-media'], + example: '/thepaper/839studio', name: '澎湃美数课作品集', maintainers: ['umm233'], handler, diff --git a/lib/routes/tingshuitz/wuhan.ts b/lib/routes/tingshuitz/wuhan.ts index 415452b97ac1..673fa2651d8b 100644 --- a/lib/routes/tingshuitz/wuhan.ts +++ b/lib/routes/tingshuitz/wuhan.ts @@ -6,14 +6,21 @@ const baseUrl = 'https://www.whwater.com'; export const route: Route = { path: '/wuhan/:channelId?', + categories: ['forecast'], + example: '/tingshuitz/wuhan', + parameters: { channelId: '分类,见下表,默认为 68' }, + description: `| channelId | 分类 | +| --------- | ---------- | +| 68 | 计划性停水 | +| 69 | 突发性停水 |`, radar: [ { source: ['whwater.com/IWater.shtml', 'whwater.com/'], target: '/wuhan', }, ], - name: 'Unknown', - maintainers: [], + name: '武汉市', + maintainers: ['MoonBegonia'], handler, url: 'whwater.com/IWater.shtml', }; diff --git a/lib/routes/tokeninsight/bulletin.ts b/lib/routes/tokeninsight/bulletin.ts index fe39c6dcfdaf..7b29c563cb76 100644 --- a/lib/routes/tokeninsight/bulletin.ts +++ b/lib/routes/tokeninsight/bulletin.ts @@ -35,7 +35,7 @@ export const route: Route = { }, ], name: 'Latest', - maintainers: [], + maintainers: ['fuergaosi233'], handler, }; diff --git a/lib/routes/tokeninsight/report.ts b/lib/routes/tokeninsight/report.ts index 7e26add65462..7e927d133df6 100644 --- a/lib/routes/tokeninsight/report.ts +++ b/lib/routes/tokeninsight/report.ts @@ -29,7 +29,7 @@ export const route: Route = { }, ], name: 'Research', - maintainers: [], + maintainers: ['fuergaosi233'], handler, description: `Language: diff --git a/lib/routes/toodaylab/column.ts b/lib/routes/toodaylab/column.ts new file mode 100644 index 000000000000..20edd922c92b --- /dev/null +++ b/lib/routes/toodaylab/column.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/column/:id', + categories: ['new-media'], + example: '/toodaylab/column/299', + parameters: { id: '专栏 id,见下表,可在对应专栏页 URL 中找到' }, + radar: [ + { + source: ['toodaylab.com/column/:id'], + target: '/column/:id', + }, + ], + name: '专栏', + description: `| 专题 | 攻略 | +| ---- | ---- | +| 299 | 300 |`, + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/toodaylab/field.ts b/lib/routes/toodaylab/field.ts new file mode 100644 index 000000000000..553b728bf88c --- /dev/null +++ b/lib/routes/toodaylab/field.ts @@ -0,0 +1,22 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/field/:id', + categories: ['new-media'], + example: '/toodaylab/field/308', + parameters: { id: '领域 id,见下表,可在对应领域页 URL 中找到' }, + radar: [ + { + source: ['toodaylab.com/field/:id'], + target: '/field/:id', + }, + ], + name: '领域', + description: `| 快消 | 时尚 | 智能 | 娱乐 | 运动 | 生活 | 设计 | 出行 | +| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | +| 308 | 307 | 306 | 305 | 304 | 303 | 302 | 301 |`, + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/toodaylab/hot.ts b/lib/routes/toodaylab/hot.ts new file mode 100644 index 000000000000..628c266cb7cb --- /dev/null +++ b/lib/routes/toodaylab/hot.ts @@ -0,0 +1,18 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/hot', + categories: ['new-media'], + example: '/toodaylab/hot', + radar: [ + { + source: ['toodaylab.com/posts'], + target: '/hot', + }, + ], + name: '最热', + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/toodaylab/index.ts b/lib/routes/toodaylab/index.ts index 5c99f49c1967..1960d9626056 100644 --- a/lib/routes/toodaylab/index.ts +++ b/lib/routes/toodaylab/index.ts @@ -2,25 +2,34 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/:params{.+}?', - name: 'Unknown', - maintainers: [], + path: '/posts', + categories: ['new-media'], + example: '/toodaylab/posts', + radar: [ + { + source: ['toodaylab.com/posts'], + target: '/posts', + }, + ], + name: '滚动', + maintainers: ['nczitzk'], handler, }; -async function handler(ctx) { - const { params = 'posts' } = ctx.req.param(); +export async function handler(ctx) { + const path = getSubPath(ctx); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; - const isHot = params === 'hot'; + const isHot = path === '/hot'; const rootUrl = 'https://www.toodaylab.com'; - const currentUrl = new URL(isHot ? 'posts' : params, rootUrl).href; + const currentUrl = new URL(isHot ? '/posts' : path, rootUrl).href; const { data: response } = await got(currentUrl); diff --git a/lib/routes/toodaylab/topic.ts b/lib/routes/toodaylab/topic.ts new file mode 100644 index 000000000000..e804c2b054c2 --- /dev/null +++ b/lib/routes/toodaylab/topic.ts @@ -0,0 +1,25 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/topic/:id', + categories: ['new-media'], + example: '/toodaylab/topic/309', + parameters: { id: '话题 id,见下表,可在对应话题页 URL 中找到' }, + features: { + antiCrawler: true, + }, + radar: [ + { + source: ['toodaylab.com/topic/:id'], + target: '/topic/:id', + }, + ], + name: '话题', + description: `| 今日消费资讯 | 实验室带你过周末 | 实验室带你过假期 | 每日一图 | 每周一书 | 实验室数字 | 新鲜社会人 | 实验室 TV | +| ------------ | ---------------- | ---------------- | -------- | -------- | ---------- | ---------- | --------- | +| 309 | 37 | 40 | 32 | 33 | 310 | 316 | 476 |`, + maintainers: ['nczitzk'], + handler, +}; diff --git a/lib/routes/tradingview/blog.ts b/lib/routes/tradingview/blog.ts index fa8908c9dd57..5da34c01d527 100644 --- a/lib/routes/tradingview/blog.ts +++ b/lib/routes/tradingview/blog.ts @@ -10,9 +10,57 @@ import { renderDescription } from './templates/description'; export const route: Route = { path: '/blog/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['program-update'], + example: '/tradingview/blog/en', + parameters: { + category: 'Language, see below, `en` as English by default', + }, + name: 'Blog', + maintainers: ['nczitzk'], handler, + description: `#### Language + +| Id | Language | +| -- | ------------------- | +| en | English | +| ru | Русский | +| ja | 日本語 | +| es | Español | +| tr | Türkçe | +| ko | 한국어 | +| it | Italiano | +| pt | Português do Brasil | +| de | Deutsch | +| fr | Français | +| pl | Polski | +| id | Bahasa Indonesia | +| my | Bahasa Malaysia | +| tw | 繁體 | +| cn | 简体 | +| vi | Tiếng Việt | +| th | ภาษาไทย | +| sv | Svenska | +| ar | العربية | +| il | Hebrew | + +#### Category + +| Category | ID | +| ---------------------------------------------------------------------------------------------- | ----------------------------- | +| [Alerts](https://www.tradingview.com/blog/en/category/alerts/) | category/alerts | +| [Bitcoin and Crypto](https://www.tradingview.com/blog/en/category/bitcoin-charts/) | category/bitcoin-charts | +| [Business Updates](https://www.tradingview.com/blog/en/category/business-updates/) | category/business-updates | +| [Charting](https://www.tradingview.com/blog/en/category/charts/) | category/charts | +| [Charting Library](https://www.tradingview.com/blog/en/category/charting-library/) | category/charting-library | +| [Data Feeds and Exchanges](https://www.tradingview.com/blog/en/category/data-feeds-exchanges/) | category/data-feeds-exchanges | +| [Desktop](https://www.tradingview.com/blog/en/category/desktop/) | category/desktop | +| [Market Analysis](https://www.tradingview.com/blog/en/category/market-analysis/) | category/market-analysis | +| [Mobile](https://www.tradingview.com/blog/en/category/mobile/) | category/mobile | +| [Pine Script®](https://www.tradingview.com/blog/en/category/pine/) | category/pine | +| [Screener](https://www.tradingview.com/blog/en/category/stock-screener/) | category/stock-screener | +| [Social](https://www.tradingview.com/blog/en/category/social/) | category/social | +| [Trading and Brokerage](https://www.tradingview.com/blog/en/category/trading/) | category/trading | +| [Widgets](https://www.tradingview.com/blog/en/category/widgets/) | category/widgets |`, }; async function handler(ctx) { diff --git a/lib/routes/tradingview/pine.ts b/lib/routes/tradingview/pine.ts index c95b76ac514a..a4fbf357040e 100644 --- a/lib/routes/tradingview/pine.ts +++ b/lib/routes/tradingview/pine.ts @@ -6,15 +6,22 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/pine/:version?', + categories: ['program-update'], + example: '/tradingview/pine', + parameters: { + version: 'Version, see below, `v5` by default', + }, radar: [ { source: ['tradingview.com/pine-script-docs/en/:version/Release_notes.html'], target: '/pine/:version', }, ], - name: 'Unknown', - maintainers: [], + name: 'Pine Script™ Release notes', + maintainers: ['nczitzk'], handler, + description: `| v5 | v4 | +| -- | -- |`, }; async function handler(ctx) { diff --git a/lib/routes/transcriptforest/index.ts b/lib/routes/transcriptforest/index.ts index 69db12e50141..44c2632be951 100644 --- a/lib/routes/transcriptforest/index.ts +++ b/lib/routes/transcriptforest/index.ts @@ -21,16 +21,76 @@ const bakeTimestamp = (seconds) => { export const route: Route = { path: '/:channel?', + categories: ['multimedia'], + example: '/transcriptforest/all-the-hacks', + parameters: { + channel: 'Channel, see below, all by default', + }, + features: { + supportPodcast: true, + }, radar: [ { source: ['www.transcriptforest.com/en/channel'], target: '', }, ], - name: 'Unknown', + name: 'Channel', maintainers: ['nczitzk'], handler, url: 'www.transcriptforest.com/en/channel', + description: `| Channel | ID | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| [All](https://www.transcriptforest.com/en) | | +| [a16z podcast](https://www.transcriptforest.com/en/channel/a16z-podcast) | [a16z-podcast](https://rsshub.app/transcriptforest/a16z-podcast) | +| [Aarthi and Sriram's Good Time Show](https://www.transcriptforest.com/en/channel/aarthi-and-srirams-good-time-show) | [aarthi-and-srirams-good-time-show](https://rsshub.app/transcriptforest/aarthi-and-srirams-good-time-show) | +| [Acquired](https://www.transcriptforest.com/en/channel/acquired) | [acquired](https://rsshub.app/transcriptforest/acquired) | +| [All-In with Chamath, Jason, Sacks & Friedberg](https://www.transcriptforest.com/en/channel/all-in-with-chamath-jason-sacks-friedberg) | [all-in-with-chamath-jason-sacks-friedberg](https://rsshub.app/transcriptforest/all-in-with-chamath-jason-sacks-friedberg) | +| [All the Hacks](https://www.transcriptforest.com/en/channel/all-the-hacks) | [all-the-hacks](https://rsshub.app/transcriptforest/all-the-hacks) | +| [Breaking Points](https://www.transcriptforest.com/en/channel/breaking-points) | [breaking-points](https://rsshub.app/transcriptforest/breaking-points) | +| [Cartoon Avatars](https://www.transcriptforest.com/en/channel/cartoon-avatars) | [cartoon-avatars](https://rsshub.app/transcriptforest/cartoon-avatars) | +| [Conversations With Coleman](https://www.transcriptforest.com/en/channel/conversations-with-coleman) | [conversations-with-coleman](https://rsshub.app/transcriptforest/conversations-with-coleman) | +| [CSPI Podcast](https://www.transcriptforest.com/en/channel/cspi-podcast) | [cspi-podcast](https://rsshub.app/transcriptforest/cspi-podcast) | +| [Culpable](https://www.transcriptforest.com/en/channel/culpable) | [culpable](https://rsshub.app/transcriptforest/culpable) | +| [Dateline NBC](https://www.transcriptforest.com/en/channel/dateline-nbc) | [dateline-nbc](https://rsshub.app/transcriptforest/dateline-nbc) | +| [Execs](https://www.transcriptforest.com/en/channel/execs) | [execs](https://rsshub.app/transcriptforest/execs) | +| [Exponent](https://www.transcriptforest.com/en/channel/exponent) | [exponent](https://rsshub.app/transcriptforest/exponent) | +| [Freakonomics](https://www.transcriptforest.com/en/channel/freakonomics) | [freakonomics](https://rsshub.app/transcriptforest/freakonomics) | +| [Future of StoryTelling](https://www.transcriptforest.com/en/channel/future-of-storytelling) | [future-of-storytelling](https://rsshub.app/transcriptforest/future-of-storytelling) | +| [Gamecraft](https://www.transcriptforest.com/en/channel/gamecraft) | [gamecraft](https://rsshub.app/transcriptforest/gamecraft) | +| [Get WIRED](https://www.transcriptforest.com/en/channel/get-wired) | [get-wired](https://rsshub.app/transcriptforest/get-wired) | +| [Greymatter](https://www.transcriptforest.com/en/channel/greymatter) | [greymatter](https://rsshub.app/transcriptforest/greymatter) | +| [How I built this](https://www.transcriptforest.com/en/channel/how-I-built-this) | [how-I-built-this](https://rsshub.app/transcriptforest/how-I-built-this) | +| [Huberman Lab](https://www.transcriptforest.com/en/channel/huberman-lab) | [huberman-lab](https://rsshub.app/transcriptforest/huberman-lab) | +| [ICYMI](https://www.transcriptforest.com/en/channel/icymi) | [icymi](https://rsshub.app/transcriptforest/icymi) | +| [In Machines We Trust](https://www.transcriptforest.com/en/channel/in-machines-we-trust) | [in-machines-we-trust](https://rsshub.app/transcriptforest/in-machines-we-trust) | +| [Invest Like the Best with Patrick O'Shaughnessy](https://www.transcriptforest.com/en/channel/invest-like-the-best-with-patrick-o-shaughnessy) | [invest-like-the-best-with-patrick-o-shaughnessy](https://rsshub.app/transcriptforest/invest-like-the-best-with-patrick-o-shaughnessy) | +| [Joe Rogan Experience Review podcast](https://www.transcriptforest.com/en/channel/joe-rogan-experience-review-podcast) | [joe-rogan-experience-review-podcast](https://rsshub.app/transcriptforest/joe-rogan-experience-review-podcast) | +| [Land of Giants](https://www.transcriptforest.com/en/channel/land-of-giants) | [land-of-giants](https://rsshub.app/transcriptforest/land-of-giants) | +| [Lenny's Podcast: Product \\| Growth \\| Career](https://www.transcriptforest.com/en/channel/lenny-podcast-product-growth-career) | [lenny-podcast-product-growth-career](https://rsshub.app/transcriptforest/lenny-podcast-product-growth-career) | +| [Lex Fridman Podcast](https://www.transcriptforest.com/en/channel/lex-fridman-podcast) | [lex-fridman-podcast](https://rsshub.app/transcriptforest/lex-fridman-podcast) | +| [Making Sense with Sam Harris](https://www.transcriptforest.com/en/channel/making-sense-with-sam-harris) | [making-sense-with-sam-harris](https://rsshub.app/transcriptforest/making-sense-with-sam-harris) | +| [Masters of Scale](https://www.transcriptforest.com/en/channel/masters-of-scale) | [masters-of-scale](https://rsshub.app/transcriptforest/masters-of-scale) | +| [Modern Wisdom](https://www.transcriptforest.com/en/channel/modern-wisdom) | [modern-wisdom](https://rsshub.app/transcriptforest/modern-wisdom) | +| [Moment of Zen](https://www.transcriptforest.com/en/channel/moment-of-zen) | [moment-of-zen](https://rsshub.app/transcriptforest/moment-of-zen) | +| [Morbid](https://www.transcriptforest.com/en/channel/morbid) | [morbid](https://rsshub.app/transcriptforest/morbid) | +| [My First Million](https://www.transcriptforest.com/en/channel/my-first-million) | [my-first-million](https://rsshub.app/transcriptforest/my-first-million) | +| [Naval](https://www.transcriptforest.com/en/channel/naval) | [naval](https://rsshub.app/transcriptforest/naval) | +| [Newcomer Podcast](https://www.transcriptforest.com/en/channel/newcomer-podcast) | [newcomer-podcast](https://rsshub.app/transcriptforest/newcomer-podcast) | +| [Not investment advice](https://www.transcriptforest.com/en/channel/not-investment-advice) | [not-investment-advice](https://rsshub.app/transcriptforest/not-investment-advice) | +| [Odd Lots](https://www.transcriptforest.com/en/channel/odd-lots) | [odd-lots](https://rsshub.app/transcriptforest/odd-lots) | +| [On with Kara Swisher](https://www.transcriptforest.com/en/channel/on-with-kara-swisher) | [on-with-kara-swisher](https://rsshub.app/transcriptforest/on-with-kara-swisher) | +| [Proof: A True Crime Podcast](https://www.transcriptforest.com/en/channel/proof-a-true-crime-podcast) | [proof-a-true-crime-podcast](https://rsshub.app/transcriptforest/proof-a-true-crime-podcast) | +| [Reply All](https://www.transcriptforest.com/en/channel/reply-all) | [reply-all](https://rsshub.app/transcriptforest/reply-all) | +| [Revisionist History](https://www.transcriptforest.com/en/channel/revisionist-history) | [revisionist-history](https://rsshub.app/transcriptforest/revisionist-history) | +| [Serial](https://www.transcriptforest.com/en/channel/serial-podcast) | [serial-podcast](https://rsshub.app/transcriptforest/serial-podcast) | +| [Slow Burn](https://www.transcriptforest.com/en/channel/slow-burn) | [slow-burn](https://rsshub.app/transcriptforest/slow-burn) | +| [StrictlyVC Download](https://www.transcriptforest.com/en/channel/strictlyvc-download) | [strictlyvc-download](https://rsshub.app/transcriptforest/strictlyvc-download) | +| [Stuff You Should Know:](https://www.transcriptforest.com/en/channel/stuff-you-should-know) | [stuff-you-should-know](https://rsshub.app/transcriptforest/stuff-you-should-know) | +| [Subversive w/Alex Kaschuta](https://www.transcriptforest.com/en/channel/subversive-w-alex-kaschuta) | [subversive-w-alex-kaschuta](https://rsshub.app/transcriptforest/subversive-w-alex-kaschuta) | +| [TED Radio Hour](https://www.transcriptforest.com/en/channel/ted-radio-hour) | [ted-radio-hour](https://rsshub.app/transcriptforest/ted-radio-hour) | +| [The Bootstrapped Founder](https://www.transcriptforest.com/en/channel/the-bootstrapped-founder) | [the-bootstrapped-founder](https://rsshub.app/transcriptforest/the-bootstrapped-founder) | +| [The Boyscast with Ryan Long](https://www.transcriptforest.com/en/channel/the-boyscast-with-ryan-long) | [the-boyscast-with-ryan-long](https://rsshub.app/transcriptforest/the-boyscast-with-ryan-long) |`, }; async function handler(ctx) { diff --git a/lib/routes/tribalfootball/latest.tsx b/lib/routes/tribalfootball/latest.tsx index 6a20aff5285d..f740a83d3fde 100644 --- a/lib/routes/tribalfootball/latest.tsx +++ b/lib/routes/tribalfootball/latest.tsx @@ -23,13 +23,15 @@ const renderDescription = (desc, headerImage) => export const route: Route = { path: '/', + categories: ['new-media'], + example: '/tribalfootball', radar: [ { source: ['tribalfootball.com/'], target: '', }, ], - name: 'Unknown', + name: 'Latest News', maintainers: ['Rongronggg9'], handler, url: 'tribalfootball.com/', diff --git a/lib/routes/tynu/tynu.ts b/lib/routes/tynu/tynu.ts index 99373ee6b286..735beb8a8c91 100644 --- a/lib/routes/tynu/tynu.ts +++ b/lib/routes/tynu/tynu.ts @@ -9,13 +9,15 @@ const baseUrl = 'http://www.tynu.edu.cn'; export const route: Route = { path: '/', + categories: ['university'], + example: '/tynu', radar: [ { source: ['tynu.edu.cn/index/tzgg.htm', 'tynu.edu.cn/index.htm', 'tynu.edu.cn/'], target: '', }, ], - name: 'Unknown', + name: '通知公告', maintainers: ['2PoL'], handler, url: 'tynu.edu.cn/index/tzgg.htm', diff --git a/lib/routes/ulapia/research.ts b/lib/routes/ulapia/research.ts index c79a115a7406..896ac3a7cd5d 100644 --- a/lib/routes/ulapia/research.ts +++ b/lib/routes/ulapia/research.ts @@ -28,7 +28,7 @@ export const route: Route = { }, ], name: '最新研报', - maintainers: [], + maintainers: ['Fatpandac'], handler, url: 'www.ulapia.com/', }; diff --git a/lib/routes/uraaka-joshi/uraaka-joshi.ts b/lib/routes/uraaka-joshi/uraaka-joshi.ts index 98ed55ae74a9..a4794d72b540 100644 --- a/lib/routes/uraaka-joshi/uraaka-joshi.ts +++ b/lib/routes/uraaka-joshi/uraaka-joshi.ts @@ -6,13 +6,15 @@ import playwright from '@/utils/playwright'; export const route: Route = { path: '/', + categories: ['other'], + example: '/uraaka-joshi', radar: [ { source: ['uraaka-joshi.com/'], target: '', }, ], - name: 'Unknown', + name: 'Homepage', maintainers: ['SettingDust', 'Halcao'], handler, url: 'uraaka-joshi.com/', diff --git a/lib/routes/usts/jwch.ts b/lib/routes/usts/jwch.ts index 62bccf666fff..4ba1f0b2ac76 100644 --- a/lib/routes/usts/jwch.ts +++ b/lib/routes/usts/jwch.ts @@ -22,7 +22,7 @@ export const route: Route = { supportScihub: false, }, name: '教务处', - maintainers: [], + maintainers: ['Fatpandac'], handler, description: `| 类型 | 教务动态 | 公告在线 | 选课通知 | | ---- | -------- | -------- | -------- | diff --git a/lib/routes/v1tx/index.ts b/lib/routes/v1tx/index.ts index 4da2bb2f6716..8acb0872e4ad 100644 --- a/lib/routes/v1tx/index.ts +++ b/lib/routes/v1tx/index.ts @@ -7,13 +7,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['blog'], + example: '/v1tx', radar: [ { source: ['v1tx.com/'], target: '', }, ], - name: 'Unknown', + name: '最新文章', maintainers: ['TonyRL'], handler, url: 'v1tx.com/', diff --git a/lib/routes/v2rayshare/index.ts b/lib/routes/v2rayshare/index.ts index 5a8f753ff8c9..833932d006e5 100644 --- a/lib/routes/v2rayshare/index.ts +++ b/lib/routes/v2rayshare/index.ts @@ -4,13 +4,16 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['other'], + example: '/v2rayshare', radar: [ { source: ['v2rayshare.com/'], target: '', }, ], - name: 'Unknown', + name: '免费节点', + description: '获取来自 V2rayShare 的免费节点,可以通过链接复制或下载', maintainers: ['77taibai'], handler, url: 'v2rayshare.com/', diff --git a/lib/routes/vcb-s/index.ts b/lib/routes/vcb-s/index.ts index 1895f826d270..634c07658fdb 100644 --- a/lib/routes/vcb-s/index.ts +++ b/lib/routes/vcb-s/index.ts @@ -9,13 +9,15 @@ const postsAPIUrl = `${rootUrl}/wp-json/wp/v2/posts`; export const route: Route = { path: '/', + categories: ['anime'], + example: '/vcb-s', radar: [ { source: ['vcb-s.com/'], target: '', }, ], - name: 'Unknown', + name: '最新文章', maintainers: ['cxfksword'], handler, url: 'vcb-s.com/', diff --git a/lib/routes/wallpaperhub/index.tsx b/lib/routes/wallpaperhub/index.tsx index 7093c47feb79..680f6ce14bf9 100644 --- a/lib/routes/wallpaperhub/index.tsx +++ b/lib/routes/wallpaperhub/index.tsx @@ -6,13 +6,15 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', + categories: ['picture'], + example: '/wallpaperhub', radar: [ { source: ['wallpaperhub.app/wallpaperhub', 'wallpaperhub.app/'], target: '', }, ], - name: 'Unknown', + name: 'Wallpapers', maintainers: ['nczitzk'], handler, url: 'wallpaperhub.app/wallpaperhub', diff --git a/lib/routes/wdc/download.ts b/lib/routes/wdc/download.ts index f2c66f82ec2d..08147a7bfc7f 100644 --- a/lib/routes/wdc/download.ts +++ b/lib/routes/wdc/download.ts @@ -18,7 +18,7 @@ export const route: Route = { supportScihub: false, }, name: 'Download', - maintainers: [], + maintainers: ['nczitzk'], handler, }; diff --git a/lib/routes/web3caff/index.ts b/lib/routes/web3caff/index.ts index 90f8e9422cce..2fa1d57bef06 100644 --- a/lib/routes/web3caff/index.ts +++ b/lib/routes/web3caff/index.ts @@ -2,22 +2,27 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['new-media'], + example: '/web3caff/zh/archives/category/news_zh', + parameters: { path: '路径,默认为首页' }, + name: '发现', + maintainers: ['nczitzk'], + description: `路径处填写对应页面 URL 中 \`https://web3caff.com/\` 后的字段。下面是一个例子。 + +若订阅 [叙事 - Web3Caff](https://web3caff.com/zh/archives/category/news_zh) 则将对应页面 URL <https://web3caff.com/zh/archives/category/news_zh> 中 \`https://web3caff.com/\` 后的字段 \`zh/archives/category/news_zh\` 作为路径填入。此时路由为 [\`/web3caff/zh/archives/category/news_zh\`](https://rsshub.app/web3caff/zh/archives/category/news_zh)`, handler, }; async function handler(ctx) { - const params = getSubPath(ctx) === '/' ? '' : getSubPath(ctx); + const path = ctx.req.param('path'); const rootUrl = 'https://web3caff.com'; - const currentUrl = `${rootUrl}${params}`; + const currentUrl = path ? `${rootUrl}/${path}` : rootUrl; const response = await got({ method: 'get', diff --git a/lib/routes/wechat/data258.ts b/lib/routes/wechat/data258.ts index 4e1344b90611..c544cb2055e6 100644 --- a/lib/routes/wechat/data258.ts +++ b/lib/routes/wechat/data258.ts @@ -23,15 +23,31 @@ const parsePage = ($item, hyperlinkSelector, timeSelector) => { export const route: Route = { path: '/data258/:id?', + categories: ['new-media'], + example: '/wechat/data258/gh_cbbad4c1d33c', + parameters: { id: '公众号 id 或分类 id,可在公众号页或分类页 URL 中找到;若略去,则抓取首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, radar: [ { source: ['mp.data258.com/', 'mp.data258.com/article/category/:id'], }, ], - name: 'Unknown', + name: '公众号(微阅读来源)', maintainers: ['Rongronggg9'], handler, url: 'mp.data258.com/', + description: `::: warning +由于使用了一些针对反爬的缓解措施,本路由响应较慢。默认只抓取前 5 条,可通过 \`?limit=\` 改变(不推荐,容易被反爬)。 + +该网站使用 IP 甄别访客,且应用严格的每日阅读量限额(约 15 次),请自建并确保正确配置缓存;如使用内存缓存而非 Redis 缓存,请增大缓存容量。该限额足够订阅至少 3 个公众号(假设公众号每日仅更新一次);首页 / 分类页更新相当频繁,不推荐订阅。 +:::`, }; async function handler(ctx) { @@ -81,7 +97,7 @@ async function handler(ctx) { let err; // !!! let RSSHub throw an anti-crawler prompt if the route is empty !!! - /* eslint-disable no-await-in-loop */ + /* oxlint-disable no-await-in-loop */ for (const item of items) { // https://mp.data258.com/wx?id=${id}&t={token}, id is a permanent hex, token is a temporary base64 const cacheId = item.link.match(/id=([\da-f]+)/)[1]; diff --git a/lib/routes/wechat/sogou.ts b/lib/routes/wechat/sogou.ts index ea01b3ffb841..b4e197b5b96c 100644 --- a/lib/routes/wechat/sogou.ts +++ b/lib/routes/wechat/sogou.ts @@ -149,7 +149,7 @@ export const route: Route = { supportScihub: false, }, name: '公众号(搜狗来源)', - maintainers: ['EthanWng97', 'pseudoyu'], + maintainers: ['IvanWng97', 'pseudoyu'], handler, }; diff --git a/lib/routes/wenku8/index.ts b/lib/routes/wenku8/index.ts index 73109a6f1c5d..96a9af64691d 100644 --- a/lib/routes/wenku8/index.ts +++ b/lib/routes/wenku8/index.ts @@ -25,8 +25,22 @@ const cateTitleMap = { export const route: Route = { path: '/:category?', - name: 'Unknown', + categories: ['reading'], + example: '/wenku8/lastupdate', + parameters: { category: '首页分类,见下表,默认为今日更新' }, + features: { + requireConfig: [ + { + name: 'WENKU8_COOKIE', + description: '登陆轻小说文库后的 cookie', + }, + ], + }, + name: '首页分类', maintainers: ['Fatpandac'], + description: `| 今日更新 | 完结全本 | 新书一览 | 动画化作品 | 热门轻小说 | 轻小说列表 | +| :--------: | :------: | :------: | :--------: | :--------: | :---------: | +| lastupdate | fullflag | postdate | anime | allvisit | articlelist |`, handler, }; diff --git a/lib/routes/whu/hyxt.ts b/lib/routes/whu/hyxt.ts index bad55684e08c..76377b8b83f6 100644 --- a/lib/routes/whu/hyxt.ts +++ b/lib/routes/whu/hyxt.ts @@ -8,9 +8,52 @@ import { domain, getMeta, processItems, processMeta } from './util'; export const route: Route = { path: '/hyxt/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/whu/hyxt', + parameters: { category: '分类,见下表,默认为 `tzgg`, 即 **通知公告**' }, + name: '弘毅学堂', + maintainers: ['nczitzk'], handler, + description: `| 新闻动态 | 通知公告 | 学子风采 | 学术论坛 | +| -------- | -------- | -------- | -------- | +| xwdt | tzgg | xzfc | xslt | + +<details> + <summary>更多分类</summary> + +#### 学堂简报 + +| 学堂简报 | +| -------- | +| xtjb | + +#### 人才培养 + +| 人才培养 | 招生工作 | 培养方案 | 科研训练 | 毕业去向 | 学习资源 | +| -------- | --------- | --------- | --------- | --------- | --------- | +| rcpy | rcpy/zsgz | rcpy/pyfa | rcpy/kyxl | rcpy/byqx | rcpy/xxzy | + +#### 学生工作 + +| 学生工作 | 党团建设 | 学术交流 | 书院生活 | 奖助体系 | 事务服务 | +| -------- | --------- | --------- | --------- | --------- | --------- | +| xsgz | xsgz/dtjs | xsgz/xsjl | xsgz/sysh | xsgz/jztx | xsgz/swfw | + +#### 国际合作 + +| 国际合作 | 国际交流 | 交流分享 | +| -------- | --------- | --------- | +| gjhz | gjhz/gjjl | gjhz/jlfx | + +#### 校友风采 + +| 校友风采 | +| -------- | +| xyfc | + +</details> + +此外 route 后可以加上 \`?limit=n\` 的查询参数,表示只获取前 n 条内容;如果不指定默认为 30。`, }; async function handler(ctx) { diff --git a/lib/routes/whu/news.ts b/lib/routes/whu/news.ts index 0a63882c2d6f..5d93ac6f12ab 100644 --- a/lib/routes/whu/news.ts +++ b/lib/routes/whu/news.ts @@ -11,9 +11,9 @@ export const route: Route = { path: '/news/:category{.+}?', categories: ['university'], example: '/whu/news', - parameters: { category: '新闻栏目,可选' }, + parameters: { category: '分类,见下表,默认为 `wdzx/wdyw`, 即 **武大要闻**' }, name: '新闻网', - maintainers: [], + maintainers: ['SChen1024', 'nczitzk'], handler, description: `category 参数可选,范围如下: diff --git a/lib/routes/wiensued/index.ts b/lib/routes/wiensued/index.ts index c29b25015d57..b80788418a21 100644 --- a/lib/routes/wiensued/index.ts +++ b/lib/routes/wiensued/index.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Data, DataItem, Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import ofetch from '@/utils/ofetch'; const FEED_LANGUAGE = 'de' as const; @@ -11,15 +10,16 @@ const BASE_URL = 'https://www.wiensued.at/' as const; export const route: Route = { name: 'Objekte', example: '/wiensued/city=Wien&search=&space-from=30&space-to=100&room-from=2&room-to=4&rent=1&property=1&state[]=inplanung&state[]=inbau&state[]=sofort&state[]=bestand', - path: '*', + path: '/:path{.+}?', + parameters: { path: 'Query parameters and/or the path leading up to the listing, see the description below' }, maintainers: ['sk22'], categories: ['other'], description: `Pass in the parameters (e.g. \`city=Wien&state[]=sofort\`) and/or the path leading up to the listing (e.g. \`wohnen/sofort-verfuegbar\`)`, async handler(ctx) { - // ['', 'wohnen', 'sofort-verfuegbar', 'city=Wien'] - const parts = getSubPath(ctx).split('/'); + // ['wohnen', 'sofort-verfuegbar', 'city=Wien'] + const parts = (ctx.req.param('path') ?? '').split('/'); const subPaths = parts.filter((p) => p.length && !p.includes('=')); if (subPaths.length === 0) { subPaths.push('wohnen'); diff --git a/lib/routes/wmc-bj/publish.tsx b/lib/routes/wmc-bj/publish.tsx index b6df8e65d00e..f465490c020d 100644 --- a/lib/routes/wmc-bj/publish.tsx +++ b/lib/routes/wmc-bj/publish.tsx @@ -8,9 +8,19 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/publish/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['other'], + example: '/wmc-bj/publish/CRA-Reanalysis/2m-Temperature/6-hour/index.html', + parameters: { + category: 'Category, can be found in URL, `CRA-Reanalysis/2m-Temperature/6-hour/index.html` by default', + }, + name: 'Publish', + maintainers: ['nczitzk'], handler, + description: `::: tip +\`category\` is the text after \`publish/\` in the URL. + +eg. The URL for [Monitoring\\_CMA-RA\\_2m-Temperature\\_6-hour](http://www.wmc-bj.net/publish/CRA-Reanalysis/2m-Temperature/6-hour/index.html) is <http://www.wmc-bj.net/publish/CRA-Reanalysis/2m-Temperature/6-hour/index.html>. The \`category\` for route can be represented as [\`/wmc-bj/publish/CRA-Reanalysis/2m-Temperature/6-hour/index.html\`](https://rsshub.app/wmc-bj/publish/CRA-Reanalysis/2m-Temperature/6-hour/index.html). +:::`, }; async function handler(ctx) { diff --git a/lib/routes/worldjournal/index.ts b/lib/routes/worldjournal/index.ts index fd280e0261c5..c7e33bc59c2d 100644 --- a/lib/routes/worldjournal/index.ts +++ b/lib/routes/worldjournal/index.ts @@ -10,14 +10,17 @@ const baseUrl = 'https://www.worldjournal.com'; export const route: Route = { path: '/:path{.+}?', + categories: ['new-media'], + example: '/worldjournal', + parameters: { path: 'URL 中 `/wj/` 後的路徑,預設為 `cate/breaking`' }, radar: [ { source: ['worldjournal.com/wj/*path'], target: '/:path', }, ], - name: 'Unknown', - maintainers: [], + name: '新聞', + maintainers: ['TonyRL'], handler, url: 'worldjournal.com/wj/*path', }; diff --git a/lib/routes/wzu/news.ts b/lib/routes/wzu/news.ts index e73e22618308..7ff41459fec7 100644 --- a/lib/routes/wzu/news.ts +++ b/lib/routes/wzu/news.ts @@ -52,7 +52,13 @@ async function loadContent(link) { export const route: Route = { path: '/news/:type?', - name: 'Unknown', + categories: ['university'], + example: '/wzu/news/0', + parameters: { type: '分类,见下表 默认为`0`' }, + description: `| 温大新闻 | 媒体温大 | 学术温大 | 通知公告 | 招标信息 | 学术公告 | +| :------: | :------: | :------: | :------: | :------: | :------: | +| 0 | 1 | 2 | 3 | 4 | 5 |`, + name: '新闻', maintainers: ['Chandler-Lu'], handler, }; diff --git a/lib/routes/xjtu/dyyy/index.ts b/lib/routes/xjtu/dyyy/index.ts index bcd7451b4a04..274152e5bc33 100644 --- a/lib/routes/xjtu/dyyy/index.ts +++ b/lib/routes/xjtu/dyyy/index.ts @@ -9,8 +9,11 @@ const baseUrl = 'http://www.dyyy.xjtu.edu.cn'; export const route: Route = { path: '/dyyy/:path{.+}', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/xjtu/dyyy/index/xsxx', + parameters: { path: '栏目路径,支持多级,不包括末尾的`.htm`' }, + name: '第一附属医院新闻', + maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/xjtu/international.ts b/lib/routes/xjtu/international.ts index f6af36582d84..d5defa05bcda 100644 --- a/lib/routes/xjtu/international.ts +++ b/lib/routes/xjtu/international.ts @@ -7,8 +7,11 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/international/:subpath{.+}', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/xjtu/international/hzjl', + parameters: { subpath: '栏目路径,支持多级,不包括末尾的`.htm`' }, + name: '国际处通知', + maintainers: ['guitaoliu'], handler, }; diff --git a/lib/routes/xmnn/news.ts b/lib/routes/xmnn/news.ts index 754a900a62f4..e988aa9678da 100644 --- a/lib/routes/xmnn/news.ts +++ b/lib/routes/xmnn/news.ts @@ -8,9 +8,37 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/news/:category{.+}?', - name: 'Unknown', - maintainers: [], + categories: ['traditional-media'], + example: '/xmnn/news/xmxw', + parameters: { category: '分类 id,见下表,默认为厦门新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['news.xmnn.cn/:category'], + target: '/news/:category', + }, + ], + name: '新闻', + maintainers: ['nczitzk'], handler, + description: `| 分类名 | 分类 id | +| ------------ | ------- | +| 厦门新闻发布 | xmxwfb | +| 厦门新闻 | xmxw | +| 本网快报 | bwkb | +| 厦门网眼 | xmwy | +| 福建新闻 | fjxw | +| 国内新闻 | gnxw | +| 国际新闻 | gjxw | +| 台海新闻 | thxw | +| 社会新闻 | shxw |`, }; async function handler(ctx) { diff --git a/lib/routes/xmut/jwc/bkjw.ts b/lib/routes/xmut/jwc/bkjw.ts index 7e17f0a6468e..a413bba4518a 100644 --- a/lib/routes/xmut/jwc/bkjw.ts +++ b/lib/routes/xmut/jwc/bkjw.ts @@ -9,9 +9,15 @@ const xmut = 'https://jwc.xmut.edu.cn'; export const route: Route = { path: '/jwc/bkjw/:category?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/xmut/jwc/bkjw/jxyx', + parameters: { category: '分类如下表' }, + name: '本科生教务处', + maintainers: ['icecliffs'], handler, + description: `| 教学运行 | 综合事务 | 学务管理 | 实践教学 | 教研教改 | +| :------: | :------: | :------: | :------: | :------: | +| jxyx | zhsw | xwgl | sjjx | jyjg |`, }; async function handler(ctx) { diff --git a/lib/routes/xmut/jwc/yjs.ts b/lib/routes/xmut/jwc/yjs.ts index 8d0ee5008cb2..d57485c5ab73 100644 --- a/lib/routes/xmut/jwc/yjs.ts +++ b/lib/routes/xmut/jwc/yjs.ts @@ -9,9 +9,15 @@ const xmut = 'https://yjs.xmut.edu.cn'; export const route: Route = { path: '/jwc/yjjw/:category?', - name: 'Unknown', - maintainers: [], + categories: ['university'], + example: '/xmut/jwc/yjjw/tzgg', + parameters: { category: '分类如下表' }, + name: '研究生处', + maintainers: ['icecliffs'], handler, + description: `| 通知公告 | 新闻动态 | 学术研究 | 工作简讯 | +| :------: | :------: | :------: | :------: | +| tzgg | xwdt | xstj | yjsjw |`, }; async function handler(ctx) { diff --git a/lib/routes/xueqiu/stock-comments.tsx b/lib/routes/xueqiu/stock-comments.tsx index 6cfe9e76cb51..da5a83792287 100644 --- a/lib/routes/xueqiu/stock-comments.tsx +++ b/lib/routes/xueqiu/stock-comments.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: '股票评论', - maintainers: [], + maintainers: ['zytomorrow'], handler, }; diff --git a/lib/routes/xyzrank/hot-episodes-new.ts b/lib/routes/xyzrank/hot-episodes-new.ts new file mode 100644 index 000000000000..a135fe471837 --- /dev/null +++ b/lib/routes/xyzrank/hot-episodes-new.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/hot-episodes-new', + categories: ['multimedia'], + example: '/xyzrank/hot-episodes-new', + radar: [ + { + source: ['xyzrank.com/'], + target: '/hot-episodes-new', + }, + ], + name: '新锐节目', + maintainers: ['nczitzk'], + handler, + url: 'xyzrank.com/', +}; diff --git a/lib/routes/xyzrank/hot-podcasts.ts b/lib/routes/xyzrank/hot-podcasts.ts new file mode 100644 index 000000000000..db42ef8b027e --- /dev/null +++ b/lib/routes/xyzrank/hot-podcasts.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/hot-podcasts', + categories: ['multimedia'], + example: '/xyzrank/hot-podcasts', + radar: [ + { + source: ['xyzrank.com/'], + target: '/hot-podcasts', + }, + ], + name: '热门播客', + maintainers: ['nczitzk'], + handler, + url: 'xyzrank.com/', +}; diff --git a/lib/routes/xyzrank/index.tsx b/lib/routes/xyzrank/index.tsx index 7e519654452f..b834ef5dbce5 100644 --- a/lib/routes/xyzrank/index.tsx +++ b/lib/routes/xyzrank/index.tsx @@ -2,25 +2,28 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; +import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '/:category?', + path: '/', + categories: ['multimedia'], + example: '/xyzrank', radar: [ { source: ['xyzrank.com/'], - target: '', + target: '/', }, ], - name: 'Unknown', - maintainers: [], + name: '热门节目', + maintainers: ['nczitzk'], handler, url: 'xyzrank.com/', }; -async function handler(ctx) { - const category = ctx.req.param('category') ?? ''; +export async function handler(ctx) { + const category = getSubPath(ctx).slice(1); const rootUrl = 'https://xyzrank.com'; const currentUrl = `${rootUrl}/#/${category}`; @@ -32,36 +35,25 @@ async function handler(ctx) { const $ = load(response.data); - response = await got({ - method: 'get', - url: response.data.match(/<script type="module" crossorigin src="(.*?)"><\/script>/)[1], - }); - - const matches = response.data.match(/pI="(.*?)",gI="(.*?)",mI="(.*?)",_I="(.*?)";var/); - const categories = { '': { - url: matches[3], + url: `${rootUrl}/api/episodes`, title: '热门节目', - id: 'hot-episodes', type: 'episodes', }, 'hot-podcasts': { - url: matches[1], + url: `${rootUrl}/api/podcasts`, title: '热门播客', - id: 'full', type: 'podcasts', }, 'hot-episodes-new': { - url: matches[4], + url: `${rootUrl}/api/new-episodes`, title: '新锐节目', - id: 'hot-episodes-new', type: 'episodes', }, 'new-podcasts': { - url: matches[2], + url: `${rootUrl}/api/new-podcasts`, title: '新锐播客', - id: 'new-podcasts', type: 'podcasts', }, }; @@ -73,7 +65,7 @@ async function handler(ctx) { const type = categories[category].type; - const items = response.data.data[type].slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 250).map((item, index) => ({ + const items = response.data.items.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 250).map((item, index) => ({ title: `#${index + 1} ${item.title ?? item.name}`, category: [item.primaryGenreName], author: item.authorsText, diff --git a/lib/routes/xyzrank/new-podcasts.ts b/lib/routes/xyzrank/new-podcasts.ts new file mode 100644 index 000000000000..fa11d1a08c54 --- /dev/null +++ b/lib/routes/xyzrank/new-podcasts.ts @@ -0,0 +1,19 @@ +import type { Route } from '@/types'; + +import { handler } from './index'; + +export const route: Route = { + path: '/new-podcasts', + categories: ['multimedia'], + example: '/xyzrank/new-podcasts', + radar: [ + { + source: ['xyzrank.com/'], + target: '/new-podcasts', + }, + ], + name: '新锐播客', + maintainers: ['nczitzk'], + handler, + url: 'xyzrank.com/', +}; diff --git a/lib/routes/yangtzeu/dongke.ts b/lib/routes/yangtzeu/dongke.ts index e94c72ebdfd0..f5c72ceb727c 100644 --- a/lib/routes/yangtzeu/dongke.ts +++ b/lib/routes/yangtzeu/dongke.ts @@ -2,15 +2,22 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export const route: Route = { - path: '/dongke/*', - name: 'Unknown', - maintainers: [], + path: '/dongke/:path{.+}?', + categories: ['university'], + example: '/yangtzeu/dongke/yqzl/tzgg', + parameters: { path: '路径,默认为学院新闻' }, + description: `路径处填写网址中 \`https://dongke.yangtzeu.edu.cn\` 到末尾 \`.htm\` 之间的部分,默认为学院新闻。 + +如订阅 [院情总览 - 通知公告](https://dongke.yangtzeu.edu.cn/yqzl/tzgg.htm),网址为 \`https://dongke.yangtzeu.edu.cn/yqzl/tzgg.htm\`,截取 \`/yqzl/tzgg\` 作为参数,此时路由为 [\`/yangtzeu/dongke/yqzl/tzgg\`](https://rsshub.app/yangtzeu/dongke/yqzl/tzgg)。 + +若订阅子分类 [学生工作](https://dongke.yangtzeu.edu.cn/xsgz.htm),网址为 \`https://dongke.yangtzeu.edu.cn/xsgz.htm\`。截取 \`https://dongke.yangtzeu.edu.cn\` 到末尾 \`.htm\` 的部分 \`/xsgz\` 作为参数,此时路由为 [\`/yangtzeu/dongke/xsgz\`](https://rsshub.app/yangtzeu/dongke/xsgz)。`, + name: '动物科学学院', + maintainers: ['nczitzk'], handler, }; @@ -18,7 +25,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; const rootUrl = 'https://dongke.yangtzeu.edu.cn'; - const currentUrl = new URL(`${getSubPath(ctx).replace(/^\/dongke/, '') || '/yqzl/xyxw'}.htm`, rootUrl).href; + const currentUrl = new URL(`/${ctx.req.param('path') ?? 'yqzl/xyxw'}.htm`, rootUrl).href; const { data: response } = await got(currentUrl); diff --git a/lib/routes/yoasobi-music/info.tsx b/lib/routes/yoasobi-music/info.tsx index 2711d54f64ed..9870f33ed348 100644 --- a/lib/routes/yoasobi-music/info.tsx +++ b/lib/routes/yoasobi-music/info.tsx @@ -27,7 +27,7 @@ export const route: Route = { }, ], name: 'News & Biography', - maintainers: [], + maintainers: ['Kiotlin'], handler, url: 'www.yoasobi-music.jp/', }; diff --git a/lib/routes/zagg/new-arrivals.tsx b/lib/routes/zagg/new-arrivals.tsx index 94659cacaa24..ab06baebc97b 100644 --- a/lib/routes/zagg/new-arrivals.tsx +++ b/lib/routes/zagg/new-arrivals.tsx @@ -19,7 +19,7 @@ export const route: Route = { supportScihub: false, }, name: 'New Arrivals', - maintainers: ['EthanWng97'], + maintainers: ['IvanWng97'], handler, description: 'For instance, in `https://www.zagg.com/en_us/new-arrivals?brand=164&cat=3038%2C3041`, the query is `brand=164&cat=3038%2C3041`', }; diff --git a/lib/routes/zhibo8/luxiang.ts b/lib/routes/zhibo8/luxiang.ts index 487332f37676..3c6a776287bf 100644 --- a/lib/routes/zhibo8/luxiang.ts +++ b/lib/routes/zhibo8/luxiang.ts @@ -7,13 +7,19 @@ import timezone from '@/utils/timezone'; export const route: Route = { path: '/luxiang/:category?', + categories: ['multimedia'], + example: '/zhibo8/luxiang/nba', + parameters: { category: '分类,见下表,默认为 `nba`' }, radar: [ { source: ['zhibo8.cc/:category/luxiang.htm'], target: '/luxiang/:category', }, ], - name: 'Unknown', + name: '录像', + description: `| NBA | 足球 | +| --- | ----- | +| nba | zuqiu |`, maintainers: ['TonyRL'], handler, }; diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index 843ff87b9b4c..126b5897a92d 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.ts @@ -30,7 +30,7 @@ export const route: Route = { }, ], name: '问题', - maintainers: [], + maintainers: ['xyqfer', 'hacklu'], handler, }; diff --git a/lib/routes/zhuwang/index.ts b/lib/routes/zhuwang/index.ts index 80500f5bd396..ed3dd56835dd 100644 --- a/lib/routes/zhuwang/index.ts +++ b/lib/routes/zhuwang/index.ts @@ -20,7 +20,7 @@ export const route: Route = { }, ], name: '全国今日生猪价格', - maintainers: [], + maintainers: ['importcjj'], handler, url: 'zhujia.zhuwang.cc/', }; diff --git a/lib/routes/zjgtjy/index.ts b/lib/routes/zjgtjy/index.ts index 6e39c92abeff..73b89fd0cd2b 100644 --- a/lib/routes/zjgtjy/index.ts +++ b/lib/routes/zjgtjy/index.ts @@ -4,8 +4,14 @@ import ofetch from '@/utils/ofetch'; export const route: Route = { path: '/:type?', - name: 'Unknown', + categories: ['government'], + example: '/zjgtjy/all', + parameters: { type: '分类名' }, + name: '公告信息', maintainers: ['Fatpandac'], + description: `| 全部公告 | 挂牌公告 | 拍卖公告 | 补充公告 | +| :------: | :------: | :------: | :------: | +| all | gpgg | pmgg | bcgg |`, handler, }; diff --git a/lib/routes/zyshow/index.tsx b/lib/routes/zyshow/index.tsx index e5808afff05c..952c90750bc3 100644 --- a/lib/routes/zyshow/index.tsx +++ b/lib/routes/zyshow/index.tsx @@ -2,20 +2,31 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { - path: '*', - name: 'Unknown', - maintainers: [], + path: '/:path{.+}?', + categories: ['multimedia'], + example: '/zyshow/chongchongchong', + parameters: { path: '综艺 id,综艺详情对应页 URL 中找到' }, + features: { + antiCrawler: true, + }, + name: '综艺', + maintainers: ['pharaoh2012', 'nczitzk'], + description: `地区,见下表,默认为空,即台湾 + +| 台湾 | 韩国 | 大陆 | +| ---- | ---- | ---- | +| | kr | dl |`, handler, }; async function handler(ctx) { const rootUrl = 'http://www.zyshow.net'; - const currentUrl = `${rootUrl}${getSubPath(ctx).replace(/\/$/, '')}/`; + const path = ctx.req.param('path')?.replace(/\/$/, ''); + const currentUrl = `${rootUrl}${path ? `/${path}` : ''}/`; const response = await got({ method: 'get', From 598cd9184fa166116d6a3490ee37db1e0c589671 Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:36:16 +0800 Subject: [PATCH 0452/1190] chore: weekly full routes test --- .github/workflows/test-full-routes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index 0f7fb60ac3d7..63d58e5890cd 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -3,7 +3,7 @@ name: Build assets (Full Routes Test Result) on: workflow_dispatch: schedule: - - cron: '0 0 * * *' + - cron: '0 0 * * 0' jobs: build: From 3645d3411dc3a56feedb9bf82db31e631118d980 Mon Sep 17 00:00:00 2001 From: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:09:27 +0800 Subject: [PATCH 0453/1190] fix(route): lhratings.com research pdf links (#22866) --- lib/routes/lhratings/research.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/routes/lhratings/research.ts b/lib/routes/lhratings/research.ts index df23675ebe72..b44c602d6005 100644 --- a/lib/routes/lhratings/research.ts +++ b/lib/routes/lhratings/research.ts @@ -24,11 +24,10 @@ export const handler = async (ctx: Context): Promise<Data> => { .toArray() .map((el): Element => { const $el: Cheerio<Element> = $(el); - const $aEl: Cheerio<Element> = $el.find('a').first(); const title: string = $el.find('h2').text(); const pubDateStr: string | undefined = $el.find('p').text().split(':', 2)[1]?.trim(); - const linkUrl: string | undefined = $aEl.attr('href') ? new URL($aEl.attr('href') ?? '', baseUrl).href : undefined; + const linkUrl: string | undefined = $el.attr('href') ? new URL($el.attr('href') ?? '', baseUrl).href : undefined; const categoryEls: Array<Cheerio<Element>> = [$el.find('h3').contents()].filter(Boolean); const categories: string[] = [...new Set(categoryEls.map((el) => $(el).text()).filter(Boolean))]; const image: string | undefined = $el.find('div.xylist_img img').attr('src') ? new URL($el.find('div.xylist_img img').attr('src') ?? '', baseUrl).href : undefined; From 764d9247aabffbd77f227b2a1e9fb18c1021c4cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:14:04 +0000 Subject: [PATCH 0454/1190] chore(deps): bump imapflow from 1.6.1 to 1.6.4 (#22869) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.6.1 to 1.6.4. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.6.1...v1.6.4) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.6.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bc8fb93a8d2a..602bf0dc446c 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.6.1", + "imapflow": "1.6.4", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f65595c58b81..30f69ba4ec7d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.6.1 - version: 1.6.1 + specifier: 1.6.4 + version: 1.6.4 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4378,8 +4378,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.6.1: - resolution: {integrity: sha512-lBkh0ZcKeYHDS3sWfxEcD7j24frrrn4wtbe0UUtLqdLwMSvm9Mygx8j7ZtzWgnvqI7V1DkNfcsidJ/qasb7FBA==} + imapflow@1.6.4: + resolution: {integrity: sha512-4KTjkhmyIyeFlTyNuQRQ0pl5LwkBlzoTq2vG9zjF0HqQ0+GIYVaIWq2joOCOXERbyXNI5EuqlyGXQh3NUTqoFQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -9887,7 +9887,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.6.1: + imapflow@1.6.4: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 From 53df037bd7f88d2031282f17371170e8593b9aef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:15:06 +0000 Subject: [PATCH 0455/1190] chore(deps): bump jsdom from 30.0.0 to 30.0.1 (#22870) Bumps [jsdom](https://github.com/jsdom/jsdom) from 30.0.0 to 30.0.1. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](https://github.com/jsdom/jsdom/compare/v30.0.0...v30.0.1) --- updated-dependencies: - dependency-name: jsdom dependency-version: 30.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 602bf0dc446c..ed00f8902aa4 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", - "jsdom": "30.0.0", + "jsdom": "30.0.1", "json-bigint": "1.0.0", "jsonpath-plus": "10.4.0", "jsrsasign": "11.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30f69ba4ec7d..a3825711ef70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,8 +155,8 @@ importers: specifier: 5.0.0 version: 5.0.0 jsdom: - specifier: 30.0.0 - version: 30.0.0(@noble/hashes@2.2.0) + specifier: 30.0.1 + version: 30.0.1(@noble/hashes@2.2.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -469,7 +469,7 @@ importers: version: 7.0.0-alpha.1(@typescript/typescript6@6.0.2)(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.114.0 version: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -4564,8 +4564,8 @@ packages: resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==} engines: {node: '>=20.0.0'} - jsdom@30.0.0: - resolution: {integrity: sha512-JQHfRGmmKmaZoUAvIgff5jjG/0SzTQlGz8c7t72KzBzo8ZULEjAjnYE0sNwBOUA4QtWwYE2xoYitg8NFsmiYxA==} + jsdom@30.0.1: + resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -6673,7 +6673,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: @@ -8496,7 +8496,7 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -10070,7 +10070,7 @@ snapshots: jsdoc-type-pratt-parser@7.2.0: {} - jsdom@30.0.0(@noble/hashes@2.2.0): + jsdom@30.0.1(@noble/hashes@2.2.0): dependencies: '@asamuzakjp/css-color': 6.0.5 '@asamuzakjp/dom-selector': 8.3.0 @@ -12073,7 +12073,7 @@ snapshots: tsx: 4.23.1 yaml: 2.9.0 - vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.0(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) @@ -12099,7 +12099,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@types/node': 26.1.2 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) - jsdom: 30.0.0(@noble/hashes@2.2.0) + jsdom: 30.0.1(@noble/hashes@2.2.0) transitivePeerDependencies: - msw From db69bbda97c01d314c6af69d8d2fd59090e468b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:07:24 +0800 Subject: [PATCH 0456/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 4 updates (#22868) Bumps the cloudflare group with 4 updates in the / directory: [@cloudflare/playwright](https://github.com/cloudflare/playwright/tree/HEAD/packages/playwright-cloudflare), [@cloudflare/vitest-pool-workers](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-pool-workers), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/playwright` from 1.3.2 to 1.3.3 - [Release notes](https://github.com/cloudflare/playwright/releases) - [Commits](https://github.com/cloudflare/playwright/commits/v1.3.3/packages/playwright-cloudflare) Updates `@cloudflare/vitest-pool-workers` from 0.18.8 to 0.19.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-pool-workers/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-pool-workers@0.19.0/packages/vitest-pool-workers) Updates `@cloudflare/workers-types` from 5.20260728.1 to 5.20260729.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.114.0 to 4.115.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.115.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/playwright" dependency-version: 1.3.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/vitest-pool-workers" dependency-version: 0.19.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260729.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.115.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 8 ++++---- pnpm-lock.yaml | 56 +++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index ed00f8902aa4..59679180243a 100644 --- a/package.json +++ b/package.json @@ -147,9 +147,9 @@ "@actions/github": "9.1.1", "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", - "@cloudflare/playwright": "1.3.2", - "@cloudflare/vitest-pool-workers": "0.18.8", - "@cloudflare/workers-types": "5.20260728.1", + "@cloudflare/playwright": "1.3.3", + "@cloudflare/vitest-pool-workers": "0.19.0", + "@cloudflare/workers-types": "5.20260729.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.76.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.1", "vitest": "4.1.10", - "wrangler": "4.114.0", + "wrangler": "4.115.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3825711ef70..18477fff60e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,14 +291,14 @@ importers: specifier: 0.3.7 version: 0.3.7 '@cloudflare/playwright': - specifier: 1.3.2 - version: 1.3.2 + specifier: 1.3.3 + version: 1.3.3 '@cloudflare/vitest-pool-workers': - specifier: 0.18.8 - version: 0.18.8(@cloudflare/workers-types@5.20260728.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + specifier: 0.19.0 + version: 0.19.0(@cloudflare/workers-types@5.20260729.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260728.1 - version: 5.20260728.1 + specifier: 5.20260729.1 + version: 5.20260729.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -471,8 +471,8 @@ importers: specifier: 4.1.10 version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: - specifier: 4.114.0 - version: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.115.0 + version: 4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -585,8 +585,8 @@ packages: resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} engines: {node: '>=22.0.0'} - '@cloudflare/playwright@1.3.2': - resolution: {integrity: sha512-aADRLAUSxOn4jlwIc9YEE08oXJtjvzH/Nw5vyQtUncn2YboEWElk63/oWq2RAaUlidkMco81VzRhfk2jxMzhKQ==} + '@cloudflare/playwright@1.3.3': + resolution: {integrity: sha512-Iz66dgICSjWN1yIZDzLIOCGh8mNkDyvkE6gf1Boyc0oz1J/IHvf3T0C+JEJschDyZRLz2uE7ruVLLqEJOfv1+Q==} peerDependencies: playwright-core: '*' peerDependenciesMeta: @@ -602,8 +602,8 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.18.8': - resolution: {integrity: sha512-O1kOMZqapidlezNFiBZ7Lbd+8mMEpkGmWwPj+nPLvOngxSL11lmWq7xl7vxyjDxbeD/7l22KqgvRGM7XFaYd9w==} + '@cloudflare/vitest-pool-workers@0.19.0': + resolution: {integrity: sha512-6t6zs1YBoAo9jJVpA8pTxPWYEY3c/ai07MWZYJ4vkUc5cyW9PHfMB6tFhDsRg7eLS+i26NMuB3aUOlBKdRiwhw==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 @@ -639,8 +639,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260728.1': - resolution: {integrity: sha512-rZqmesLEH40Xt67PpQSj+iJqwkYBzVr7U/UOa5twkToMsxO5pYsPw2QAaTKDdiCSiki9fWAgsWTj9OmFnbwLRQ==} + '@cloudflare/workers-types@5.20260729.1': + resolution: {integrity: sha512-X5r/4y0gKMq/B72qkz/tEwNK4c3v2regT3to6Ia8qqC66E0+YIN/fU7x0JG6ej2rLxtU3TV3aVhfK9y8+jMMAw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3706,7 +3706,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.52: @@ -4980,8 +4980,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@4.20260722.0: - resolution: {integrity: sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw==} + miniflare@4.20260722.1: + resolution: {integrity: sha512-FJIg4omaCb2wwSyOeRosEdmVRi3JzGAOOH3pa3twmmtvWECl6BVZMIDwJbjByLKRyU+mrfk0M/n3oSiojFZvSA==} engines: {node: '>=22.0.0'} hasBin: true @@ -6374,8 +6374,8 @@ packages: engines: {node: '>=16'} hasBin: true - wrangler@4.114.0: - resolution: {integrity: sha512-M65P25t5UHA1TIJfgZXDcj+YzVobgKdRguM2QPz0xnxLFuOcuE3ErgllDht0iaho7MS4o0g/Bb4YK2+GT+bibg==} + wrangler@4.115.0: + resolution: {integrity: sha512-+upG2VW66M1sjb43yzgUZ6Ss8iYpJ6+7F3U4GF8TY5EYd+08sYdS54d24AEwCnhuef3J9KlSPusqiqR9WYy1UA==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: @@ -6658,7 +6658,7 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/playwright@1.3.2': {} + '@cloudflare/playwright@1.3.3': {} '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1)': dependencies: @@ -6666,15 +6666,15 @@ snapshots: optionalDependencies: workerd: 1.20260722.1 - '@cloudflare/vitest-pool-workers@0.18.8(@cloudflare/workers-types@5.20260728.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.19.0(@cloudflare/workers-types@5.20260729.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260722.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6696,7 +6696,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260722.1': optional: true - '@cloudflare/workers-types@5.20260728.1': {} + '@cloudflare/workers-types@5.20260729.1': {} '@colors/colors@1.6.0': {} @@ -10666,7 +10666,7 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260722.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.2 @@ -12194,18 +12194,18 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260722.1 '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.114.0(@cloudflare/workers-types@5.20260728.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260722.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260722.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 workerd: 1.20260722.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260728.1 + '@cloudflare/workers-types': 5.20260729.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 3a42f0ecbf2de6e8eeccd0cc1bcaa65bf61cceb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:11:31 +0800 Subject: [PATCH 0457/1190] chore(deps): bump @honeybadger-io/js from 6.15.3 to 6.16.0 (#22871) Bumps [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js) from 6.15.3 to 6.16.0. - [Release notes](https://github.com/honeybadger-io/honeybadger-js/releases) - [Commits](https://github.com/honeybadger-io/honeybadger-js/compare/@honeybadger-io/js@6.15.3...@honeybadger-io/js@6.16.0) --- updated-dependencies: - dependency-name: "@honeybadger-io/js" dependency-version: 6.16.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 59679180243a..9c3657498745 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@bbob/plugin-helper": "4.4.1", "@bbob/preset-html5": "4.4.1", "@googleapis/youtube": "33.0.0", - "@honeybadger-io/js": "6.15.3", + "@honeybadger-io/js": "6.16.0", "@hono/node-server": "2.0.12", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18477fff60e1..97a327d3d4ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 33.0.0 version: 33.0.0 '@honeybadger-io/js': - specifier: 6.15.3 - version: 6.15.3 + specifier: 6.16.0 + version: 6.16.0 '@hono/node-server': specifier: 2.0.12 version: 2.0.12(hono@4.12.32) @@ -1091,14 +1091,22 @@ packages: resolution: {integrity: sha512-PW3SdoZ8ULzW0We0lgplyZHjuXJtOdRvU5RL6ErNUlcTeSqjoHtk1pWdHKTwk3brPuIiqUB26K7fX84VroDqRg==} engines: {node: '>=12.0.0'} - '@honeybadger-io/core@6.10.2': - resolution: {integrity: sha512-iLj5fmz6v5+ry3+Guv2WFTxZ9Bz1KJUhBYme/uwQfSKguNH4ylgzOnvvoBX8uGDcGaOGSBu3L9OedP6fi1yGYg==} + '@honeybadger-io/core@6.11.0': + resolution: {integrity: sha512-cIo4VzV7PvQ3urJ3lA9f/sz5hEJavaypgALhhK0Kye+lBXLE1nttsG+W6e30Ii8mygyoRcla0uVgk2pDcTJTvw==} engines: {node: '>=14'} - '@honeybadger-io/js@6.15.3': - resolution: {integrity: sha512-SQxuR9JAAqxQx6uglT6NICIxMGiCw7KmP1ULmUsRKEWQ1yykY+LlMW14DarH591+AEjAehp8EPiBwBH6l4xu8g==} + '@honeybadger-io/js@6.16.0': + resolution: {integrity: sha512-5xGyk6+1W//a+wcHNrR7u0KMAqkoNFtaOCl3RGOtTeUxKFlAmo7jHp90zOmOvrU+Z/NbDB4U8fLhroBw7A028g==} engines: {node: '>=14'} hasBin: true + peerDependencies: + fastify: '>=4' + fastify-plugin: '>=4' + peerDependenciesMeta: + fastify: + optional: true + fastify-plugin: + optional: true '@hono/node-server@2.0.12': resolution: {integrity: sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==} @@ -6994,14 +7002,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@honeybadger-io/core@6.10.2': + '@honeybadger-io/core@6.11.0': dependencies: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.15.3': + '@honeybadger-io/js@6.16.0': dependencies: - '@honeybadger-io/core': 6.10.2 + '@honeybadger-io/core': 6.11.0 '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 From 94069cd48de3da33d43706bc248cc41b99ec74ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:29:36 +0800 Subject: [PATCH 0458/1190] chore(deps): bump devenv from `fa2b4dd` to `ba7318a` (#22872) Bumps [devenv](https://github.com/cachix/devenv) from `fa2b4dd` to `ba7318a`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/fa2b4ddf117bb32add8fb53ebed8d5df74f6afae...ba7318ab409ecbbf0ed79c43e5fbf1b4ec359f7a) --- updated-dependencies: - dependency-name: devenv dependency-version: ba7318ab409ecbbf0ed79c43e5fbf1b4ec359f7a dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/flake.lock b/flake.lock index 090e61fd4e0a..930e2c498f4f 100644 --- a/flake.lock +++ b/flake.lock @@ -13,10 +13,7 @@ "devenv", "git-hooks" ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ] + "nixpkgs": "nixpkgs" }, "locked": { "lastModified": 1777487137, @@ -60,15 +57,15 @@ "git-hooks": "git-hooks", "nix": "nix", "nixd": "nixd", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1785216906, - "narHash": "sha256-0wbVad9KwbSaKlMAFbuI9g3dwOWWhBrLuwYE8y7Y2qc=", + "lastModified": 1785262887, + "narHash": "sha256-z8mS9Q4poqs3zMeOm/PW/FAKfhRYG5TVWkoBSLpQAbo=", "owner": "cachix", "repo": "devenv", - "rev": "fa2b4ddf117bb32add8fb53ebed8d5df74f6afae", + "rev": "ba7318ab409ecbbf0ed79c43e5fbf1b4ec359f7a", "type": "github" }, "original": { @@ -240,21 +237,18 @@ } }, "nixpkgs": { - "inputs": { - "nixpkgs-src": "nixpkgs-src" - }, "locked": { - "lastModified": 1782132010, - "narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=", - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89", + "lastModified": 1772624091, + "narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "80bdc1e5ce51f56b19791b52b2901187931f5353", "type": "github" }, "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", "type": "github" } }, @@ -276,6 +270,25 @@ } }, "nixpkgs_2": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1782132010, + "narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1785090369, "narHash": "sha256-m0pDuRJG7EDo9ri+4Ksu83VsI+PlxNC9lNBfydejce4=", @@ -295,7 +308,7 @@ "inputs": { "devenv": "devenv", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" } }, "rust-overlay": { From 32dd0a7590a06e2b10ba5acfe9e89c6048b91d23 Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:19:59 +0800 Subject: [PATCH 0459/1190] fix(route/zaobao): update selector (#22876) * fix(route/zaobao): update selector * fix(route/zaobao): refine parseList to improve JSON handling and data extraction --- lib/routes/zaobao/util.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/routes/zaobao/util.tsx b/lib/routes/zaobao/util.tsx index b7656da86613..47e6f30f79b5 100644 --- a/lib/routes/zaobao/util.tsx +++ b/lib/routes/zaobao/util.tsx @@ -58,27 +58,24 @@ export const parseList = async ( const response = await ofetch.raw(new URL($item.attr('href') as string, origin).href); let $1 = load(response._data); - let title, pubDate, category, images; - const jsonText = $1('script[type="application/ld+json"]') + let category, images; + const jsonText = $1('script[type="application/ld+json"]:contains("NewsArticle")') .text() .replaceAll(/\p{Cc}/gu, ''); const ldJson = JSON.parse(jsonText); + const title = ldJson.headline; + const pubDate = parseDate(ldJson.datePublished); + const isSingapore = response.url.startsWith('https://www.zaobao.com.sg/'); if (isSingapore) { - const ldJson = JSON.parse($1('#seo-article-page').text()); - const article = ldJson['@graph'].find((item) => item['@type'] === 'NewsArticle'); - title = article.headline; - pubDate = parseDate(article.datePublished); category = $1('meta[name="keywords"]') .attr('content') ?.split(',') .map((s) => s.trim()); $1 = load($1('.articleBody').html(), null, false); - images = [{ url: article.image.url }]; + images = [{ url: ldJson.image[0].url }]; } else { - title = ldJson.headline; - pubDate = parseDate(ldJson.datePublished); category = ldJson.keywords?.split(','); } From 9881f39f301722fdb8dcec1b2b5af629967cd949 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 03:37:47 +0000 Subject: [PATCH 0460/1190] Update VOUCHED list https://github.com/DIYgod/RSSHub/issues/22881#issuecomment-5126120550 --- .github/VOUCHED.td | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index c21361bfb3e5..29874c34ef35 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -17,4 +17,5 @@ neverbehave pseudoyu tonyrl -vizmoe impersonate as pseudoyu in #22801. ae0a44ebb738ace62100f66d15d84272356a72c9 +-zhangsan-xyz impersonate as TonyRL, cscnk52 in #22881. 6e51dceaf55678d0a24361dc1205d4a9e794435f zhenlonghe From 6f50d57298dbc1a9cffc65af1ab123facb65a4b2 Mon Sep 17 00:00:00 2001 From: FlanChan <104259619+FlanChanXwO@users.noreply.github.com> Date: Thu, 30 Jul 2026 12:15:32 +0800 Subject: [PATCH 0461/1190] =?UTF-8?q?feat(route/baidu):=20add=20BAIDU=5FCO?= =?UTF-8?q?OKIE=20support=20and=20extract=20shared=20=E2=80=A6=20(#21663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(route/baidu): add support for BAIDU_COOKIE in various baidu tieba routes and implement user post retrieval * refactor(forum): improve code readability and structure in forum.tsx * feat(post): enhance time parsing and reply link generation in post.tsx * feat(route/baidu): refactor page content retrieval and enhance cookie handling in forum, post, search, and user routes * refactor(route/baidu): refactor cookie parsing logic and improve code formatting - Refactor parseBaiduCookies function to use chained array methods for better readability - Simplify cookie parsing by trimming and filtering empty strings before mapping - Remove redundant comments in common.ts and post.tsx files - Improve code formatting by removing excessive blank lines - Maintain same functionality while enhancing code maintainability * feat(route/baidu/tieba): add BAIDU_COOKIE support and extract shared utilities Add common.ts for cookie parsing, page retrieval, security check and URL normalization. Refactor forum, post, search and user routes to use shared utilities. Preserve rich text content in post replies. Support direct reply links. Fix cookie value parsing for cookies containing '=' character. Route/baidu * style(baidu/tieba): fix code formatting and whitespace issues - Remove trailing whitespace from empty lines - Ensure consistent line endings in utility functions * feat(route/baidu): add retry mechanism for transient errors in page content retrieval (#2) * fix: improve page content retrieval * style: auto format * fix: improve URL encoding and enhance content retrieval logic * feat(route/baidu/tieba): migrate forum route to use client API Replace Puppeteer-based HTML scraping with direct HTTP API call to /c/f/frs/page. The API requires BDUSS + MD5 client signature, eliminating the need for browser rendering and reducing resource usage. Also updated thread parsing to match the JSON response structure (thread_list at top level, author via user_list map, images from first_post_content type 3 items). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * revert: unnecessary changes mentioned in https://github.com/DIYgod/RSSHub/pull/21663#discussion_r3051928062 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> --- lib/config.ts | 7 ++ lib/routes/baidu/tieba/common.ts | 190 ++++++++++++++++++++++++++++++ lib/routes/baidu/tieba/forum.tsx | 130 +++++++++++--------- lib/routes/baidu/tieba/post.tsx | 147 ++++++++++++++--------- lib/routes/baidu/tieba/search.tsx | 86 +++++++++----- lib/routes/baidu/tieba/user.ts | 54 --------- lib/routes/baidu/tieba/user.tsx | 104 ++++++++++++++++ lib/routes/baidu/tieba/utils.ts | 91 ++++++++++++++ 8 files changed, 617 insertions(+), 192 deletions(-) create mode 100644 lib/routes/baidu/tieba/common.ts delete mode 100644 lib/routes/baidu/tieba/user.ts create mode 100644 lib/routes/baidu/tieba/user.tsx create mode 100644 lib/routes/baidu/tieba/utils.ts diff --git a/lib/config.ts b/lib/config.ts index f93bf95cc752..5a7474578619 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -83,6 +83,7 @@ type ConfigEnvKeys = | 'FOLLOW_PRICE' | 'FOLLOW_USER_LIMIT' // Route-specific (dynamic cookies with prefixes) + | 'BAIDU_COOKIE' | `BILIBILI_COOKIE_${string}` | 'BILIBILI_DM_IMG_LIST' | 'BILIBILI_DM_IMG_INTER' @@ -360,6 +361,9 @@ export type Config = { }; // Route-specific Configurations + baidu: { + cookie?: string; + }; bilibili: { cookies: Record<string, string | undefined>; dmImgList?: string; @@ -863,6 +867,9 @@ const calculateValue = () => { }, // Route-specific Configurations + baidu: { + cookie: envs.BAIDU_COOKIE, + }, bilibili: { cookies: bilibili_cookies, dmImgList: envs.BILIBILI_DM_IMG_LIST, diff --git a/lib/routes/baidu/tieba/common.ts b/lib/routes/baidu/tieba/common.ts new file mode 100644 index 000000000000..4f14241c3751 --- /dev/null +++ b/lib/routes/baidu/tieba/common.ts @@ -0,0 +1,190 @@ +import { createHash } from 'node:crypto'; + +import { Cookie } from 'tough-cookie'; + +import { config } from '@/config'; +import ConfigNotFoundError from '@/errors/types/config-not-found'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { getPlaywrightPage } from '@/utils/playwright'; + +/** + * 解析百度 cookie 字符串为 Playwright 可用的 cookie 对象数组 + * 正确处理包含 '=' 的 cookie 值 + */ +export function parseBaiduCookies(cookieStr: string): Array<{ name: string; value: string; domain: string; path: string }> { + return cookieStr + .split(';') + .map((c) => Cookie.parse(c.trim())) + .filter((c): c is Cookie => Boolean(c?.key)) + .map((c) => ({ + name: c.key, + value: c.value, + domain: '.tieba.baidu.com', + path: '/', + })); +} + +/** + * 检查 HTML 内容是否包含百度安全验证页面 + */ +export function checkSecurityVerification(html: string): void { + if (html.includes('安全验证') || html.includes('百度安全验证')) { + throw new Error('Baidu security verification required. The cookie may be expired or invalid. Please update your BAIDU_COOKIE.'); + } +} + +/** + * 使用 Playwright 获取贴吧页面内容 + * 包含统一的 cookie 设置、安全验证检查和缓存逻辑 + * 带有重试机制处理瞬态错误 + */ +export async function getTiebaPageContent( + url: string, + cacheKey: string, + options: { + waitForSelector?: string; + timeout?: number; + retries?: number; + } = {} +): Promise<string> { + const cookie = config.baidu.cookie; + + if (!cookie) { + throw new ConfigNotFoundError('Baidu Tieba RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#baidu">BAIDU_COOKIE</a>'); + } + + const cookies = parseBaiduCookies(cookie); + const { waitForSelector = '.thread-card-wrapper, .virtual-list-item, .thread-content-box, .thread-card', timeout = 3000, retries = 3 } = options; + + const data = await cache.tryGet( + cacheKey, + async () => { + let lastError: Error | undefined; + + /* eslint-disable no-await-in-loop -- Intentional sequential retry logic */ + for (let attempt = 0; attempt < retries; attempt++) { + const { page, destroy } = await getPlaywrightPage(url, { + onBeforeLoad: async (page) => { + if (cookies.length > 0) { + await page.context().addCookies(cookies); + } + }, + gotoConfig: { waitUntil: 'domcontentloaded' }, + }); + + try { + // 等待页面稳定 + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // 动态等待内容加载 + try { + await page.waitForSelector(waitForSelector, { timeout }); + } catch { + // 如果超时,继续执行 + } + + const html = await page.content(); + checkSecurityVerification(html); + return html; + } catch (error) { + lastError = error as Error; + // 如果是最后一次尝试,抛出错误 + if (attempt === retries - 1) { + throw lastError; + } + // 等待后重试 + await new Promise((resolve) => setTimeout(resolve, 1000 * (attempt + 1))); + } finally { + await destroy(); + } + } + /* eslint-enable no-await-in-loop */ + throw lastError || new Error('Failed to fetch page content'); + }, + config.cache.routeExpire, + false + ); + + return data as string; +} + +/** + * 规范化 URL 为绝对地址 + */ +export function normalizeUrl(href: string, base: string = 'https://tieba.baidu.com'): string { + if (!href) { + return ''; + } + if (href.startsWith('http')) { + return href; + } + const path = href.startsWith('/') ? href : `/${href}`; + return `${base}${path}`; +} + +/** + * 通过 /c/f/frs/page API 获取贴吧帖子列表 + * 使用贴吧客户端签名认证,无需 Puppeteer + */ +const TIEBA_CLIENT_SECRET = 'tiebaclient!!!'; + +function computeSign(params: Record<string, string>): string { + // oxlint-disable-next-line unicorn-js/require-array-sort-compare + const sortedKeys = Object.keys(params).toSorted(); + const raw = sortedKeys.map((key) => `${key}=${params[key]}`).join('') + TIEBA_CLIENT_SECRET; + return createHash('md5').update(raw).digest('hex'); +} + +export async function getTiebaForumData(params: { kw: string; cid?: string; isGood?: boolean; sortBy?: string }): Promise<any> { + const cookie = config.baidu.cookie; + if (!cookie) { + throw new ConfigNotFoundError('Baidu Tieba RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#baidu">BAIDU_COOKIE</a>'); + } + + const bduss = cookie.match(/BDUSS=([^;]+)/)?.[1] || ''; + if (!bduss) { + throw new ConfigNotFoundError('BAIDU_COOKIE must contain BDUSS. Please check your cookie configuration.'); + } + + const apiParams: Record<string, string> = { + _client_id: 'wappc_1234567890123_456', + _client_type: '2', + _client_version: '12.20.1.0', + _phone_imei: '000000000000000', + from: 'tieba', + kw: params.kw, + rn: '30', + pn: '1', + BDUSS: bduss, + }; + + if (params.isGood) { + apiParams.is_good = '1'; + } + if (params.cid && params.cid !== '0') { + apiParams.cid = params.cid; + } + if (params.sortBy === 'replied') { + apiParams.sort_type = '1'; + } + + apiParams.sign = computeSign(apiParams); + + const url = 'https://tieba.baidu.com/c/f/frs/page'; + const cacheKey = `tieba:api:forum:${params.kw}:${params.cid || '0'}:${params.sortBy || 'created'}`; + + const data = await cache.tryGet( + cacheKey, + async () => { + const { data: response } = await got.post(url, { + form: apiParams, + }); + return response; + }, + config.cache.routeExpire, + false + ); + + return data; +} diff --git a/lib/routes/baidu/tieba/forum.tsx b/lib/routes/baidu/tieba/forum.tsx index 9433fa3e0154..34ad0bff9f15 100644 --- a/lib/routes/baidu/tieba/forum.tsx +++ b/lib/routes/baidu/tieba/forum.tsx @@ -1,85 +1,109 @@ -import { load } from 'cheerio'; -import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { getTiebaForumData } from './common'; + export const route: Route = { path: ['/tieba/forum/good/:kw/:cid?/:sortBy?', '/tieba/forum/:kw/:sortBy?'], categories: ['bbs'], example: '/baidu/tieba/forum/good/女图', parameters: { kw: '吧名', cid: '精品分类,默认为 `0`(全部分类),如果不传 `cid` 则获取全部分类', sortBy: '排序方式:`created`, `replied`。默认为 `created`' }, features: { - requireConfig: false, + requireConfig: [ + { + name: 'BAIDU_COOKIE', + optional: false, + description: '百度 cookie 值,用于需要登录的贴吧页面', + }, + ], requirePuppeteer: false, - antiCrawler: false, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, }, name: '精品帖子', - maintainers: ['u3u'], + maintainers: ['u3u', 'FlanChanXwO'], handler, }; +function extractContent(items: any[]): { text: string; images: string[] } { + let text = ''; + const images: string[] = []; + if (!Array.isArray(items)) { + return { text, images }; + } + for (const item of items) { + if (Number(item.type) === 0 && item.text) { + text += item.text; + } else if (Number(item.type) === 3) { + const src = item.origin_src || item.original_src || item.big_cdn_src || item.cdn_src || item.src; + if (src) { + images.push(src); + } + } + } + return { text, images }; +} + async function handler(ctx) { - // sortBy: created, replied const { kw, cid = '0', sortBy = 'created' } = ctx.req.param(); + const isGood = ctx.req.path.includes('good'); - // PC端:https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)} - // 移动端接口:https://tieba.baidu.com/mo/q/m?kw=${encodeURIComponent(kw)}&lp=5024&forum_recommend=1&lm=0&cid=0&has_url_param=1&pn=0&is_ajax=1 - const params = { kw: encodeURIComponent(kw) }; - ctx.req.path.includes('good') && (params.tab = 'good'); - cid && (params.cid = cid); - const { data } = await got('https://tieba.baidu.com/f', { - headers: { - Referer: 'https://tieba.baidu.com/', - }, - searchParams: params, - }); + const data = await getTiebaForumData({ kw, cid, isGood, sortBy }); + + if (data?.error_code && data.error_code !== '0' && data.error_code !== 0) { + throw new Error(`Tieba API error: ${data.error_msg || data.error_code}`); + } - const threadListHTML = load(data)('code[id="pagelet_html_frs-list/pagelet/thread_list"]') - .contents() - .filter((e) => e.nodeType === '8'); + const threadList = data?.thread_list || []; - const $ = load(threadListHTML.prevObject[0].data); - const list = $('#thread_list > .j_thread_list[data-field]') - .toArray() - .map((element) => { - const item = $(element); - const { id, author_name } = item.data('field'); - const time = sortBy === 'created' ? item.find('.is_show_create_time').text().trim() : item.find('.threadlist_reply_date').text().trim(); - const title = item.find('a.j_th_tit').text().trim(); - const details = item.find('.threadlist_abs').text().trim(); - const medias = item - .find('.threadlist_media img') - .toArray() - .map((element) => { - const item = $(element); - return `<img src="${item.attr('bpic')}">`; - }) - .join(''); + if (threadList.length === 0) { + throw new Error('No threads found. The cookie may be expired or invalid. Please check your BAIDU_COOKIE.'); + } - return { - title, - description: renderToString( - <> - <p>{details}</p> - <p>{raw(medias)}</p> - <p>作者:{author_name}</p> - </> - ), - pubDate: timezone(parseDate(time, ['HH:mm', 'M-D', 'YYYY-MM'], true), 8), - link: `https://tieba.baidu.com/p/${id}`, - }; - }); + // Build author map from user_list + const userList: any[] = data?.user_list || []; + const authorMap = new Map<number, string>(); + for (const user of userList) { + if (user.id) { + authorMap.set(Number(user.id), user.name_show || user.name || ''); + } + } + + const list = threadList.map((thread) => { + // Prefer first_post_content (richer), fall back to abstract + const { text: content, images } = extractContent(thread.first_post_content || thread.abstract || []); + + const timestamp = Number(thread.create_time || 0); + const pubDate = timestamp > 0 ? timezone(new Date(timestamp * 1000), 8) : undefined; + + const authorName = authorMap.get(Number(thread.author_id)) || ''; + + return { + title: thread.title, + link: `https://tieba.baidu.com/p/${thread.id || thread.tid}`, + ...(pubDate && { pubDate }), + author: authorName, + description: renderToString( + <> + {content ? <p>{content}</p> : null} + {images.length > 0 ? ( + <div> + {images.map((img) => ( + <img src={img} alt="" style={{ maxWidth: '100%', margin: '5px 0' }} /> + ))} + </div> + ) : null} + </> + ), + }; + }); return { title: `${kw}吧`, - description: load(data)('meta[name="description"]').attr('content'), link: `https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}`, item: list, }; diff --git a/lib/routes/baidu/tieba/post.tsx b/lib/routes/baidu/tieba/post.tsx index 32eea6f955d8..e3b1b89cb891 100644 --- a/lib/routes/baidu/tieba/post.tsx +++ b/lib/routes/baidu/tieba/post.tsx @@ -3,10 +3,11 @@ import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; import type { Route } from '@/types'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { getTiebaPageContent } from './common'; +import { parseRelativeTime } from './utils'; + /** * 获取最新的帖子回复(倒序查看) * @@ -16,18 +17,19 @@ import timezone from '@/utils/timezone'; * 这个默认值我测试下来 7e6 是比较接近最大值了,因为当我输入 8e6 就会返回第一页的数据而不是最后一页了 * @returns */ -async function getPost(id, lz = 0, pn = 7e6) { - const { data } = await got(`https://tieba.baidu.com/p/${id}?see_lz=${lz}&pn=${pn}&ajax=1`, { - headers: { - Referer: 'https://tieba.baidu.com/', - }, +async function getPost(id: string, lz = 0, pn = 7e6) { + const url = `https://tieba.baidu.com/p/${id}?see_lz=${lz}&pn=${pn}`; + const html = await getTiebaPageContent(url, `tieba:post:${id}:${lz}:${pn}`, { + waitForSelector: '.virtual-list-item', + timeout: 3000, }); - const $ = load(data); - const max = Number.parseInt($('[max-page]').attr('max-page')); + + const $ = load(html); + const max = Number.parseInt($('[max-page]').attr('max-page') || '0'); if (max > pn) { - return getPost(id, max); + return getPost(id, lz, max); } - return data; + return html; } export const route: Route = { @@ -36,9 +38,15 @@ export const route: Route = { example: '/baidu/tieba/post/686961453', parameters: { id: '帖子 ID' }, features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, + requireConfig: [ + { + name: 'BAIDU_COOKIE', + optional: false, + description: '百度 cookie 值,用于需要登录的贴吧页面', + }, + ], + requirePuppeteer: true, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, @@ -49,7 +57,7 @@ export const route: Route = { }, ], name: '帖子动态', - maintainers: ['u3u'], + maintainers: ['u3u', 'FlanChanXwO'], handler, }; @@ -58,49 +66,80 @@ async function handler(ctx) { const lz = ctx.req.path.includes('lz') ? 1 : 0; const html = await getPost(id, lz); const $ = load(html); - const title = $('.core_title_txt').attr('title'); - // .substr(3); - const list = $('.p_postlist > [data-field]:not(:has(.ad_bottom_view))'); + + const title = $('.pb-title-wrap .pb-title').text().trim() || ''; + + // 使用新的 Vue 渲染页面选择器 - 只选择 virtual-list-item 避免重复 + const list = $('.virtual-list-item'); + + if (list.length === 0) { + throw new Error('No post replies found. The post may not exist or the cookie is invalid.'); + } return { title: lz ? `【只看楼主】${title}` : title, link: `https://tieba.baidu.com/p/${id}?see_lz=${lz}`, description: `${title}的最新回复`, - item: list.toArray().map((element) => { - const item = $(element); - const { author, content } = item.data('field'); - const tempList = item - .find('.post-tail-wrap > .tail-info') - .toArray() - .map((element) => $(element).text()); - let [pubContent, from, num, time] = ['', '', '', '']; - if (0 === tempList.length && 'date' in content) { - num = `${content.post_no}楼`; - time = content.date; - pubContent = item.find('.j_d_post_content').html(); - } else if (2 === tempList.length) { - [num, time] = tempList; - pubContent = content.content; - } else if (3 === tempList.length) { - [from, num, time] = tempList; - pubContent = content.content; - } - return { - title: `${author.user_name}回复了帖子《${title}》`, - description: renderToString( - <> - <p>{raw(pubContent)}</p> - <br /> - 作者:{author.user_name} - <br /> - 楼层:{num} - <br /> - {from} - </> - ), - pubDate: timezone(parseDate(time, 'YYYY-MM-DD hh:mm'), 8), - link: `https://tieba.baidu.com/p/${id}?pid=${content.post_id}#${content.post_id}`, - }; - }), + item: list + .toArray() + .map((element) => { + const item = $(element); + + // 作者名 + const authorName = item.find('.head-name').text().trim(); + + // 跳过无效用户(无作者名的条目) + if (!authorName) { + return null; + } + + // 内容 - 从 pb-rich-text 获取(保留行内富文本,如链接、图片、表情等) + const contentItems = item.find('.pb-rich-text .pb-content-item'); + let postContent = ''; + contentItems.each((_, el) => { + const html = $(el).html()?.trim(); + if (html) { + postContent += `<p>${html}</p>`; + } + }); + + // 图片 + const images = item + .find('.image-list-wrapper img') + .toArray() + .map((img) => $(img).attr('src') || $(img).attr('data-src') || '') + .filter(Boolean) + .map((src) => `<img src="${src}" alt="${title}">`) + .join(''); + + // 楼层和时间 + const descText = item.find('.pc-pb-comments-desc, .comment-desc-left').text().trim(); + const floorMatch = descText.match(/第(\d+)楼/); + const floor = floorMatch ? `${floorMatch[1]}楼` : ''; + + // 解析时间并验证有效性 - 使用完整的 descText 以支持 parseRelativeTime 能处理的所有格式 + const parsedDate = descText ? parseRelativeTime(descText) : null; + const validPubDate = parsedDate && !Number.isNaN(parsedDate.getTime()) ? timezone(parsedDate, 8) : undefined; + + // 尝试获取回复的唯一ID用于生成直接链接 + const postId = item.attr('data-post-id') || item.attr('id') || ''; + const replyLink = postId ? `https://tieba.baidu.com/p/${id}?pid=${postId}#${postId}` : `https://tieba.baidu.com/p/${id}`; + + return { + title: `${authorName} 回复了帖子《${title}》`, + description: renderToString( + <> + {postContent ? <div>{raw(postContent)}</div> : null} + {images ? <div>{raw(images)}</div> : null} + {floor ? <p>楼层:{floor}</p> : null} + </> + ), + + pubDate: validPubDate, + author: authorName, + link: replyLink, + }; + }) + .filter((item): item is NonNullable<typeof item> => item !== null), }; } diff --git a/lib/routes/baidu/tieba/search.tsx b/lib/routes/baidu/tieba/search.tsx index 9b8e5810c374..798fc344db88 100644 --- a/lib/routes/baidu/tieba/search.tsx +++ b/lib/routes/baidu/tieba/search.tsx @@ -1,28 +1,34 @@ import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; -import iconv from 'iconv-lite'; import type { Route } from '@/types'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { getTiebaPageContent, normalizeUrl } from './common'; + export const route: Route = { path: '/tieba/search/:qw/:routeParams?', categories: ['bbs'], example: '/baidu/tieba/search/neuro', parameters: { qw: '搜索关键词', routeParams: '额外参数;请参阅以下说明和表格' }, features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, + requireConfig: [ + { + name: 'BAIDU_COOKIE', + optional: false, + description: '百度 cookie 值,用于需要登录的贴吧页面', + }, + ], + requirePuppeteer: true, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, }, name: '贴吧搜索', - maintainers: ['JimenezLi'], + maintainers: ['JimenezLi', 'FlanChanXwO'], handler, description: `| 键 | 含义 | 接受的值 | 默认值 | | ------------ | ---------------------------------------------------------- | ------------- | ------ | @@ -36,43 +42,65 @@ export const route: Route = { async function handler(ctx) { const qw = ctx.req.param('qw'); + const query = new URLSearchParams(ctx.req.param('routeParams')); query.set('ie', 'utf-8'); query.set('qw', qw); - query.set('rn', query.get('rn') || '20'); // Number of returned items + query.set('rn', query.get('rn') || '20'); const link = `https://tieba.baidu.com/f/search/res?${query.toString()}`; - const response = await got.get(link, { - headers: { - Referer: 'https://tieba.baidu.com', - }, - responseType: 'buffer', + const html = await getTiebaPageContent(link, `tieba:search:${qw}:${query.toString()}`, { + waitForSelector: '.thread-content-box', + timeout: 3000, }); - const data = iconv.decode(response.data, 'gbk'); - const $ = load(data); - const resultList = $('div.s_post'); + const $ = load(html); + + const resultList = $('.thread-content-box'); + + if (resultList.length === 0) { + throw new Error('No search results found. The page structure may have changed.'); + } return { title: `${qw} - ${query.get('kw') || '百度贴'}吧搜索`, link, item: resultList.toArray().map((element) => { const item = $(element); - const titleItem = item.find('.p_title a'); - const title = titleItem.text().trim(); - const link = titleItem.attr('href'); - const time = item.find('.p_date').text().trim(); - const details = item.find('.p_content').text().trim(); + + // 标题 + const title = item.find('.title-content-wrap .title-wrap span').text().trim(); + + // 内容摘要 + const details = item.find('.abstract-wrap span').text().trim(); + + // 从链接中提取帖子URL,并规范化为绝对地址 + const linkPath = item.find('.action-bar-warp a.action-link-bg').attr('href') || ''; + const linkHref = normalizeUrl(linkPath); + + // 作者 + const author = item.find('.forum-attention.user').text().trim(); + + // 时间 - 从 top-title 中提取 "发布于 YYYY-M-D" + const timeText = item.find('.top-title').text().trim(); + const timeMatch = timeText.match(/发布于\s+(\d{4}-\d{1,2}-\d{1,2})/); + const time = timeMatch ? timeMatch[1] : ''; + const parsedDate = time ? parseDate(time, 'YYYY-M-D') : null; + const validPubDate = parsedDate && !Number.isNaN(parsedDate.getTime()) ? timezone(parsedDate, 8) : undefined; + + // 图片 const medias = item .find('.p_mediaCont img') .toArray() - .map((element) => { + .flatMap((element) => { const item = $(element); - return `<img src="${item.attr('original')}">`; + const src = (item.attr('original') || '').trim(); + return src ? [`<img src="${src}">`] : []; }) .join(''); - const tieba = item.find('a.p_forum').text().trim(); - const author = item.find('a').last().text().trim(); + + // 贴吧名 + const tieba = item.find('.forum-name-text').text().trim(); return { title, @@ -80,16 +108,12 @@ async function handler(ctx) { <> <p>{details}</p> <p>{raw(medias)}</p> - <p> - 贴吧:{tieba} - <br /> - 作者:{author} - </p> + <p>贴吧:{tieba}</p> </> ), author, - pubDate: timezone(parseDate(time, 'YYYY-MM-DD HH:mm'), 8), - link, + pubDate: validPubDate, + link: linkHref, }; }), }; diff --git a/lib/routes/baidu/tieba/user.ts b/lib/routes/baidu/tieba/user.ts deleted file mode 100644 index 4a806ebb54ae..000000000000 --- a/lib/routes/baidu/tieba/user.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { load } from 'cheerio'; - -import type { Route } from '@/types'; -import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; -import timezone from '@/utils/timezone'; - -export const route: Route = { - path: '/tieba/user/:uid', - categories: ['bbs'], - example: '/baidu/tieba/user/斗鱼游戏君', - parameters: { uid: '用户 ID' }, - features: { - requireConfig: false, - requirePuppeteer: false, - antiCrawler: false, - supportBT: false, - supportPodcast: false, - supportScihub: false, - }, - name: '用户帖子', - maintainers: ['igxlin', 'nczitzk'], - handler, - description: '用户 ID 可以通过打开用户的主页后查看地址栏的 `un` 字段来获取。', -}; - -async function handler(ctx) { - const uid = ctx.req.param('uid'); - const response = await got(`https://tieba.baidu.com/home/main?un=${uid}`); - - const data = response.data; - - const $ = load(data); - const name = $('span.userinfo_username').text(); - const list = $('div.n_right.clearfix'); - let imgurl; - - return { - title: `${name} 的贴吧`, - link: `https://tieba.baidu.com/home/main?un=${uid}`, - item: - list && - list.toArray().map((item) => { - item = $(item).find('.n_contain'); - imgurl = item.find('ul.n_media.clearfix img').attr('original'); - return { - title: item.find('div.thread_name a').attr('title'), - pubDate: timezone(parseDate(item.parent().find('div .n_post_time').text(), ['YYYY-MM-DD', 'HH:mm']), 8), - description: `${item.find('div.n_txt').text()}<br><img src="${imgurl}">`, - link: item.find('div.thread_name a').attr('href'), - }; - }), - }; -} diff --git a/lib/routes/baidu/tieba/user.tsx b/lib/routes/baidu/tieba/user.tsx new file mode 100644 index 000000000000..735f852393fc --- /dev/null +++ b/lib/routes/baidu/tieba/user.tsx @@ -0,0 +1,104 @@ +import { load } from 'cheerio'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { Route } from '@/types'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { getTiebaPageContent, normalizeUrl } from './common'; + +export const route: Route = { + path: '/tieba/user/:uid', + categories: ['bbs'], + example: '/baidu/tieba/user/斗鱼游戏君', + parameters: { uid: '用户 ID' }, + features: { + requireConfig: [ + { + name: 'BAIDU_COOKIE', + optional: false, + description: '百度 cookie 值,用于需要登录的贴吧页面', + }, + ], + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户帖子', + maintainers: ['igxlin', 'nczitzk', 'FlanChanXwO'], + handler, + description: '用户 ID 可以通过打开用户的主页后查看地址栏的 `un` 字段来获取。', +}; + +async function handler(ctx) { + const uid = ctx.req.param('uid'); + const encodedUid = encodeURIComponent(uid); + const url = `https://tieba.baidu.com/home/main?un=${encodedUid}`; + + const html = await getTiebaPageContent(url, `tieba:user:${uid}`, { + waitForSelector: '.thread-card', + timeout: 3000, + }); + + const $ = load(html); + + const name = $('span.userinfo_username').text() || uid; + const list = $('.thread-card'); + + if (list.length === 0) { + throw new Error('No user posts found. The page structure may have changed or the user does not exist.'); + } + + return { + title: `${name} 的贴吧`, + link: `https://tieba.baidu.com/home/main?un=${encodedUid}`, + item: list.toArray().map((element) => { + const item = $(element); + + // 作者 + const authorName = item.find('.head-name').text().trim() || name; + + // 标题 + const title = item.find('.title-text').text().trim(); + + // 内容 + const content = item.find('.tb-richtext .text').text().trim(); + + // 图片 + const images = item + .find('.image-list-item img') + .toArray() + .map((img) => $(img).attr('src') || $(img).attr('data-src') || '') + .filter(Boolean); + + // 时间 + const timeText = item.find('.post-num').text().trim(); + const parsedDate = timeText ? parseDate(timeText, ['YYYY-MM-DD']) : null; + const validPubDate = parsedDate && !Number.isNaN(parsedDate.getTime()) ? timezone(parsedDate, 8) : undefined; + + // 链接 + const link = normalizeUrl(item.find('a.thread-card-content').attr('href') || ''); + + return { + title, + pubDate: validPubDate, + author: authorName, + description: renderToString( + <> + {content ? <p>{content}</p> : null} + {images.length > 0 ? ( + <div> + {images.map((img) => ( + <img src={img} alt="" style={{ maxWidth: '100%', margin: '5px 0' }} /> + ))} + </div> + ) : null} + </> + ), + link, + }; + }), + }; +} diff --git a/lib/routes/baidu/tieba/utils.ts b/lib/routes/baidu/tieba/utils.ts new file mode 100644 index 000000000000..3a6a5c91d082 --- /dev/null +++ b/lib/routes/baidu/tieba/utils.ts @@ -0,0 +1,91 @@ +import type { CheerioAPI } from 'cheerio'; + +import { parseRelativeDate } from '@/utils/parse-date'; + +/** + * 解析相对时间(如"回复于4小时前")为实际日期 + */ +export function parseRelativeTime(timeStr: string): Date { + const normalized = (timeStr || '').replace(/^回复于/, '').trim(); + return parseRelativeDate(normalized, ['M-D', 'YYYY-MM-DD', 'HH:mm', 'YYYY-MM-DD HH:mm', 'YYYY-M-D HH:mm']); +} + +/** + * 帖子数据接口 + */ +export interface Thread { + id: string; + title: string; + content: string; + author: string; + time: string; + images: string[]; + link: string; +} + +/** + * 解析帖子列表 + */ +export function parseThreads($: CheerioAPI): Thread[] { + const cardThreads = $('.thread-card-wrapper') + .toArray() + .map((element) => { + const item = $(element); + + const linkHref = item.find('a.thread-content-link').attr('href') || ''; + const idMatch = linkHref.match(/\/p\/(\d+)/); + const id = idMatch ? idMatch[1] : ''; + + const title = item.find('.thread-title .text').text().trim(); + const content = item.find('.thread-content .text').text().trim(); + const author = item.find('.head-name').text().trim(); + + const descInfo = item.find('.desc-info'); + const timeText = descInfo.length > 0 ? descInfo.text().trim() : item.find('.time, .date').text().trim(); + + const images = item + .find('.image-list-item img') + .toArray() + .map((img) => $(img).attr('data-src')) + .filter((src): src is string => src !== undefined && src !== ''); + + return { + id, + title, + content, + author, + time: timeText, + images, + link: linkHref, + }; + }) + .filter((t) => t.id && t.title); + + if (cardThreads.length > 0) { + return cardThreads; + } + + return $('li.j_thread_list') + .toArray() + .map((element) => { + const item = $(element); + const linkHref = item.find('a.j_th_tit').attr('href') || ''; + const idMatch = linkHref.match(/\/p\/(\d+)/); + const id = idMatch ? idMatch[1] : ''; + + return { + id, + title: item.find('a.j_th_tit').text().trim(), + content: item.find('.threadlist_abs').text().trim(), + author: item.find('.frs-author-name').first().text().trim(), + time: item.find('.threadlist_reply_date').first().text().trim(), + images: item + .find('.threadlist_pic img') + .toArray() + .map((img) => $(img).attr('src') || $(img).attr('bpic') || '') + .filter((src) => src !== ''), + link: linkHref, + }; + }) + .filter((t) => t.id && t.title); +} From 6c76f8d13483746e121134be636ad524cd74b9aa Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Thu, 30 Jul 2026 16:10:25 +0800 Subject: [PATCH 0462/1190] fix(route/caijing): update API endpoint (#22887) * fix(route/caijing): update API endpoint * fix: update feed url * fix: reuse url --- lib/routes/caijing/roll.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/routes/caijing/roll.ts b/lib/routes/caijing/roll.ts index fa8bd8d0fa3d..79a1a136f068 100644 --- a/lib/routes/caijing/roll.ts +++ b/lib/routes/caijing/roll.ts @@ -21,7 +21,7 @@ export const route: Route = { }, radar: [ { - source: ['roll.caijing.com.cn/index1.html', 'roll.caijing.com.cn/'], + source: ['roll.caijing.com.cn/'], }, ], name: '滚动新闻', @@ -32,9 +32,8 @@ export const route: Route = { async function handler() { const baseUrl = 'https://roll.caijing.com.cn'; - const response = await got(`${baseUrl}/ajax_lists.php`, { + const response = await got(`${baseUrl}/json/lists1.json`, { searchParams: { - modelid: 0, time: Math.random(), }, }); @@ -67,7 +66,7 @@ async function handler() { return { title: '滚动新闻-财经网', image: 'https://www.caijing.com.cn/favicon.ico', - link: response.url, + link: baseUrl, item: items, }; } From a22d69686b529c49d7f791d656c5cc399e09f424 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:16:04 +0000 Subject: [PATCH 0463/1190] chore(deps): bump actions/attest from 4.2.0 to 4.2.1 (#22888) Bumps [actions/attest](https://github.com/actions/attest) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/attest/releases) - [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest/compare/f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6...508db95dd578ae2727ebd6217d5ba78e4fbda05d) --- updated-dependencies: - dependency-name: actions/attest dependency-version: 4.2.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index d8fae1294d3d..f5ca65dba99f 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -118,7 +118,7 @@ jobs: outputs: type=image,compression=zstd,force-compression=true,push-by-digest=true,name-canonical=true,push=true - name: Attest (ordinary version) - uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 + uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4.2.1 with: subject-name: | ${{ vars.DOCKER_USERNAME }}/${{ steps.repo-name.outputs.repo-name }} @@ -173,7 +173,7 @@ jobs: outputs: type=image,compression=zstd,force-compression=true,push-by-digest=true,name-canonical=true,push=true - name: Attest (Chromium-bundled version) - uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0 + uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4.2.1 with: subject-name: | ${{ vars.DOCKER_USERNAME }}/${{ steps.repo-name.outputs.repo-name }} From b433168349d5eea9cd80280ae314a57fab8deaa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:16:21 +0000 Subject: [PATCH 0464/1190] chore(deps): bump @notionhq/client from 5.23.2 to 5.23.3 (#22892) Bumps [@notionhq/client](https://github.com/makenotion/notion-sdk-js) from 5.23.2 to 5.23.3. - [Release notes](https://github.com/makenotion/notion-sdk-js/releases) - [Commits](https://github.com/makenotion/notion-sdk-js/compare/v5.23.2...v5.23.3) --- updated-dependencies: - dependency-name: "@notionhq/client" dependency-version: 5.23.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 9c3657498745..e91ad9685584 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@hono/node-server": "2.0.12", "@hono/zod-openapi": "1.5.1", "@jocmp/mercury-parser": "3.0.9", - "@notionhq/client": "5.23.2", + "@notionhq/client": "5.23.3", "@opentelemetry/api": "1.9.1", "@opentelemetry/exporter-prometheus": "0.221.0", "@opentelemetry/exporter-trace-otlp-http": "0.221.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97a327d3d4ba..c2e98a4a10ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,8 +56,8 @@ importers: specifier: 3.0.9 version: 3.0.9 '@notionhq/client': - specifier: 5.23.2 - version: 5.23.2 + specifier: 5.23.3 + version: 5.23.3 '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -1489,8 +1489,8 @@ packages: resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==} engines: {node: '>=12.4.0'} - '@notionhq/client@5.23.2': - resolution: {integrity: sha512-iyD5VU9MoNxCR0GespotyrMlBpO4kLG+BaZK2JpZW1yfFqrmOYmZGb3wl4xqfq3n1Z9eekFwNcZD6t63SaESig==} + '@notionhq/client@5.23.3': + resolution: {integrity: sha512-Uk0ycO+u6+iueoE/eK9Aa/DGWd2tZ9ymvGF/MXcfxXXL+AS2JYWm1ols19rEk5qOdZ0l2KYc6zThx0iXQ3QhpQ==} engines: {node: '>=18'} '@octokit/auth-token@6.0.0': @@ -3714,7 +3714,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.52: @@ -5375,8 +5375,8 @@ packages: resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.24: - resolution: {integrity: sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==} + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} engines: {node: ^10 || ^12 || >=14} postman-request@2.88.1-postman.48: @@ -7333,7 +7333,7 @@ snapshots: '@nolyfill/side-channel@1.0.44': {} - '@notionhq/client@5.23.2': {} + '@notionhq/client@5.23.3': {} '@octokit/auth-token@6.0.0': {} @@ -11133,7 +11133,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.24: + postcss@8.5.25: dependencies: nanoid: 3.3.16 picocolors: 1.1.1 @@ -12071,7 +12071,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - postcss: 8.5.24 + postcss: 8.5.25 rollup: 4.62.3 tinyglobby: 0.2.17 optionalDependencies: From c953a3f5ee7714ab09b80fbeed607544d18145f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:18:36 +0000 Subject: [PATCH 0465/1190] chore(deps): bump imapflow from 1.6.4 to 1.6.5 (#22891) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.6.4 to 1.6.5. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.6.4...v1.6.5) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.6.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e91ad9685584..2888e4245594 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.6.4", + "imapflow": "1.6.5", "instagram-private-api": "1.46.1", "ioredis": "5.11.1", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2e98a4a10ba..48e0ae75a76b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.6.4 - version: 1.6.4 + specifier: 1.6.5 + version: 1.6.5 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4386,8 +4386,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.6.4: - resolution: {integrity: sha512-4KTjkhmyIyeFlTyNuQRQ0pl5LwkBlzoTq2vG9zjF0HqQ0+GIYVaIWq2joOCOXERbyXNI5EuqlyGXQh3NUTqoFQ==} + imapflow@1.6.5: + resolution: {integrity: sha512-5XgLcfl6blDju2SP7TgTv4WIlTt7zxXYcfjznJFmfFrogHR2XzHGlS9s2wdqxv9ZJ5CPaRnig7KaK7qNoJPmlg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -9895,7 +9895,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.6.4: + imapflow@1.6.5: dependencies: '@zone-eu/mailsplit': 5.4.14 encoding-japanese: 2.2.0 From 0ab4315fa08ca861e9ba625706f2d43fbecf69de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 21:58:24 +0800 Subject: [PATCH 0466/1190] chore(deps): bump nixpkgs from `624af66` to `0954f7e` (#22895) Bumps [nixpkgs](https://github.com/NixOS/nixpkgs) from `624af66` to `0954f7e`. - [Commits](https://github.com/NixOS/nixpkgs/compare/624af665418d3c65d544145b4d34ad696439570e...0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: '0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5' dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 930e2c498f4f..08fb5a212086 100644 --- a/flake.lock +++ b/flake.lock @@ -290,11 +290,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1785090369, - "narHash": "sha256-m0pDuRJG7EDo9ri+4Ksu83VsI+PlxNC9lNBfydejce4=", + "lastModified": 1785318670, + "narHash": "sha256-dN6Ou5x/+23FZLEpYP3IffO+NyJFzUlGumt1uu3MMaY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "624af665418d3c65d544145b4d34ad696439570e", + "rev": "0954f7ee2f6bb3dc7d4e3d0d8bcb8fd4bde4cfc5", "type": "github" }, "original": { From 58e9bbe21db8004a0f74f9ce9150ab55ddf8f21c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 21:59:29 +0800 Subject: [PATCH 0467/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#22890) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260729.1 to 5.20260730.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260730.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2888e4245594..bf6e628bb0dc 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.3", "@cloudflare/vitest-pool-workers": "0.19.0", - "@cloudflare/workers-types": "5.20260729.1", + "@cloudflare/workers-types": "5.20260730.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.76.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48e0ae75a76b..04f4dac5debb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -295,10 +295,10 @@ importers: version: 1.3.3 '@cloudflare/vitest-pool-workers': specifier: 0.19.0 - version: 0.19.0(@cloudflare/workers-types@5.20260729.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) + version: 0.19.0(@cloudflare/workers-types@5.20260730.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10) '@cloudflare/workers-types': - specifier: 5.20260729.1 - version: 5.20260729.1 + specifier: 5.20260730.1 + version: 5.20260730.1 '@eslint/eslintrc': specifier: 3.3.6 version: 3.3.6 @@ -472,7 +472,7 @@ importers: version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) wrangler: specifier: 4.115.0 - version: 4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.115.0(@cloudflare/workers-types@5.20260730.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -639,8 +639,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260729.1': - resolution: {integrity: sha512-X5r/4y0gKMq/B72qkz/tEwNK4c3v2regT3to6Ia8qqC66E0+YIN/fU7x0JG6ej2rLxtU3TV3aVhfK9y8+jMMAw==} + '@cloudflare/workers-types@5.20260730.1': + resolution: {integrity: sha512-3e1cBXcPTwXBk2ur0CfxZKQKL6X7vUZlOn1R7VYU0zZVJcSKFcq1AoOZM+ps0LuL0uTAxdcLg+qwXrBXVu+UuQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -6674,7 +6674,7 @@ snapshots: optionalDependencies: workerd: 1.20260722.1 - '@cloudflare/vitest-pool-workers@0.19.0(@cloudflare/workers-types@5.20260729.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': + '@cloudflare/vitest-pool-workers@0.19.0(@cloudflare/workers-types@5.20260730.1)(@vitest/runner@4.1.10)(@vitest/snapshot@4.1.10)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.10)': dependencies: '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -6682,7 +6682,7 @@ snapshots: esbuild: 0.28.1 miniflare: 4.20260722.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(@vitest/coverage-v8@4.1.10)(jsdom@30.0.1(@noble/hashes@2.2.0))(msw@2.15.0(@types/node@26.1.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.1.2)(lightningcss@1.32.0)(tsx@4.23.1)(yaml@2.9.0)) - wrangler: 4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.115.0(@cloudflare/workers-types@5.20260730.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6704,7 +6704,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260722.1': optional: true - '@cloudflare/workers-types@5.20260729.1': {} + '@cloudflare/workers-types@5.20260730.1': {} '@colors/colors@1.6.0': {} @@ -12202,7 +12202,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260722.1 '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.115.0(@cloudflare/workers-types@5.20260729.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.115.0(@cloudflare/workers-types@5.20260730.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) @@ -12213,7 +12213,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260722.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260729.1 + '@cloudflare/workers-types': 5.20260730.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 8d605eae4c294890c764e0b24f84c03d78c88832 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 22:50:49 +0800 Subject: [PATCH 0468/1190] chore(deps): bump devenv from `ba7318a` to `bf3fde5` (#22894) Bumps [devenv](https://github.com/cachix/devenv) from `ba7318a` to `bf3fde5`. - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/ba7318ab409ecbbf0ed79c43e5fbf1b4ec359f7a...bf3fde552a27d5e41c52c0fcfbf258e396c995bb) --- updated-dependencies: - dependency-name: devenv dependency-version: bf3fde552a27d5e41c52c0fcfbf258e396c995bb dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 08fb5a212086..5650e4819312 100644 --- a/flake.lock +++ b/flake.lock @@ -61,11 +61,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1785262887, - "narHash": "sha256-z8mS9Q4poqs3zMeOm/PW/FAKfhRYG5TVWkoBSLpQAbo=", + "lastModified": 1785352168, + "narHash": "sha256-m/9I5Ai3+wbJuKXtwwaFLG+4Db7Pg2EUYP3ZGLsA7xU=", "owner": "cachix", "repo": "devenv", - "rev": "ba7318ab409ecbbf0ed79c43e5fbf1b4ec359f7a", + "rev": "bf3fde552a27d5e41c52c0fcfbf258e396c995bb", "type": "github" }, "original": { @@ -196,11 +196,11 @@ ] }, "locked": { - "lastModified": 1782407171, - "narHash": "sha256-xem+4ncdQCTFJsQ4PrVuyVmi3j4w/Yqg298hBUzVejA=", + "lastModified": 1785349663, + "narHash": "sha256-JSD8lPe5kalvPKx5X+inX8ZZdGLeXkAbd3Jiv7UDf+I=", "owner": "cachix", "repo": "nix", - "rev": "782ac1b155679b065ec945ae50d0fa1d495883b7", + "rev": "f33db89fd6db6edc337d93212f6628ab6d25f407", "type": "github" }, "original": { From 4517140a7c34df0eba15ff2286bb09e7615cd8ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:18:57 +0800 Subject: [PATCH 0469/1190] chore(deps): bump @sentry/node from 10.67.0 to 10.69.0 (#22893) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.67.0 to 10.69.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/10.69.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.67.0...10.69.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.69.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 69 +++++++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index bf6e628bb0dc..62a006a27209 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.11.11", - "@sentry/node": "10.67.0", + "@sentry/node": "10.69.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04f4dac5debb..5d168fa857f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,8 +86,8 @@ importers: specifier: 0.11.11 version: 0.11.11(hono@4.12.32) '@sentry/node': - specifier: 10.67.0 - version: 10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)) + specifier: 10.69.0 + version: 10.69.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -497,12 +497,12 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': - resolution: {integrity: sha512-Yidf5GOl60db80UxUtNdKK3pnY7obU/gs0xOfA0SCdnvVLMCvfYIer/egC3TqpPiT0Jg22eg3RlzcO+zKfPMcA==} + '@apm-js-collab/code-transformer-bundler-plugins@0.7.3': + resolution: {integrity: sha512-qNbPwuMZ8f5ZuGj/ttPeB7a6C/S1bB6tNYaEL5vNiRKydSAxa4AU0gxCWgaP4fVju+AuwhcumSFjrEcGF9Dv7Q==} engines: {node: '>=18.0.0'} - '@apm-js-collab/code-transformer@0.18.0': - resolution: {integrity: sha512-aN3Oq8r1J3gPJtCwErP664gM0+HhM1I1lujPr9TMTCcEl/joQQbpGpeMdts9B1+W2wHMsvioDMv5F4PvMWE6gw==} + '@apm-js-collab/code-transformer@0.18.1': + resolution: {integrity: sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==} hasBin: true '@apm-js-collab/tracing-hooks@0.13.0': @@ -2618,12 +2618,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.67.0': - resolution: {integrity: sha512-b6U3pJ8AUvN9aouq0vl+VZI8KT8RslBsfGMFuNwRr313zOmdmFJBZqTiUw9VGgJ2jGKxLO9alm9rlxBfX4hf+w==} + '@sentry/core@10.69.0': + resolution: {integrity: sha512-+uuqVEeiDzYuAKjZLqsROKXvRTbl/QeH0gfGRtpYib1cud4rAFWRIkFmcR7Jb7JGFYwmReyQotiTj/hcDszTZg==} engines: {node: '>=18'} - '@sentry/node-core@10.67.0': - resolution: {integrity: sha512-dBHHRwZyan1pOnFJ+sNBvR8TkXbZAfZU/jpxmALS3JZ2/8AGR7cQKL+b7SleKuJ7iUDZyklN3Nqi0i5JkcA+HA==} + '@sentry/node-core@10.69.0': + resolution: {integrity: sha512-IgArHczrZJxkgxoffHscj0NxQrG6kCazgmGQnlf3j58J1ec21YaUu8Tu+7G4Lo5tCiW3teQnwlKW1ttMXSqWRw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2643,20 +2643,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.67.0': - resolution: {integrity: sha512-SFKpZGqOCEFSmP93NdDP6ikZp4NS7A/JR8+2ofK3jF6Y9Vyox7pX0pxdOnPLpFcPtMybMahfWqSAWJvsFs4RmA==} + '@sentry/node@10.69.0': + resolution: {integrity: sha512-xEXA1YGIiTZbrW6MWV34uS6JGQuQg2ijTI0zed+FsJb9JZKPYel/GZK8Km26vfTVb+yCXFmWZNBesKegNcVdzg==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.67.0': - resolution: {integrity: sha512-oLTOrAK1rOqmYRktOJZwz37B1seXPx1W2FTMVtzTVNjMFA/LZwGzePeZzhUOgzZgfLHixMd/ceWtGqoxAndcjQ==} + '@sentry/opentelemetry@10.69.0': + resolution: {integrity: sha512-3FyWV6YcEJuvLrlaKGE1dHXCI+1YO0a62w7PkwlRg8yp6K6YXkmdwu9GjqaYD+Ju4tm7uC7mHIsGFQMm0M7pqQ==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.67.0': - resolution: {integrity: sha512-GQ9t+RSTx5s3b/aZrLFuL4nrwPLMah5NiZk5cjxJmgmOSgm3nMdO8gdqCedDch5B13F3YsxtaQYjSJnzLz8M5A==} + '@sentry/server-utils@10.69.0': + resolution: {integrity: sha512-0MwHrA8+nNvMIsqf8m3cXwCBlUjr6AS7N6CZvHJtY1DkqEvQqEbD5VIrhzEyHN/KMZIgQ8XeDCQRhjnXFQGRhg==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -6553,14 +6553,14 @@ snapshots: '@actions/io@3.0.2': {} - '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': + '@apm-js-collab/code-transformer-bundler-plugins@0.7.3': dependencies: - '@apm-js-collab/code-transformer': 0.18.0 + '@apm-js-collab/code-transformer': 0.18.1 es-module-lexer: 2.3.1 magic-string: 0.30.21 module-details-from-path: 1.0.4 - '@apm-js-collab/code-transformer@0.18.0': + '@apm-js-collab/code-transformer@0.18.1': dependencies: '@types/estree': 1.0.9 astring: 1.9.0 @@ -6571,7 +6571,7 @@ snapshots: '@apm-js-collab/tracing-hooks@0.13.0': dependencies: - '@apm-js-collab/code-transformer': 0.18.0 + '@apm-js-collab/code-transformer': 0.18.1 debug: 4.4.3 module-details-from-path: 1.0.4 transitivePeerDependencies: @@ -8081,15 +8081,15 @@ snapshots: '@sentry/conventions@0.16.0': {} - '@sentry/core@10.67.0': + '@sentry/core@10.69.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.67.0 - '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/core': 10.69.0 + '@sentry/opentelemetry': 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.3.2 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -8098,36 +8098,37 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.67.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.69.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.67.0 - '@sentry/node-core': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.67.0 + '@sentry/core': 10.69.0 + '@sentry/node-core': 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.221.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.69.0 import-in-the-middle: 3.3.2 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.67.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.69.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.67.0 + '@sentry/core': 10.69.0 - '@sentry/server-utils@10.67.0': + '@sentry/server-utils@10.69.0': dependencies: - '@apm-js-collab/code-transformer-bundler-plugins': 0.7.1 + '@apm-js-collab/code-transformer-bundler-plugins': 0.7.3 '@apm-js-collab/tracing-hooks': 0.13.0 '@sentry/conventions': 0.16.0 - '@sentry/core': 10.67.0 + '@sentry/core': 10.69.0 + meriyah: 6.1.4 transitivePeerDependencies: - supports-color From 49b3eb74531c6312e9eb11f8244e82ed0532de28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:29:03 +0800 Subject: [PATCH 0470/1190] chore(deps): bump docker/login-action from 4.5.2 to 4.6.0 (#22889) Bumps [docker/login-action](https://github.com/docker/login-action) from 4.5.2 to 4.6.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/371161bbe7024a29a25c5e19bfcbc0804fe9ad2c...dbcb813823bdd20940b903addbd779551569679f) --- updated-dependencies: - dependency-name: docker/login-action dependency-version: 4.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index f5ca65dba99f..5605f11db113 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -74,13 +74,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -207,13 +207,13 @@ jobs: uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Log in to Docker Hub - uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the Container registry - uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} From 54345e0759b1940e33e0ce7271c662360edc016c Mon Sep 17 00:00:00 2001 From: Tony <TonyRL@users.noreply.github.com> Date: Sat, 1 Aug 2026 01:51:31 +0800 Subject: [PATCH 0471/1190] fix(route/ximalaya): add back audio enclosure (#22903) * fix(route/ximalaya): add back audio enclosure * fix: getXmSign --- lib/routes/ximalaya/album.ts | 16 ++++++-- lib/routes/ximalaya/types.ts | 6 +++ lib/routes/ximalaya/utils.ts | 79 ++++++++++++++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/lib/routes/ximalaya/album.ts b/lib/routes/ximalaya/album.ts index e2be5b5b9442..d1fb0dd57a71 100644 --- a/lib/routes/ximalaya/album.ts +++ b/lib/routes/ximalaya/album.ts @@ -6,8 +6,8 @@ import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import type { Album, RichIntro, TrackInfoResponse } from './types'; -import { decryptUrl, getRandom16 } from './utils'; +import type { Album, MobileTrack, RichIntro, TrackInfoResponse } from './types'; +import { decryptUrl, getRandom16, getXmSign } from './utils'; const baseUrl = 'https://www.ximalaya.com'; @@ -50,13 +50,14 @@ function judgeTrue(str, ...validStrings) { } export const route: Route = { - path: '/:type/:id/:all/:shownote?', + path: '/:type/:id/:all?/:shownote?', categories: ['multimedia'], example: '/ximalaya/album/299146', parameters: { type: '专辑类型, 通常可以使用 `album`,可在对应专辑页面的 URL 中找到', id: '专辑 id, 可在对应专辑页面的 URL 中找到', all: '是否需要获取全部节目,填入 `1`、`true`、`all` 视为获取所有节目,填入其他则不获取。', + shownote: '是否需要获取节目的 ShowNote,填入 `1`、`true`,`shownote` 视为获取,填入其他则不获取。', }, features: { requireConfig: [ @@ -143,6 +144,12 @@ async function handler(ctx) { } return _desc; }); + + const playPath = await cache.tryGet(`ximalaya:track:${item.trackId}`, async () => { + const track = await ofetch<MobileTrack>(`https://m.ximalaya.com/tracks/${item.trackId}.json`); + return { url: track.play_path_64 ?? track.play_path_32 ?? track.play_path ?? '' }; + }); + item.playPathAacv224 = playPath.url; }) ); @@ -158,11 +165,12 @@ async function handler(ctx) { headers: { 'user-agent': 'ting_6.7.9(GM1900,Android29)', cookie: `1&_device=android&${randomToken}&6.7.9;1&_token=${token}`, + 'xm-sign': getXmSign(), }, }); const trackInfo = trackPayInfoResponse.trackInfo; const _item = {}; - if (!trackInfo.isAuthorized) { + if (!trackInfo?.isAuthorized) { return _item; } _item.playPathAacv224 = decryptUrl(trackInfo.playUrlList[0].url); diff --git a/lib/routes/ximalaya/types.ts b/lib/routes/ximalaya/types.ts index ad4b1a0d82cf..99d3735c609b 100644 --- a/lib/routes/ximalaya/types.ts +++ b/lib/routes/ximalaya/types.ts @@ -67,6 +67,12 @@ export interface RichIntro { richIntro: string; } +export interface MobileTrack { + play_path_64: string | null; + play_path_32: string | null; + play_path: string | null; +} + interface SubscriptInfo { albumSubscriptValue: number; url: string; diff --git a/lib/routes/ximalaya/utils.ts b/lib/routes/ximalaya/utils.ts index 3abc57de12d9..435a51483b0e 100644 --- a/lib/routes/ximalaya/utils.ts +++ b/lib/routes/ximalaya/utils.ts @@ -1,4 +1,5 @@ -import crypto from 'node:crypto'; +import { createCipheriv, randomBytes } from 'node:crypto'; +import { crc32 } from 'node:zlib'; /* const getParams = (ep) => { const a1 = 'xkt3a41psizxrh9l'; @@ -123,11 +124,81 @@ const getUrl = (r) => { */ const getRandom16 = (len) => - crypto - .randomBytes(Math.ceil(len / 2)) + randomBytes(Math.ceil(len / 2)) .toString('hex') .slice(0, len); +const base62 = (num) => { + const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let out = ''; + do { + out = chars[num % 62] + out; + num = Math.floor(num / 62); + } while (num > 0); + return out.padStart(8, '0'); +}; + +/* +const generateRandomString = (payload, code) => { + // 8 random hex chars + const a = 'xxxxxxxx'.replaceAll('x', () => Math.trunc(16 * Math.random()).toString(16)); + // 4 random base36 chars + const b = Math.random().toString(36).substring(2, 6); + // Date.now() in base62, left-padded to 8 + const c = base62(Date.now()); + + // 5 flag bits -> one base32 char + let bits = '00000'; + if (payload.csl === true) { + bits = setCharAt(bits, 4, '1'); + } + if (payload.ets !== null && payload.ets !== '') { + bits = setCharAt(bits, 3, '1'); + } + if (payload.rid && payload.rid.dev !== null && payload.rid.dev !== 0) { + bits = setCharAt(bits, 1, '1'); + } + const flags = parseInt(bits, 2).toString(32); + + const head = '' + a + b + c + flags + code; + const plain = head + crc32(head).toString(16).padStart(8, '0'); + + let out; + try { + out = base64url(aesEncrypt(plain, 'y3hbnr8d4s2ztjbca1wgxk6mqktf9pxr')); + } catch { + out = ''; + } + return out + '_2'; +}; + +const setCharAt = (str, i, ch) => str.substring(0, i) + ch + str.substring(i + 1); + +const aesEncrypt = (plaintext, keyStr) => { + const key = CryptoJS.lib.WordArray.create(utf8Bytes(keyStr).slice(0, 16)); + const enc = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(plaintext), key, { + iv: key, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + return hexToUint8Array(enc.ciphertext.toString()); // hex string -> bytes +}; + +const base64url = (bytes) => + btoa(String.fromCharCode(...new Uint8Array(bytes))) + .replaceAll('+', '-') + .replaceAll('/', '_') + .replace(/=+$/, ''); +*/ + +const getXmSign = () => { + const head = `${getRandom16(8)}${getRandom16(4)}${base62(Date.now())}0202`; + const key = Buffer.from('y3hbnr8d4s2ztjbca1wgxk6mqktf9pxr').subarray(0, 16); + const cipher = createCipheriv('aes-128-cbc', key, key); + const sid = Buffer.concat([cipher.update(head + crc32(head).toString(16).padStart(8, '0'), 'utf8'), cipher.final()]).toString('base64url'); + return `&&${sid}_2`; +}; + const decryptUrl = (encryptedUrl) => { const o = [ 183, 174, 108, 16, 131, 159, 250, 5, 239, 110, 193, 202, 153, 137, 251, 176, 119, 150, 47, 204, 97, 237, 1, 71, 177, 42, 88, 218, 166, 82, 87, 94, 14, 195, 69, 127, 215, 240, 225, 197, 238, 142, 123, 44, 219, 50, 190, 29, @@ -165,4 +236,4 @@ const decryptUrl = (encryptedUrl) => { } return Buffer.from(decryptedData).toString('utf8'); }; -export { /* getUrl, */ decryptUrl, getRandom16 }; +export { /* getUrl, */ decryptUrl, getRandom16, getXmSign }; From 080796cc4fb7e5552791c309d8c05fe98a25c289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eren=20Ero=C4=9Flu?= <contact@ereneroglu.net> Date: Sat, 1 Aug 2026 06:41:08 +0300 Subject: [PATCH 0472/1190] feat(route): add Omega Scans route (#22904) * feat(route): add Omega Scans route * fix(route): fix improper error, limit and filters --- lib/routes/omegascans/namespace.ts | 9 ++++ lib/routes/omegascans/series.ts | 77 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 lib/routes/omegascans/namespace.ts create mode 100644 lib/routes/omegascans/series.ts diff --git a/lib/routes/omegascans/namespace.ts b/lib/routes/omegascans/namespace.ts new file mode 100644 index 000000000000..7a8df29f0cad --- /dev/null +++ b/lib/routes/omegascans/namespace.ts @@ -0,0 +1,9 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Omega Scans', + url: 'omegascans.org', + description: `::: tip +Omega Scans is a localization team working tirelessly to provide readers with high-quality Comics and Novels to read. +:::`, +}; diff --git a/lib/routes/omegascans/series.ts b/lib/routes/omegascans/series.ts new file mode 100644 index 000000000000..4bb55363b925 --- /dev/null +++ b/lib/routes/omegascans/series.ts @@ -0,0 +1,77 @@ +import NotFoundError from '@/errors/types/not-found'; +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +interface Chapter { + id: number; + chapter_name: string; + chapter_title: string | null; + chapter_thumbnail: string | null; + chapter_slug: string; + price: number; + created_at: string; + series: { + series_slug: string; + id: number; + }; +} + +interface ChapterQueryResponse { + data: Chapter[]; +} + +export const route: Route = { + path: '/series/:id', + name: 'Series Chapters', + url: 'omegascans.org', + maintainers: ['ereneroglum'], + example: '/omegascans/series/632', + parameters: { + id: 'Series ID, can be found in API get request on series page', + }, + categories: ['anime'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + handler: async (ctx) => { + const { id } = ctx.req.param(); + + const response = await ofetch<ChapterQueryResponse>('https://api.omegascans.org/chapter/query', { + query: { + page: 1, + perPage: 30, + series_id: id, + }, + }); + + const chapters = response.data; + if (chapters.length === 0) { + throw new NotFoundError(`Series ${id} not found on Omega Scans`); + } + + const seriesSlug = chapters[0].series.series_slug; + const seriesTitle = seriesSlug.replaceAll('-', ' ').replaceAll(/\b\w/g, (c) => c.toUpperCase()); + const seriesLink = `https://omegascans.org/series/${seriesSlug}`; + + return { + title: `Omega Scans - ${seriesTitle}`, + link: seriesLink, + image: 'https://omegascans.org/wetried_only.png', + item: chapters.map((chapter) => ({ + title: chapter.chapter_title ?? chapter.chapter_name, + link: `https://omegascans.org/series/${chapter.series.series_slug}/${chapter.chapter_slug}`, + pubDate: parseDate(chapter.created_at), + image: chapter.chapter_thumbnail ?? undefined, + guid: `omegascans-chapter-${chapter.id}`, + category: [chapter.price === 0 ? 'Free' : 'Paid'], + })), + }; + }, +}; From b0275386f64b17b6b2e7950307c0bdc6ac32d6e5 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Sun, 2 Aug 2026 23:48:34 +0800 Subject: [PATCH 0473/1190] fix(route/lhratings): support new daily news list template --- lib/routes/lhratings/research.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/lhratings/research.ts b/lib/routes/lhratings/research.ts index b44c602d6005..bbd6d1366881 100644 --- a/lib/routes/lhratings/research.ts +++ b/lib/routes/lhratings/research.ts @@ -19,7 +19,7 @@ export const handler = async (ctx: Context): Promise<Data> => { const $: CheerioAPI = load(response); const language = $('html').attr('lang') ?? 'zh-CN'; - const items: DataItem[] = $('div.xlistNr ul li a') + const items: DataItem[] = $('div.xlistNr ul li a, div.hgjjNr ul li a') .slice(0, limit) .toArray() .map((el): Element => { @@ -44,7 +44,7 @@ export const handler = async (ctx: Context): Promise<Data> => { language, }; - const enclosureUrl: string | undefined = linkUrl; + const enclosureUrl: string | undefined = linkUrl?.endsWith('.pdf') ? linkUrl : undefined; if (enclosureUrl) { processedItem = { From 79ab670b3a9d64fe13c5d8c5ade70aec72d6c586 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Sun, 2 Aug 2026 23:55:25 +0800 Subject: [PATCH 0474/1190] fix(route/bloomberg): switch to official RSS feeds as news sitemaps were removed Bloomberg removed all per-site news sitemaps (/feeds/{site}/sitemap_news.xml now returns 404), which broke every section of this route. - Fetch the official RSS feeds (/feeds/{site}/news.rss) instead, via ofetch so PROXY_URI and the global request layer still apply - Update section IDs to match the feeds Bloomberg currently serves, keeping bpol/bbiz as backward-compatible aliases of politics/business - Drop sections Bloomberg no longer provides (green, pursuits, equality, citylab); add economics, industries and crypto - Keep the full-text pipeline; pass the RSS summary through when the story API is blocked so degraded items still have a description Fixes #22787 --- lib/routes/bloomberg/index.ts | 52 +++++++++++++++++++++++------------ lib/routes/bloomberg/utils.ts | 27 ++---------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/lib/routes/bloomberg/index.ts b/lib/routes/bloomberg/index.ts index b54ea9296718..c341186c49b8 100644 --- a/lib/routes/bloomberg/index.ts +++ b/lib/routes/bloomberg/index.ts @@ -2,29 +2,37 @@ import pMap from 'p-map'; import type { Route } from '@/types'; import { ViewType } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import rssParser from '@/utils/rss-parser'; -import { parseArticle, parseNewsList, rootUrl } from './utils'; +import { parseArticle, rootUrl } from './utils'; const siteTitleMapping = { '/': 'News', - bpol: 'Politics', - bbiz: 'Business', + politics: 'Politics', + business: 'Business', markets: 'Markets', technology: 'Technology', - green: 'Green', wealth: 'Wealth', - pursuits: 'Pursuits', bview: 'Opinion', - equality: 'Equality', businessweek: 'Businessweek', - citylab: 'CityLab', + economics: 'Economics', + industries: 'Industries', + crypto: 'Crypto', +}; + +// Bloomberg removed the per-site news sitemaps; map legacy site IDs to the equivalent RSS feeds +const legacySiteMapping = { + bpol: 'politics', + bbiz: 'business', }; export const route: Route = { path: '/:site?', categories: ['finance'], view: ViewType.Articles, - example: '/bloomberg/bbiz', + example: '/bloomberg/business', parameters: { site: { description: 'Site ID, can be found below', @@ -44,29 +52,37 @@ export const route: Route = { description: `| Site ID | Title | | ------------ | ------------ | | / | News | -| bpol | Politics | -| bbiz | Business | +| politics | Politics | +| business | Business | | markets | Markets | | technology | Technology | -| green | Green | | wealth | Wealth | -| pursuits | Pursuits | | bview | Opinion | -| equality | Equality | | businessweek | Businessweek | -| citylab | CityLab |`, +| economics | Economics | +| industries | Industries | +| crypto | Crypto | + +Legacy site IDs \`bpol\` and \`bbiz\` still work as aliases of \`politics\` and \`business\`.`, handler, }; async function handler(ctx) { const site = ctx.req.param('site'); - const currentUrl = site ? `${rootUrl}/${site}/sitemap_news.xml` : `${rootUrl}/sitemap_news.xml`; + const mappedSite = site ? (legacySiteMapping[site] ?? site) : undefined; + const currentUrl = mappedSite ? `${rootUrl}/${mappedSite}/news.rss` : `${rootUrl}/news.rss`; - const list = await parseNewsList(currentUrl, ctx); + const feed = await rssParser.parseString(await ofetch(currentUrl)); + const list = feed.items.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50).map((item) => ({ + title: item.title, + link: item.link, + pubDate: item.pubDate ? parseDate(item.pubDate) : undefined, + description: item.content, + })); const items = await pMap(list, (item) => parseArticle(item), { concurrency: 1 }); return { - title: `Bloomberg - ${siteTitleMapping[site ?? '/']}`, - link: currentUrl, + title: `Bloomberg - ${siteTitleMapping[mappedSite ?? '/'] ?? feed.title}`, + link: feed.link ?? currentUrl, item: items, }; } diff --git a/lib/routes/bloomberg/utils.ts b/lib/routes/bloomberg/utils.ts index 3237d0bf604a..34e5476a40e0 100644 --- a/lib/routes/bloomberg/utils.ts +++ b/lib/routes/bloomberg/utils.ts @@ -2,7 +2,6 @@ import { load } from 'cheerio'; import { destr } from 'destr'; import cache from '@/utils/cache'; -import got from '@/utils/got'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; @@ -71,28 +70,6 @@ const redirectGot = (url) => }), }); -const parseNewsList = async (url, ctx) => { - const resp = await got(url); - const $ = load(resp.data, { - xml: { - xmlMode: true, - }, - }); - const urls = $('urlset url'); - return urls - .toArray() - .slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50) - .map((u) => { - u = $(u); - const item = { - title: u.find(String.raw`news\:title`).text(), - link: u.find('loc').text(), - pubDate: parseDate(u.find(String.raw`news\:publication_date`).text()), - }; - return item; - }); -}; - const parseArticle = (item) => cache.tryGet(item.link, async () => { const group = regex @@ -119,6 +96,7 @@ const parseArticle = (item) => title: item.title, link: item.link, pubDate: item.pubDate, + description: item.description, }; } } @@ -130,6 +108,7 @@ const parseArticle = (item) => title: item.title, link: item.link, pubDate: item.pubDate, + description: item.description, }; } @@ -611,4 +590,4 @@ const documentToHtmlString = async (document) => { return str; }; -export { parseArticle, parseNewsList, rootUrl }; +export { parseArticle, rootUrl }; From ec501c4e4e6530f5f0aa2b76bbf07521817f6972 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 00:01:12 +0800 Subject: [PATCH 0475/1190] fix(route/freebuf): solve Aliyun WAF acw_sc__v2 challenge Aliyun WAF answers flagged IPs (such as the rsshub.app demo server) with an HTTP 405 JS challenge. Solve it with the shared acw_sc__v2 solver and retry with the cookie, and mark the route as anti-crawler. Fixes #22027 --- lib/routes/freebuf/index.ts | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/routes/freebuf/index.ts b/lib/routes/freebuf/index.ts index 686c654a4025..8d18f0d73ff2 100644 --- a/lib/routes/freebuf/index.ts +++ b/lib/routes/freebuf/index.ts @@ -1,7 +1,11 @@ +import { FetchError } from 'ofetch'; + import type { Route } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { getAcwScV2ByArg1 } from '../5eplay/utils'; + export const route: Route = { path: '/articles/:type', categories: ['blog'], @@ -10,7 +14,7 @@ export const route: Route = { features: { requireConfig: false, requirePuppeteer: false, - antiCrawler: false, + antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, @@ -25,6 +29,8 @@ export const route: Route = { handler, description: `::: tip Freebuf 的文章页面带有反爬虫机制,所以目前无法获取文章的完整内容。 + +站点位于阿里云 WAF 之后,请求频繁的 IP 可能触发 405 JS 质询,此时路由会自动计算 \`acw_sc__v2\` Cookie 并重试。 :::`, }; @@ -49,7 +55,7 @@ async function handler(ctx) { }, }; - const response = await ofetch(fapi, options); + const response = await fetchWithAcwChallenge(fapi, options); const items = response.data.data_list.map((item) => ({ title: item.post_title, @@ -65,3 +71,22 @@ async function handler(ctx) { item: items, }; } + +// 阿里云 WAF 会对被风控的 IP 返回 405 JS 质询,需计算 acw_sc__v2 Cookie 后重试 +async function fetchWithAcwChallenge(url, options) { + try { + return await ofetch(url, options); + } catch (error) { + const arg1 = error instanceof FetchError && typeof error.data === 'string' ? error.data.match(/var arg1='(.*?)';/)?.[1] : undefined; + if (!arg1) { + throw error; + } + return await ofetch(url, { + ...options, + headers: { + ...options.headers, + cookie: `acw_sc__v2=${getAcwScV2ByArg1(arg1)}`, + }, + }); + } +} From baa8d99308c2d092ad649971a267345e041396f0 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 00:13:38 +0800 Subject: [PATCH 0476/1190] fix(route/foresightnews): fetch API via Playwright Amp-Thread-ID: https://ampcode.com/threads/T-019fc331-8a09-7609-bc48-7ffd887a05d6 Co-authored-by: Amp <amp@ampcode.com> --- lib/routes/foresightnews/article.ts | 2 +- lib/routes/foresightnews/column.ts | 2 +- lib/routes/foresightnews/index.ts | 8 ++++++++ lib/routes/foresightnews/news.ts | 2 +- lib/routes/foresightnews/util.tsx | 22 +++++++++++++++++++--- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/routes/foresightnews/article.ts b/lib/routes/foresightnews/article.ts index b54a148819dd..b6146bcd049f 100644 --- a/lib/routes/foresightnews/article.ts +++ b/lib/routes/foresightnews/article.ts @@ -9,7 +9,7 @@ export const route: Route = { parameters: {}, features: { requireConfig: false, - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: false, supportBT: false, supportPodcast: false, diff --git a/lib/routes/foresightnews/column.ts b/lib/routes/foresightnews/column.ts index 094711ed8469..950dd2f003fd 100644 --- a/lib/routes/foresightnews/column.ts +++ b/lib/routes/foresightnews/column.ts @@ -9,7 +9,7 @@ export const route: Route = { parameters: { id: '专栏 id, 可在对应专栏页 URL 中找到' }, features: { requireConfig: false, - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: false, supportBT: false, supportPodcast: false, diff --git a/lib/routes/foresightnews/index.ts b/lib/routes/foresightnews/index.ts index d4268f484dae..680136dca481 100644 --- a/lib/routes/foresightnews/index.ts +++ b/lib/routes/foresightnews/index.ts @@ -12,6 +12,14 @@ export const route: Route = { target: '', }, ], + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, name: '精选资讯', maintainers: ['nczitzk'], handler, diff --git a/lib/routes/foresightnews/news.ts b/lib/routes/foresightnews/news.ts index 3b0c4bbd5626..59eecb430bfa 100644 --- a/lib/routes/foresightnews/news.ts +++ b/lib/routes/foresightnews/news.ts @@ -9,7 +9,7 @@ export const route: Route = { parameters: {}, features: { requireConfig: false, - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: false, supportBT: false, supportPodcast: false, diff --git a/lib/routes/foresightnews/util.tsx b/lib/routes/foresightnews/util.tsx index c1e6fa69d008..649b493ecbcc 100644 --- a/lib/routes/foresightnews/util.tsx +++ b/lib/routes/foresightnews/util.tsx @@ -3,8 +3,8 @@ import zlib from 'node:zlib'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { getPlaywrightPage } from '@/utils/playwright'; const constants = { labelHot: '热门', @@ -40,9 +40,25 @@ const processItems = async (apiUrl, limit, ...parameters) => { column: '', }; - const { data: response } = await got(apiUrl, { - searchParams, + const requestUrl = new URL(apiUrl); + for (const [key, value] of Object.entries(searchParams)) { + requestUrl.searchParams.set(key, String(value)); + } + + // Cloudflare fingerprints the HTTP client, so browser-like headers alone are insufficient. + const { page, destroy } = await getPlaywrightPage(requestUrl.href, { + onBeforeLoad: async (page) => { + await page.route('**/*', (route) => { + route.request().resourceType() === 'document' ? route.continue() : route.abort(); + }); + }, }); + let response; + try { + response = JSON.parse((await page.textContent('body')) ?? ''); + } finally { + await destroy(); + } const buffer = Buffer.from(response.data?.list ?? response.data, 'base64'); let items = JSON.parse(String(zlib.inflateSync(buffer))); From 88201d26da0988bebc4e249edcae409a3364cd36 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 00:26:43 +0800 Subject: [PATCH 0477/1190] feat(route/hypergryph)!: remove stale Arknights announce route The upstream in-game announcement API stopped updating in May 2025. Use /hypergryph/arknights/news/ANNOUNCEMENT instead. --- lib/routes/hypergryph/arknights/announce.ts | 90 --------------------- 1 file changed, 90 deletions(-) delete mode 100644 lib/routes/hypergryph/arknights/announce.ts diff --git a/lib/routes/hypergryph/arknights/announce.ts b/lib/routes/hypergryph/arknights/announce.ts deleted file mode 100644 index 2e6e459174dc..000000000000 --- a/lib/routes/hypergryph/arknights/announce.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { load } from 'cheerio'; - -import { config } from '@/config'; -import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; -import { parseDate } from '@/utils/parse-date'; - -type AnnounceItem = { - announceId: string; - title: string; - isWebUrl: boolean; - webUrl: string; - day: number; - month: number; - group: string; -}; - -export const route: Route = { - path: '/arknights/announce/:platform?/:group?', - categories: ['game'], - example: '/hypergryph/arknights/announce', - parameters: { platform: '平台,默认为 Android', group: '分组,默认为 ALL' }, - name: '明日方舟 - 游戏内公告', - maintainers: ['swwind'], - handler, - description: `平台 - -| 安卓服 | iOS 服 | B 服 | -| :-----: | :----: | :------: | -| Android | IOS | Bilibili | - -分组 - -| 全部 | 系统公告 | 活动公告 | -| :--: | :------: | :------: | -| ALL | SYSTEM | ACTIVITY |`, -}; - -async function handler(ctx) { - const { platform = 'Android', group = 'ALL' } = ctx.req.param(); - - let announceList = (await cache.tryGet( - `hypergryph:arknights:announce_meta:${platform}`, - async () => { - const { announceList } = await ofetch(`https://ak-conf.hypergryph.com/config/prod/announce_meta/${platform}/announcement.meta.json`); - return announceList; - }, - config.cache.routeExpire, - false - )) as AnnounceItem[]; - - if (group !== 'ALL') { - announceList = announceList.filter((item) => item.group === group); - } - - const items = await Promise.all( - announceList.map((item) => - cache.tryGet(item.webUrl, async () => { - const data = await ofetch(item.webUrl); - - const $ = load(data); - let description = - // 一般来讲是有字的 - $('.content').html() ?? - // 有些情况只有一张图 - $('.banner-image-container.cover').html() ?? - // 有些情况啥都没有(暂时没有遇到) - 'No Description'; - - // 游戏内部跳转链接 - description = description.replace(/href="uniwebview:\/\/.+?"/, 'href="#"'); - - return { - title: item.title, - description, - // 不知道是哪一年的,所以不管了 - pubDate: parseDate(`${item.month}-${item.day}`, 'M-D'), - link: item.webUrl, - }; - }) - ) - ); - - return { - title: `《明日方舟》${group === 'SYSTEM' ? '系统' : group === 'ACTIVITY' ? '活动' : '全部'}公告`, - link: 'https://ak.hypergryph.com/', - item: items, - }; -} From 6a265e9d86e66d584157052b38fc5d31526c34a0 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 00:44:50 +0800 Subject: [PATCH 0478/1190] fix: prevent Twitter cache key collisions Use stable operation names for Web API and Developer API user cache keys instead of anonymous callback names. Fixes #22864 --- lib/routes/twitter/api/developer-api/api.ts | 17 +++-- lib/routes/twitter/api/web-api/api.ts | 17 +++-- lib/utils/twitter-cache-key.test.ts | 77 +++++++++++++++++++++ lib/utils/twitter-cache-key.ts | 1 + 4 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 lib/utils/twitter-cache-key.test.ts create mode 100644 lib/utils/twitter-cache-key.ts diff --git a/lib/routes/twitter/api/developer-api/api.ts b/lib/routes/twitter/api/developer-api/api.ts index 420f356ce812..01328795a0f4 100644 --- a/lib/routes/twitter/api/developer-api/api.ts +++ b/lib/routes/twitter/api/developer-api/api.ts @@ -5,6 +5,7 @@ import { config } from '@/config'; import ConfigNotFoundError from '@/errors/types/config-not-found'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import cache from '@/utils/cache'; +import { getTwitterUserCacheKey } from '@/utils/twitter-cache-key'; interface ClientWrapper { client: TwitterApiReadOnly; @@ -217,16 +218,14 @@ const getUserData = (id: string) => return mapUserToLegacy(response?.data); }); -const cacheTryGet = async (_id: string, params: Record<string, any> | undefined, func: (id: string, params?: Record<string, any>) => Promise<any>) => { +const cacheTryGet = async (_id: string, params: Record<string, any> | undefined, operationName: string, func: (id: string, params?: Record<string, any>) => Promise<any>) => { const userData: any = await getUserData(_id); const id = userData?.id_str; if (id === undefined) { cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire); throw new InvalidParameterError('User not found'); } - const funcName = func.name; - const paramsString = JSON.stringify(params); - return cache.tryGet(`twitter:${id}:${funcName}:${paramsString}`, () => func(id, params), config.cache.routeExpire, false); + return cache.tryGet(getTwitterUserCacheKey(id, operationName, params), () => func(id, params), config.cache.routeExpire, false); }; const getUserTimeline = async (id: string, params?: Record<string, any>, options: Record<string, any> = {}) => { @@ -242,18 +241,18 @@ const getUserTimeline = async (id: string, params?: Record<string, any>, options return mapTweetResponseToLegacy(response); }; -const getUserTweets = (id: string, params?: Record<string, any>) => cacheTryGet(id, params, (id, params = {}) => getUserTimeline(id, params, { exclude: 'replies' })); +const getUserTweets = (id: string, params?: Record<string, any>) => cacheTryGet(id, params, 'getUserTweets', (id, params = {}) => getUserTimeline(id, params, { exclude: 'replies' })); -const getUserTweetsAndReplies = (id: string, params?: Record<string, any>) => cacheTryGet(id, params, (id, params = {}) => getUserTimeline(id, params)); +const getUserTweetsAndReplies = (id: string, params?: Record<string, any>) => cacheTryGet(id, params, 'getUserTweetsAndReplies', (id, params = {}) => getUserTimeline(id, params)); const getUserMedia = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => { + cacheTryGet(id, params, 'getUserMedia', async (id, params = {}) => { const data = await getUserTimeline(id, params); return data.filter((tweet) => tweet.extended_entities?.media); }); const getUserLikes = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => { + cacheTryGet(id, params, 'getUserLikes', async (id, params = {}) => { const client = await getAppClient(); const response = await client.v2.get(`users/${id}/liked_tweets`, { max_results: params.count ?? 20, @@ -266,7 +265,7 @@ const getUserLikes = (id: string, params?: Record<string, any>) => }); const getUserTweet = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (_id, params = {}) => { + cacheTryGet(id, params, 'getUserTweet', async (_id, params = {}) => { const client = await getAppClient(); const tweetId = params.focalTweetId; if (!tweetId) { diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts index 84d9208b7092..ea787933a675 100644 --- a/lib/routes/twitter/api/web-api/api.ts +++ b/lib/routes/twitter/api/web-api/api.ts @@ -2,6 +2,7 @@ import { config } from '@/config'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; +import { getTwitterUserCacheKey } from '@/utils/twitter-cache-key'; import { baseUrl, gqlFeatures, gqlMap, initGqlMap } from './constants'; import { gatherLegacyFromData, paginationTweets, twitterGot } from './utils'; @@ -41,20 +42,18 @@ const getUserData = (id) => }); }); -const cacheTryGet = async (_id, params, func) => { +const cacheTryGet = async (_id, params, operationName, func) => { const userData: any = await getUserData(_id); const id = (userData.data?.user || userData.data?.user_result)?.result?.rest_id; if (id === undefined) { cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire); throw new InvalidParameterError('User not found'); } - const funcName = func.name; - const paramsString = JSON.stringify(params); - return cache.tryGet(`twitter:${id}:${funcName}:${paramsString}`, () => func(id, params), config.cache.routeExpire, false); + return cache.tryGet(getTwitterUserCacheKey(id, operationName, params), () => func(id, params), config.cache.routeExpire, false); }; const getUserTweets = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => + cacheTryGet(id, params, 'getUserTweets', async (id, params = {}) => gatherLegacyFromData( await paginationTweets('UserTweets', id, { ...params, @@ -68,7 +67,7 @@ const getUserTweets = (id: string, params?: Record<string, any>) => ); const getUserTweetsAndReplies = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => + cacheTryGet(id, params, 'getUserTweetsAndReplies', async (id, params = {}) => gatherLegacyFromData( await paginationTweets('UserTweetsAndReplies', id, { ...params, @@ -84,7 +83,7 @@ const getUserTweetsAndReplies = (id: string, params?: Record<string, any>) => ); const getUserMedia = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => + cacheTryGet(id, params, 'getUserMedia', async (id, params = {}) => gatherLegacyFromData( await paginationTweets('UserMedia', id, { ...params, @@ -99,7 +98,7 @@ const getUserMedia = (id: string, params?: Record<string, any>) => ); const getUserLikes = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => + cacheTryGet(id, params, 'getUserLikes', async (id, params = {}) => gatherLegacyFromData( await paginationTweets('Likes', id, { ...params, @@ -113,7 +112,7 @@ const getUserLikes = (id: string, params?: Record<string, any>) => ); const getUserTweet = (id: string, params?: Record<string, any>) => - cacheTryGet(id, params, async (id, params = {}) => + cacheTryGet(id, params, 'getUserTweet', async (id, params = {}) => gatherLegacyFromData( await paginationTweets( 'TweetDetail', diff --git a/lib/utils/twitter-cache-key.test.ts b/lib/utils/twitter-cache-key.test.ts new file mode 100644 index 000000000000..4c0dd9ca2b49 --- /dev/null +++ b/lib/utils/twitter-cache-key.test.ts @@ -0,0 +1,77 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import developerApi from '@/routes/twitter/api/developer-api/api'; +import webApi from '@/routes/twitter/api/web-api/api'; +import { getTwitterUserCacheKey } from '@/utils/twitter-cache-key'; + +const mocks = vi.hoisted(() => ({ + tryGet: vi.fn(), + set: vi.fn(), + userData: undefined as unknown, +})); + +vi.mock('@/utils/cache', () => ({ + default: { + clients: { + redisClient: null, + }, + tryGet: mocks.tryGet, + set: mocks.set, + }, +})); + +beforeEach(() => { + mocks.tryGet.mockReset(); + mocks.set.mockReset(); + mocks.tryGet.mockImplementation((key: string) => Promise.resolve(key.startsWith('twitter-userdata-') ? mocks.userData : key)); +}); + +describe('Twitter user timeline cache keys', () => { + it('preserves the existing user and parameter key segments', () => { + expect(getTwitterUserCacheKey('123', 'getUserTweets', { count: 17 })).toBe('twitter:123:getUserTweets:{"count":17}'); + }); + + it('separates web API operations for the same user and parameters', async () => { + mocks.userData = { data: { user: { result: { rest_id: '123' } } } }; + const params = { count: 17 }; + + const keys = await Promise.all([ + webApi.getUserTweets('RSSHub', params), + webApi.getUserTweetsAndReplies('RSSHub', params), + webApi.getUserMedia('RSSHub', params), + webApi.getUserLikes('RSSHub', params), + webApi.getUserTweet('RSSHub', params), + ]); + + expect(keys).toEqual([ + 'twitter:123:getUserTweets:{"count":17}', + 'twitter:123:getUserTweetsAndReplies:{"count":17}', + 'twitter:123:getUserMedia:{"count":17}', + 'twitter:123:getUserLikes:{"count":17}', + 'twitter:123:getUserTweet:{"count":17}', + ]); + expect(new Set(keys).size).toBe(keys.length); + }); + + it('separates developer API operations for the same user and parameters', async () => { + mocks.userData = { id_str: '123' }; + const params = { count: 17 }; + + const keys = await Promise.all([ + developerApi.getUserTweets('RSSHub', params), + developerApi.getUserTweetsAndReplies('RSSHub', params), + developerApi.getUserMedia('RSSHub', params), + developerApi.getUserLikes('RSSHub', params), + developerApi.getUserTweet('RSSHub', params), + ]); + + expect(keys).toEqual([ + 'twitter:123:getUserTweets:{"count":17}', + 'twitter:123:getUserTweetsAndReplies:{"count":17}', + 'twitter:123:getUserMedia:{"count":17}', + 'twitter:123:getUserLikes:{"count":17}', + 'twitter:123:getUserTweet:{"count":17}', + ]); + expect(new Set(keys).size).toBe(keys.length); + }); +}); diff --git a/lib/utils/twitter-cache-key.ts b/lib/utils/twitter-cache-key.ts new file mode 100644 index 000000000000..a5bd890c013b --- /dev/null +++ b/lib/utils/twitter-cache-key.ts @@ -0,0 +1 @@ +export const getTwitterUserCacheKey = (id: string, operationName: string, params: Record<string, unknown> | undefined) => `twitter:${id}:${operationName}:${JSON.stringify(params)}`; From b879d8d7fbf7702ff0453f2b3cdcc4436fa6ac51 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 08:43:25 +0800 Subject: [PATCH 0479/1190] fix(route/twitter): include list conversations --- lib/routes/twitter/api/web-api/api.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts index ea787933a675..7954772082b1 100644 --- a/lib/routes/twitter/api/web-api/api.ts +++ b/lib/routes/twitter/api/web-api/api.ts @@ -158,7 +158,8 @@ const getList = async (id: string, params?: Record<string, any>) => count: 20, }, ['list', 'tweets_timeline', 'timeline'] - ) + ), + ['listConversation-'] ); const getUser = async (id: string) => { From 9caca197b34f3ec6c0df911723d0b7495539820b Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 09:32:31 +0800 Subject: [PATCH 0480/1190] fix(route/gov): update govall search API --- lib/gov-zhengce-govall.spec.ts | 251 +++++++++++++++++++++++++++++++ lib/routes/gov/zhengce/govall.ts | 201 ++++++++++++++++++++----- 2 files changed, 411 insertions(+), 41 deletions(-) create mode 100644 lib/gov-zhengce-govall.spec.ts diff --git a/lib/gov-zhengce-govall.spec.ts b/lib/gov-zhengce-govall.spec.ts new file mode 100644 index 000000000000..db5d135f7f41 --- /dev/null +++ b/lib/gov-zhengce-govall.spec.ts @@ -0,0 +1,251 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { describe, expect, it } from 'vitest'; + +import type { Data } from '@/types'; + +import { route } from './routes/gov/zhengce/govall'; + +const apiUrl = 'https://sousuoht.www.gov.cn/athena/forward/2B22E8E39E850E17F95A016A74FCB6B673336FA8B6FEC0E2955907EF9AEE06BE'; +const articleUrl = 'http://www.gov.cn/gongbao/content/2009/content_1322126.htm'; + +describe('gov.cn information search route', () => { + it('supports the documented legacy advanced-search parameters', async () => { + const { default: server } = await import('@/setup.test'); + let requestBody: any; + + server.use( + http.get('http://sousuo.gov.cn/list.htm', () => HttpResponse.text('<html></html>')), + http.post(apiUrl, async ({ request }) => { + requestBody = await request.json(); + + expect(request.headers.get('athenaAppKey')).toBeTruthy(); + expect(request.headers.get('athenaAppName')).toBe(encodeURIComponent('国网搜索')); + + return HttpResponse.json({ + resultCode: { + code: 200, + }, + result: { + data: { + middle: { + list: [ + { + title: '中华人民共和国国务院令(第<em>555</em>号)<br>  流动人口计划生育工作条例', + title_no_tag: '中华人民共和国国务院令(第555号)<br>  流动人口计划生育工作条例', + url: articleUrl, + summary: '搜索接口摘要', + time: '2009-05-30 23:59:59', + }, + ], + }, + }, + }, + }); + }), + http.get(articleUrl, () => HttpResponse.text('<div id="UCAP-CONTENT"><p>文章全文</p></div>')) + ); + + const ctx = { + req: { + param: (name: string) => (name === 'advance' ? 'orpro=555¬pro=2&search_field=title' : undefined), + }, + } as unknown as Context; + const result = (await route.handler(ctx)) as Data; + + expect(requestBody).toMatchObject({ + code: '17da70961a7', + dataTypeId: '107', + orderBy: 'time', + searchBy: 'title', + pageNo: 1, + pageSize: 20, + isDefaultAdvanced: 1, + isAdvancedSearch: 1, + advancedFilters: [ + { + fieldName: 'containsAll', + searchWord: [], + }, + { + fieldName: 'containsOne', + searchWord: ['555'], + }, + { + fieldName: 'none', + searchWord: ['2'], + }, + ], + }); + expect(result.link).toMatch(/^https:\/\/sousuo\.www\.gov\.cn\/sousuo\/search\.shtml\?/); + expect(result.item).toEqual([ + { + title: '中华人民共和国国务院令(第555号) 流动人口计划生育工作条例', + link: articleUrl, + description: '<p>文章全文</p>', + pubDate: new Date('2009-05-30T15:59:59.000Z'), + }, + ]); + }); + + it('maps legacy keyword and date filters to the Athena request', async () => { + const { default: server } = await import('@/setup.test'); + let requestBody: any; + + server.use( + http.post(apiUrl, async ({ request }) => { + requestBody = await request.json(); + + return HttpResponse.json({ + resultCode: { + code: 200, + }, + result: { + data: { + middle: { + list: [], + }, + }, + }, + }); + }) + ); + + const ctx = { + req: { + param: (name: string) => + name === 'advance' + ? 'allpro=%E5%8C%BB%E7%96%97+%E4%BF%9D%E9%9A%9C&inpro=%E5%AE%8C%E6%95%B4+%E7%9F%AD%E8%AF%AD&orpro=%E5%8C%BB%E4%BF%9D+%E7%A4%BE%E4%BF%9D¬pro=%E6%95%99%E8%82%B2&searchfield=content&pubmintimeYear=2009&pubmintimeMonth=5&pubmaxtimeYear=2009&pubmaxtimeMonth=5' + : undefined, + }, + } as unknown as Context; + await route.handler(ctx); + + expect(requestBody).toMatchObject({ + searchBy: 'all', + granularity: 'CUSTOM', + beginDateTime: Date.UTC(2009, 4, 1) - 8 * 60 * 60 * 1000, + endDateTime: Date.UTC(2009, 5, 1) - 8 * 60 * 60 * 1000 - 1, + advancedFilters: [ + { + fieldName: 'containsAll', + searchWord: ['医疗', '保障', '完整 短语'], + }, + { + fieldName: 'containsOne', + searchWord: ['医保', '社保'], + }, + { + fieldName: 'none', + searchWord: ['教育'], + }, + ], + }); + }); + + it('requests the latest items when advanced-search parameters are omitted', async () => { + const { default: server } = await import('@/setup.test'); + let requestBody: any; + + server.use( + http.post(apiUrl, async ({ request }) => { + requestBody = await request.json(); + + return HttpResponse.json({ + resultCode: { + code: 200, + }, + result: { + data: { + middle: { + list: [], + }, + }, + }, + }); + }) + ); + + const ctx = { + req: { + param: () => {}, + }, + } as unknown as Context; + const result = (await route.handler(ctx)) as Data; + + expect(requestBody).toMatchObject({ + allData: true, + pageNo: 1, + pageSize: 20, + searchBy: 'all', + }); + expect(requestBody).not.toHaveProperty('advancedFilters'); + expect(result.link).toContain('allData=true'); + }); + + it('uses API content when an article page cannot be fetched', async () => { + const { default: server } = await import('@/setup.test'); + const unavailableArticleUrl = 'https://www.gov.cn/test/unavailable-article.htm'; + + server.use( + http.post(apiUrl, () => + HttpResponse.json({ + resultCode: { + code: 200, + }, + result: { + data: { + middle: { + list: [ + { + title: '<em>测试</em><br/>标题', + url: unavailableArticleUrl, + content: '接口正文', + }, + ], + }, + }, + }, + }) + ), + http.get(unavailableArticleUrl, () => new HttpResponse(null, { status: 500 })) + ); + + const ctx = { + req: { + param: () => {}, + }, + } as unknown as Context; + const result = (await route.handler(ctx)) as Data; + + expect(result.item).toEqual([ + { + title: '测试 标题', + link: unavailableArticleUrl, + description: '接口正文', + }, + ]); + }); + + it('reports an actionable error when the search API fails', async () => { + const { default: server } = await import('@/setup.test'); + + server.use( + http.post(apiUrl, () => + HttpResponse.json({ + resultCode: { + code: 1000, + }, + }) + ) + ); + + const ctx = { + req: { + param: () => {}, + }, + } as unknown as Context; + + await expect(route.handler(ctx)).rejects.toThrow('中国政府网搜索接口请求失败,错误代码:1000'); + }); +}); diff --git a/lib/routes/gov/zhengce/govall.ts b/lib/routes/gov/zhengce/govall.ts index c255fefa63f3..68fa2da4d99e 100644 --- a/lib/routes/gov/zhengce/govall.ts +++ b/lib/routes/gov/zhengce/govall.ts @@ -1,11 +1,136 @@ +import { constants, publicEncrypt } from 'node:crypto'; + import { load } from 'cheerio'; -import type { Route } from '@/types'; +import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +const searchCode = '17da70961a7'; +const dataTypeId = '107'; +const searchPageUrl = 'https://sousuo.www.gov.cn/sousuo/search.shtml'; +const searchApiUrl = 'https://sousuoht.www.gov.cn/athena/forward/2B22E8E39E850E17F95A016A74FCB6B673336FA8B6FEC0E2955907EF9AEE06BE'; + +// These values are embedded in the public search page's JavaScript and are not private credentials. +const athenaAppCredential = 'a46884b2013e4d189f2a8e2d49a23525'; +const athenaPublicKey = `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCSMhMJQ+XLI7oW0k9Bwufur4Ag40tcsrzT7WZf6Ao0O/hyY1gZtCSYFxkxIZUXjW46j27XSW8IDX1rTJoHaMxHCWsOpTi2W5stybGYZytsY5on8gd8AIaS1d52h9eaS2TFydtJJtE50xHmT0WmoyoinWCuVCOkdCLhh9b9jSdeSQIDAQAB +-----END PUBLIC KEY-----`; + +const splitWords = (value: string | null) => value?.split(/\s+/).filter(Boolean) ?? []; + +const getMonthBoundary = (yearValue: string | null, monthValue: string | null, end: boolean) => { + const year = Number(yearValue); + const month = monthValue ? Number(monthValue) : end ? 12 : 1; + + if (!Number.isSafeInteger(year) || !Number.isSafeInteger(month) || year < 1000 || year > 9999 || month < 1 || month > 12) { + return ''; + } + + const chinaOffset = 8 * 60 * 60 * 1000; + return end ? Date.UTC(year, month, 1) - chinaOffset - 1 : Date.UTC(year, month - 1, 1) - chinaOffset; +}; + +const buildSearchRequest = (advance?: string) => { + const params = new URLSearchParams(advance); + const searchField = params.get('search_field') ?? params.get('searchfield'); + const beginDateTime = getMonthBoundary(params.get('pubmintimeYear'), params.get('pubmintimeMonth'), false); + const endDateTime = getMonthBoundary(params.get('pubmaxtimeYear'), params.get('pubmaxtimeMonth'), true); + const isAdvancedSearch = Boolean(advance); + + return { + code: searchCode, + historySearchWords: [], + dataTypeId, + orderBy: 'time', + searchBy: searchField === 'title' ? 'title' : 'all', + appendixType: '', + granularity: beginDateTime || endDateTime ? 'CUSTOM' : 'ALL', + trackTotalHits: true, + beginDateTime, + endDateTime, + isSearchForced: 0, + filters: [], + pageNo: 1, + pageSize: 20, + ...(isAdvancedSearch + ? { + isDefaultAdvanced: 1, + advancedFilters: [ + { + fieldId: '', + fieldName: 'containsAll', + searchWord: [...splitWords(params.get('allpro')), ...(params.get('inpro') ? [params.get('inpro')] : [])], + }, + { + fieldId: '', + fieldName: 'containsOne', + searchWord: splitWords(params.get('orpro')), + }, + { + fieldId: '', + fieldName: 'none', + searchWord: splitWords(params.get('notpro')), + }, + ], + customFilter: { + operator: 'and', + properties: [], + }, + isAdvancedSearch: 1, + } + : { + allData: true, + customFilter: { + operator: 'and', + properties: [], + }, + }), + }; +}; + +const buildSearchLink = (request: ReturnType<typeof buildSearchRequest>) => { + const url = new URL(searchPageUrl); + const fields = ['code', 'dataTypeId', 'orderBy', 'searchBy', 'granularity', 'beginDateTime', 'endDateTime', 'allData', 'isDefaultAdvanced', 'advancedFilters'] as const; + + for (const field of fields) { + const value = request[field]; + if (value !== undefined && value !== '') { + url.searchParams.set(field, typeof value === 'string' ? value : JSON.stringify(value)); + } + } + + return url.href; +}; + +const normalizeSearchItem = (item): DataItem => { + const title = load((item.title_no_tag || item.title || '').replaceAll(/<br\s*\/?>/gi, ' ')) + .text() + .replaceAll(/\s+/g, ' '); + + return { + title, + link: item.url, + description: item.summary || item.content, + ...(item.time && { + pubDate: timezone(parseDate(item.time, 'YYYY-MM-DD HH:mm:ss'), 8), + }), + }; +}; + +const getAthenaAppKey = () => + encodeURIComponent( + publicEncrypt( + { + key: athenaPublicKey, + padding: constants.RSA_PKCS1_PADDING, + }, + Buffer.from(athenaAppCredential) + ).toString('base64') + ); + export const route: Route = { path: '/govall/:advance?', categories: ['government'], @@ -29,65 +154,59 @@ export const route: Route = { maintainers: ['ciaranchen'], handler, url: 'www.gov.cn/', - description: `| 选项 | 意义 | 备注 | -| :-----------------------------: | :----------------------------------------------: | :----------------------------: | -| orpro | 包含以下任意一个关键词。 | 用空格分隔。 | -| allpro | 包含以下全部关键词 | | -| notpro | 不包含以下关键词 | | -| inpro | 完整不拆分的关键词 | | -| searchfield | title: 搜索词在标题中;content: 搜索词在正文中。 | 默认为空,即网页的任意位置。 | -| pubmintimeYear, pubmintimeMonth | 从某年某月 | 单独使用月份参数无法只筛选月份 | -| pubmaxtimeYear, pubmaxtimeMonth | 到某年某月 | 单独使用月份参数无法只筛选月份 | -| colid | 栏目 | 比较复杂,不建议使用 |`, + description: `| 选项 | 意义 | 备注 | +| :-----------------------------: | :----------------------------------------------: | :----------------------------------------: | +| orpro | 包含以下任意一个关键词。 | 用空格分隔。 | +| allpro | 包含以下全部关键词 | | +| notpro | 不包含以下关键词 | | +| inpro | 完整不拆分的关键词 | | +| searchfield | title: 搜索词在标题中;content: 搜索词在正文中。 | 上游已不支持仅正文,content 将搜索全部位置 | +| pubmintimeYear, pubmintimeMonth | 从某年某月 | 单独使用月份参数无法只筛选月份 | +| pubmaxtimeYear, pubmaxtimeMonth | 到某年某月 | 单独使用月份参数无法只筛选月份 | +| colid | 栏目 | 上游新接口已不再支持 |`, }; async function handler(ctx) { const advance = ctx.req.param('advance'); - const link = 'http://sousuo.gov.cn/list.htm'; - const params = new URLSearchParams({ - n: 20, - t: 'govall', - sort: 'pubtime', - advance: 'true', - }); - const query = `${params.toString()}&${advance}`; - const res = await got.get(link, { - searchParams: query.replaceAll(/[\u{4E00}-\u{9FA5}]/gu, (str) => encodeURIComponent(str)), + const request = buildSearchRequest(advance); + const link = buildSearchLink(request); + const { data: response } = await got.post(searchApiUrl, { + headers: { + athenaAppKey: getAthenaAppKey(), + athenaAppName: encodeURIComponent('国网搜索'), + }, + json: request, }); - const $ = load(res.data); - - const list = $('body > div.dataBox > table > tbody > tr') - .slice(1) - .toArray() - .map((elem) => { - elem = $(elem); - return { - title: elem.find('td:nth-child(2) > a').text(), - link: elem.find('td:nth-child(2) > a').attr('href'), - pubDate: timezone(parseDate(elem.find('td:nth-child(5)').text()), 8), - }; - }); + + if (response?.resultCode?.code !== 200 || !Array.isArray(response?.result?.data?.middle?.list)) { + throw new Error(`中国政府网搜索接口请求失败,错误代码:${response?.resultCode?.code ?? '未知'}`); + } + + const list = response.result.data.middle.list.filter((item) => item.url).map((item) => normalizeSearchItem(item)); const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - let description: string; + let description = item.description; try { const contentData = await got(item.link); - const $ = load(contentData.data); - description = $('#UCAP-CONTENT').html(); + const content = load(contentData.data); + description = content('#UCAP-CONTENT, div.TRS_UEDITOR').first().html() || description; } catch { - description = '文章已被删除'; + // Keep the API summary when the article page is unavailable. } - item.description = description; - return item; + + return { + ...item, + description, + }; }) ) ); return { title: '信息稿件 - 中国政府网', - link: `${link}?${query}`, + link, item: items, }; } From 3372b4f285c88896eb95589483b8472adf9ccaa9 Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 10:53:20 +0800 Subject: [PATCH 0481/1190] fix(route/people): restore People's Daily paper feed Add a dedicated /people/paper route for the current digital edition, including page selection, bounded detail fetching, caching, and regression tests. --- lib/people-paper.spec.ts | 171 +++++++++++++++++++++++++++++++++++++ lib/routes/people/paper.ts | 152 +++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 lib/people-paper.spec.ts create mode 100644 lib/routes/people/paper.ts diff --git a/lib/people-paper.spec.ts b/lib/people-paper.spec.ts new file mode 100644 index 000000000000..90e18ac98beb --- /dev/null +++ b/lib/people-paper.spec.ts @@ -0,0 +1,171 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import { route } from '@/routes/people/paper'; +import type { Data } from '@/types'; +import cache from '@/utils/cache'; + +const rootUrl = 'https://paper.people.com.cn/rmrb/pc/'; +const indexUrl = `${rootUrl}layout/index.html`; +const pageOneUrl = `${rootUrl}layout/202608/03/node_01.html`; +const pageTwoUrl = `${rootUrl}layout/202608/03/node_02.html`; +const articleOneUrl = `${rootUrl}content/202608/03/content_1.html`; +const articleTwoUrl = `${rootUrl}content/202608/03/content_2.html`; +const articleThreeUrl = `${rootUrl}content/202608/03/content_3.html`; + +const indexHtml = ` + <ul id="list"> + <li><a href="202608/03/node_01.html">第01版 要闻</a></li> + <li><a href="202608/03/node_02.html">第02版 评论</a></li> + </ul> +`; + +const pageOneHtml = ` + <ul class="news-list"> + <li><a href="../../../content/202608/03/content_1.html">列表标题一</a></li> + <li><a href="../../../content/202608/03/content_2.html">列表标题二</a></li> + </ul> +`; + +const pageTwoHtml = ` + <ul class="news-list"> + <li><a href="../../../content/202608/03/content_3.html">列表标题三</a></li> + </ul> +`; + +function createArticleHtml(title: string, author: string, page: string) { + return ` + <div class="article"> + <h1>${title}</h1> + <p class="sec"> + ${author} + <span class="date"> + 《人民日报》(<span class="newstime">2026年08月03日</span> 第 ${page} 版) + </span> + </p> + <div id="ozoom"> + <p> + 正文 ${title} + <img src="../../../pic/202608/03/image.jpg"> + <a href="../../../content/202608/03/source.html">相关链接</a> + </p> + </div> + </div> + `; +} + +function createCtx(page?: string, limit?: string) { + return { + req: { + param: (name: string) => (name === 'page' ? page : undefined), + query: (name: string) => (name === 'limit' ? limit : undefined), + }, + } as unknown as Context; +} + +describe('GET /people/paper', () => { + beforeEach(() => cache.clients.memoryCache?.clear()); + + it.each([ + ['the default route', undefined], + ['the explicit all route', 'all'], + ])('aggregates all pages and applies limit before fetching article details for %s', async (_, page) => { + const { default: server } = await import('@/setup.test'); + const articleThreeHandler = vi.fn(() => HttpResponse.html(createArticleHtml('正文标题三', '记者三', '02'))); + + server.use( + http.get(indexUrl, () => HttpResponse.html(indexHtml)), + http.get(pageOneUrl, () => HttpResponse.html(pageOneHtml)), + http.get(pageTwoUrl, () => HttpResponse.html(pageTwoHtml)), + http.get(articleOneUrl, () => HttpResponse.html(createArticleHtml('正文标题一', '记者一', '01'))), + http.get(articleTwoUrl, () => HttpResponse.html(createArticleHtml('正文标题二', '记者二', '01'))), + http.get(articleThreeUrl, articleThreeHandler) + ); + + const feed = (await route.handler(createCtx(page, '2'))) as Data; + + expect(feed.title).toBe('人民日报电子版 - 2026年08月03日'); + expect(feed.link).toBe(indexUrl); + expect(feed.item).toHaveLength(2); + expect(feed.item?.[0]).toMatchObject({ + title: '正文标题一', + link: articleOneUrl, + author: '记者一', + category: ['第01版 要闻'], + }); + expect(new Date(feed.item?.[0].pubDate ?? '').getFullYear()).toBe(2026); + expect(feed.item?.[0].description).toContain('正文 正文标题一'); + expect(feed.item?.[0].description).toContain(`${rootUrl}pic/202608/03/image.jpg`); + expect(feed.item?.[0].description).toContain(`${rootUrl}content/202608/03/source.html`); + expect(articleThreeHandler).not.toHaveBeenCalled(); + }); + + it('fetches only the requested page', async () => { + const { default: server } = await import('@/setup.test'); + const pageOneHandler = vi.fn(() => HttpResponse.html(pageOneHtml)); + + server.use( + http.get(indexUrl, () => HttpResponse.html(indexHtml)), + http.get(pageOneUrl, pageOneHandler), + http.get(pageTwoUrl, () => HttpResponse.html(pageTwoHtml)), + http.get(articleThreeUrl, () => HttpResponse.html(createArticleHtml('正文标题三', '记者三', '02'))) + ); + + const feed = (await route.handler(createCtx('02', '3'))) as Data; + + expect(feed.title).toBe('人民日报电子版 - 第02版 评论 - 2026年08月03日'); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '正文标题三', + link: articleThreeUrl, + category: ['第02版 评论'], + }); + expect(pageOneHandler).not.toHaveBeenCalled(); + }); + + it('limits the default route to 30 articles before fetching details', async () => { + const { default: server } = await import('@/setup.test'); + const articleLinks = Array.from({ length: 31 }, (_, index) => `<li><a href="../../../content/202608/03/default_${index + 1}.html">文章 ${index + 1}</a></li>`).join(''); + const detailHandler = vi.fn(({ request }: { request: Request }) => HttpResponse.html(createArticleHtml(new URL(request.url).pathname, '记者', '01'))); + + server.use( + http.get(indexUrl, () => HttpResponse.html('<ul id="list"><li><a href="202608/03/node_01.html">第01版 要闻</a></li></ul>')), + http.get(pageOneUrl, () => HttpResponse.html(`<ul class="news-list">${articleLinks}</ul>`)), + http.get(new RegExp(`${rootUrl}content/202608/03/default_\\d+\\.html`), detailHandler) + ); + + const feed = (await route.handler(createCtx())) as Data; + + expect(feed.item).toHaveLength(30); + expect(detailHandler).toHaveBeenCalledTimes(30); + }); + + it('keeps list metadata when one article detail request fails', async () => { + const { default: server } = await import('@/setup.test'); + server.use( + http.get(indexUrl, () => HttpResponse.html('<ul id="list"><li><a href="202608/03/node_01.html">第01版 要闻</a></li></ul>')), + http.get(pageOneUrl, () => HttpResponse.html('<ul class="news-list"><li><a href="../../../content/202608/03/content_1.html">列表标题一</a></li></ul>')), + http.get(articleOneUrl, () => HttpResponse.text('upstream failure', { status: 500 })) + ); + + const feed = (await route.handler(createCtx())) as Data; + + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '列表标题一', + link: articleOneUrl, + category: ['第01版 要闻'], + }); + expect(feed.item?.[0].description).toBeUndefined(); + }); + + it('rejects a page that is not present in the current edition', async () => { + const { default: server } = await import('@/setup.test'); + server.use(http.get(indexUrl, () => HttpResponse.html(indexHtml))); + + await expect(route.handler(createCtx('99'))).rejects.toBeInstanceOf(InvalidParameterError); + await expect(route.handler(createCtx('99'))).rejects.toThrow('Invalid page'); + }); +}); diff --git a/lib/routes/people/paper.ts b/lib/routes/people/paper.ts new file mode 100644 index 000000000000..653d8693370a --- /dev/null +++ b/lib/routes/people/paper.ts @@ -0,0 +1,152 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const rootUrl = 'https://paper.people.com.cn/rmrb/pc/'; +const indexUrl = `${rootUrl}layout/index.html`; +const defaultLimit = 30; + +type PaperPage = { + id: string; + title: string; + url: string; +}; + +type PaperArticle = DataItem & { + link: string; +}; + +const normalizeText = (text: string) => text.replaceAll(/\s+/g, ' ').trim(); + +const fetchHtml = (url: string) => cache.tryGet(url, () => ofetch<string>(url)); + +const getEditionDate = (url: string) => { + const match = url.match(/\/(\d{4})(\d{2})\/(\d{2})\//); + if (!match) { + throw new Error('Unable to determine the current People’s Daily edition date'); + } + return `${match[1]}年${match[2]}月${match[3]}日`; +}; + +const getPages = async () => { + const html = await fetchHtml(indexUrl); + const $ = load(html); + const pages = $('#list li a[href]') + .toArray() + .map((element) => { + const href = $(element).attr('href') ?? ''; + const id = href.match(/node_(\d+)\.html/)?.[1]; + return id + ? { + id, + title: normalizeText($(element).text()), + url: new URL(href, indexUrl).href, + } + : undefined; + }) + .filter((page): page is PaperPage => Boolean(page)); + + if (pages.length === 0) { + throw new Error('No pages found in the current People’s Daily edition'); + } + return pages; +}; + +const getArticles = async (page: PaperPage, pubDate: Date): Promise<PaperArticle[]> => { + const html = await fetchHtml(page.url); + const $ = load(html); + return $('.news-list a[href]') + .toArray() + .map((element) => ({ + title: normalizeText($(element).text()), + link: new URL($(element).attr('href') ?? '', page.url).href, + category: [page.title], + pubDate, + })); +}; + +const getArticleDetail = async (article: PaperArticle): Promise<PaperArticle> => { + try { + return await cache.tryGet(article.link, async () => { + const html = await ofetch<string>(article.link); + const $ = load(html); + const content = $('#ozoom'); + + content.find('img[src]').each((_, image) => { + const src = $(image).attr('src'); + if (src) { + $(image).attr('src', new URL(src, article.link).href); + } + }); + content.find('a[href]').each((_, anchor) => { + const href = $(anchor).attr('href'); + if (href) { + $(anchor).attr('href', new URL(href, article.link).href); + } + }); + + const byline = $('.article > .sec').clone(); + byline.find('.date').remove(); + const author = normalizeText(byline.text()); + const date = normalizeText($('.article > .sec .newstime').first().text()); + + return { + ...article, + title: normalizeText($('.article > h1').text()) || article.title, + description: content.html()?.trim(), + pubDate: date ? parseDate(date, 'YYYY年MM月DD日') : article.pubDate, + author: author || undefined, + }; + }); + } catch { + return article; + } +}; + +export const route: Route = { + path: '/paper/:page?', + categories: ['traditional-media'], + example: '/people/paper', + parameters: { page: '版面编号,如 `01`;使用 `all` 或留空获取全部版面' }, + radar: [ + { + source: ['paper.people.com.cn/rmrb/pc/layout/index.html'], + target: '/paper', + }, + ], + name: '人民日报电子版', + maintainers: ['pseudoyu'], + handler, + url: 'paper.people.com.cn/rmrb/pc/layout/index.html', + description: '获取当日《人民日报》全部版面或指定版面的文章。', +}; + +async function handler(ctx) { + const requestedPage = ctx.req.param('page') ?? 'all'; + const limit = Number(ctx.req.query('limit') ?? defaultLimit); + const pages = await getPages(); + const normalizedPage = /^\d{1,2}$/.test(requestedPage) ? requestedPage.padStart(2, '0') : requestedPage; + const selectedPage = normalizedPage === 'all' ? undefined : pages.find((page) => page.id === normalizedPage); + + if (normalizedPage !== 'all' && !selectedPage) { + throw new InvalidParameterError(`Invalid page '${requestedPage}'`); + } + + const editionDate = getEditionDate(pages[0].url); + const pubDate = parseDate(editionDate, 'YYYY年MM月DD日'); + const targetPages = selectedPage ? [selectedPage] : pages; + const articleLists = await pMap(targetPages, (page) => getArticles(page, pubDate), { concurrency: 5 }); + const uniqueArticles = new Map(articleLists.flat().map((article) => [article.link, article])).values().toArray().slice(0, limit); + const items = await pMap(uniqueArticles, getArticleDetail, { concurrency: 5 }); + + return { + title: `人民日报电子版${selectedPage ? ` - ${selectedPage.title}` : ''} - ${editionDate}`, + link: selectedPage?.url ?? indexUrl, + item: items, + }; +} From 6e2cf519a4d241ebe58a310c271ca36982249e2a Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 11:09:05 +0800 Subject: [PATCH 0482/1190] fix(route/people): update message board feed --- lib/people-liuyan.spec.ts | 141 ++++++++++++++++++++++++++++++++++++ lib/routes/people/liuyan.ts | 123 +++++++++++++++++++------------ 2 files changed, 219 insertions(+), 45 deletions(-) create mode 100644 lib/people-liuyan.spec.ts diff --git a/lib/people-liuyan.spec.ts b/lib/people-liuyan.spec.ts new file mode 100644 index 000000000000..48c05934252a --- /dev/null +++ b/lib/people-liuyan.spec.ts @@ -0,0 +1,141 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { describe, expect, it, vi } from 'vitest'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import { route } from '@/routes/people/liuyan'; +import type { Data } from '@/types'; + +const rootUrl = 'https://liuyan.people.com.cn'; +const apiUrl = `${rootUrl}/threads/queryThreadsList`; +const forumUrl = `${rootUrl}/threads/list?fid=539`; + +const baseItem = { + tid: 1001, + subject: '留言标题', + content: '留言正文 <script>alert(1)</script>\n第二行', + nickName: '网友甲', + dateline: 1_785_000_000, + threadsCheckTime: 1_785_000_100, + forumName: '北京市委书记', + typeName: '建言', + domainName: '交通', + stateInfo: '办理中', + answerContent: null, + answerDateline: null, + answerOrganization: null, +}; + +function createCtx({ id, state, limit }: { id?: string; state?: string; limit?: string } = {}) { + return { + req: { + param: (name: string) => ({ id, state })[name], + query: (name: string) => (name === 'limit' ? limit : undefined), + }, + } as unknown as Context; +} + +async function registerApiMock(responseData: Array<Record<string, unknown>>, success = true, expectedState = '1') { + const { default: server } = await import('@/setup.test'); + const response = { result: success ? 'success' : 'error', responseData, success }; + const detailRequest = vi.fn(); + + server.use( + http.post(apiUrl, async ({ request }) => { + expect(request.headers.get('referer')).toBe(forumUrl); + const form = await request.formData(); + expect(form.get('fid')).toBe('539'); + expect(form.get('state')).toBe(expectedState); + expect(form.get('lastItem')).toBe('0'); + return new HttpResponse(JSON.stringify(response), { + headers: { 'Content-Type': 'text/html;charset=UTF-8' }, + }); + }), + http.post('http://liuyan.people.com.cn/threads/queryThreadsList', () => HttpResponse.json({}, { status: 500 })), + http.get(`${rootUrl}/threads/content`, ({ request }) => { + detailRequest(request.url); + return HttpResponse.html('<div id="app"></div>'); + }), + http.get('http://liuyan.people.com.cn/threads/content', ({ request }) => { + detailRequest(request.url); + return HttpResponse.html('<div id="app"></div>'); + }) + ); + + return detailRequest; +} + +describe('GET /people/liuyan/:id/:state?', () => { + it('builds escaped feed items directly from the list API and applies limit', async () => { + const detailRequest = await registerApiMock([ + baseItem, + { + ...baseItem, + tid: 1002, + subject: '第二条留言', + }, + ]); + + const feed = (await route.handler(createCtx({ id: '539', limit: '1' }))) as Data; + + expect(feed.title).toBe('北京市委书记 - 领导留言板 - 人民网'); + expect(feed.link).toBe(`${forumUrl}#state=1`); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '留言标题', + author: '网友甲', + link: `${rootUrl}/threads/content?tid=1001`, + category: ['北京市委书记', '建言', '交通', '办理中'], + }); + expect(feed.item?.[0].description).toContain('留言正文 <script>alert(1)</script><br>第二行'); + expect(feed.item?.[0].description).not.toContain('<script>'); + expect(new Date(feed.item?.[0].pubDate ?? '').getTime()).toBe(baseItem.dateline * 1000); + expect(detailRequest).not.toHaveBeenCalled(); + }); + + it('passes a supported state to the API and feed link', async () => { + await registerApiMock([baseItem], true, '3'); + + const feed = (await route.handler(createCtx({ id: '539', state: '3' }))) as Data; + + expect(feed.link).toBe(`${forumUrl}#state=3`); + }); + + it('includes an official answer when the API provides one', async () => { + await registerApiMock([ + { + ...baseItem, + answerContent: '回复内容\n下一行', + answerDateline: 1_785_000_200, + answerOrganization: '北京市交通委', + }, + ]); + + const feed = (await route.handler(createCtx({ id: '539' }))) as Data; + + expect(feed.item?.[0].description).toContain('<strong>北京市交通委</strong>'); + expect(feed.item?.[0].description).toContain('回复内容<br>下一行'); + expect(new Date(feed.item?.[0].updated ?? '').getTime()).toBe(1_785_000_200_000); + }); + + it('requires a forum id instead of falling through to the generic People route', async () => { + await expect(route.handler(createCtx())).rejects.toBeInstanceOf(InvalidParameterError); + await expect(route.handler(createCtx())).rejects.toThrow('Forum id is required'); + }); + + it('rejects a non-numeric forum id', async () => { + await expect(route.handler(createCtx({ id: 'invalid' }))).rejects.toBeInstanceOf(InvalidParameterError); + await expect(route.handler(createCtx({ id: 'invalid' }))).rejects.toThrow('Invalid forum id'); + }); + + it('rejects an unsupported state', async () => { + await expect(route.handler(createCtx({ id: '539', state: '9' }))).rejects.toBeInstanceOf(InvalidParameterError); + await expect(route.handler(createCtx({ id: '539', state: '9' }))).rejects.toThrow('Invalid state'); + }); + + it('reports an unsuccessful upstream response clearly', async () => { + await registerApiMock([], false); + + await expect(route.handler(createCtx({ id: '539' }))).rejects.toThrow('Failed to fetch messages from the People’s Daily message board'); + }); +}); diff --git a/lib/routes/people/liuyan.ts b/lib/routes/people/liuyan.ts index a15c39cd5688..e94b34cb7945 100644 --- a/lib/routes/people/liuyan.ts +++ b/lib/routes/people/liuyan.ts @@ -1,12 +1,50 @@ -import { load } from 'cheerio'; +import { escapeText } from 'entities'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +const rootUrl = 'https://liuyan.people.com.cn'; +const apiUrl = `${rootUrl}/threads/queryThreadsList`; +const allowedStates = new Set(['1', '2', '3', '4']); + +type Message = { + tid: number; + subject: string; + content: string; + nickName: string; + dateline: number; + threadsCheckTime: number; + forumName: string; + typeName?: string; + domainName?: string; + stateInfo?: string; + answerContent?: string | null; + answerDateline?: number | null; + answerOrganization?: string | null; +}; + +type ApiResponse = { + result: string; + responseData: Message[]; + success: boolean; +}; + +const formatText = (text: string) => escapeText(text).replaceAll(/\r?\n/g, '<br>'); + +const getDescription = (message: Message) => { + const content = `<p>${formatText(message.content)}</p>`; + if (!message.answerContent) { + return content; + } + + const organization = escapeText(message.answerOrganization ?? '官方回复'); + return `${content}<hr><p><strong>${organization}</strong></p><p>${formatText(message.answerContent)}</p>`; +}; + export const route: Route = { - path: '/liuyan/:id/:state?', + path: '/liuyan/:id?/:state?', categories: ['traditional-media'], example: '/people/liuyan/539', parameters: { id: '编号,可在对应人物页 URL 中找到', state: '状态,见下表,默认为全部' }, @@ -18,13 +56,8 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, - radar: [ - { - source: ['liuyan.people.com.cn/'], - }, - ], name: '领导留言板', - maintainers: ['nczitzk'], + maintainers: ['nczitzk', 'pseudoyu'], handler, url: 'liuyan.people.com.cn/', description: `| 全部 | 待回复 | 办理中 | 已办理 | @@ -34,50 +67,50 @@ export const route: Route = { async function handler(ctx) { const fid = ctx.req.param('id'); - const state = ctx.req.param('state') ?? '1'; - - const rootUrl = 'http://liuyan.people.com.cn'; - const currentUrl = `${rootUrl}/threads/list?fid=${fid}#state=${state}`; + if (!fid) { + throw new InvalidParameterError('Forum id is required'); + } + if (!/^\d+$/.test(fid)) { + throw new InvalidParameterError(`Invalid forum id '${fid}'`); + } - let currentForum; + const state = ctx.req.param('state') ?? '1'; + if (!allowedStates.has(state)) { + throw new InvalidParameterError(`Invalid state '${state}'`); + } - const apiResponse = await got({ - method: 'post', - url: `${rootUrl}/threads/queryThreadsList`, - form: { + const limit = Number(ctx.req.query('limit') ?? 30); + const forumUrl = `${rootUrl}/threads/list?fid=${fid}`; + const currentUrl = `${forumUrl}#state=${state}`; + const apiResponse = await ofetch<ApiResponse>(apiUrl, { + method: 'POST', + responseType: 'json', + headers: { + Referer: forumUrl, + }, + body: new URLSearchParams({ fid, state, - lastItem: 0, - }, + lastItem: '0', + }), }); - const list = apiResponse.data.responseData.map((item) => ({ - title: item.subject, - author: item.nickName, - link: `${rootUrl}/threads/content?tid=${item.tid}`, - pubDate: parseDate(item.threadsCheckTime * 1000), - })); - - const items = await Promise.all( - list.map((item) => - cache.tryGet(item.link, async () => { - const detailResponse = await got({ - method: 'get', - url: item.link, - }); - - const content = load(detailResponse.data); + if (apiResponse.result !== 'success' || !apiResponse.success || !Array.isArray(apiResponse.responseData)) { + throw new Error('Failed to fetch messages from the People’s Daily message board'); + } - item.description = content('.content').html(); - currentForum ??= content('#currentForum').text(); - - return item; - }) - ) - ); + const items = apiResponse.responseData.slice(0, limit).map((message) => ({ + title: message.subject, + author: message.nickName, + link: `${rootUrl}/threads/content?tid=${message.tid}`, + pubDate: parseDate(message.dateline * 1000), + description: getDescription(message), + category: [message.forumName, message.typeName, message.domainName, message.stateInfo].filter((category): category is string => Boolean(category)), + ...(message.answerDateline && { updated: parseDate(message.answerDateline * 1000) }), + })); return { - title: `${currentForum} - 领导留言板 - 人民网`, + title: `${apiResponse.responseData[0]?.forumName ?? `留言板 ${fid}`} - 领导留言板 - 人民网`, link: currentUrl, item: items, }; From 7f3fbda5c0170cf2ffa0b4fb66a2a29da74e5daa Mon Sep 17 00:00:00 2001 From: pseudoyu <pseudoyu@connect.hku.hk> Date: Mon, 3 Aug 2026 11:18:27 +0800 Subject: [PATCH 0483/1190] fix(route/people): refresh CPC rolling news --- lib/people-cpc.spec.ts | 75 ++++++++++++++++++++++++++++++++++++++ lib/routes/people/index.ts | 6 +-- 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 lib/people-cpc.spec.ts diff --git a/lib/people-cpc.spec.ts b/lib/people-cpc.spec.ts new file mode 100644 index 000000000000..e7c63696d62a --- /dev/null +++ b/lib/people-cpc.spec.ts @@ -0,0 +1,75 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { describe, expect, it, vi } from 'vitest'; + +import { route } from '@/routes/people'; +import type { Data } from '@/types'; + +const rootUrl = 'http://cpc.people.com.cn'; +const currentUrl = `${rootUrl}/GB/64093/64387`; +const firstArticleUrl = `${rootUrl}/n1/2026/0803/c64387-40772759.html`; +const secondArticleUrl = `${rootUrl}/n1/2026/0803/c64387-40772755.html`; + +function createCtx(limit?: string) { + return { + req: { + param: () => ({ site: 'cpc', category: '24h' }), + query: (name: string) => (name === 'limit' ? limit : undefined), + }, + } as unknown as Context; +} + +describe('GET /people/cpc/24h', () => { + it('uses the maintained CPC news list and modern article content selector', async () => { + const { default: server } = await import('@/setup.test'); + const secondDetailRequest = vi.fn(); + + server.use( + http.get(currentUrl, () => + HttpResponse.html(` + <html> + <head><meta charset="utf-8"><title>综合报道 + + + + + `) + ), + http.get(`${rootUrl}/GB/87228`, () => HttpResponse.text('Archived page must not be used', { status: 500 })), + http.get(firstArticleUrl, () => + HttpResponse.html(` + + + 2026年08月03日08:15 +

    正文一

    + + + `) + ), + http.get(secondArticleUrl, () => { + secondDetailRequest(); + return HttpResponse.html('

    正文二

    '); + }) + ); + + const feed = (await route.handler(createCtx('1'))) as Data; + + expect(feed.title).toBe('综合报道'); + expect(feed.link).toBe(currentUrl); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '第一条新闻', + link: firstArticleUrl, + description: '

    正文一

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T00:15:00.000Z'); + expect(secondDetailRequest).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts index 7ffb9cf213e3..ed958fc26579 100644 --- a/lib/routes/people/index.ts +++ b/lib/routes/people/index.ts @@ -20,7 +20,7 @@ export const route: Route = { async function handler(ctx) { const { site = 'www' } = ctx.req.param(); let { category = site === 'www' ? '59476' : '' } = ctx.req.param(); - category = site === 'cpc' && category === '24h' ? '87228' : category; + category = site === 'cpc' && category === '24h' ? '64093/64387' : category; const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; @@ -48,7 +48,7 @@ async function handler(ctx) { $(e).parent().remove(); }); - let items = $('.p6, div.p2j_list, div.headingNews, div.ej_list_box, .leftItem') + let items = $('.p6, div.p2j_list, div.headingNews, div.ej_list_box, .leftItem, div.p2j_con02 > div.fl') .find('a') .slice(0, limit) .toArray() @@ -76,7 +76,7 @@ async function handler(ctx) { content('.paper_num, #rwb_tjyd').remove(); - item.description = content('#rwb_zw').html(); + item.description = content('#rwb_zw, #rm_txt_zw').first().html(); item.pubDate = timezone(parseDate(data.match(/(\d{4}年\d{2}月\d{2}日\d{2}:\d{2})/)?.[1] || '', 'YYYY年MM月DD日 HH:mm'), 8); } catch (error) { item.description = String(error); From e56e1cef6cd237e793cb79c6e519a1c8660fe38b Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 3 Aug 2026 11:22:59 +0800 Subject: [PATCH 0484/1190] fix(route/people): update education headlines --- lib/people-edu.spec.ts | 70 ++++++++++++++++++++++++++++++++++++++ lib/routes/people/index.ts | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 lib/people-edu.spec.ts diff --git a/lib/people-edu.spec.ts b/lib/people-edu.spec.ts new file mode 100644 index 000000000000..6830675239d3 --- /dev/null +++ b/lib/people-edu.spec.ts @@ -0,0 +1,70 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { describe, expect, it, vi } from 'vitest'; + +import { route } from '@/routes/people'; +import type { Data } from '@/types'; + +const rootUrl = 'http://edu.people.com.cn'; +const currentUrl = `${rootUrl}/GB/`; +const firstArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772728.html`; +const secondArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772725.html`; + +function createCtx(limit?: string) { + return { + req: { + param: () => ({ site: 'edu', category: '' }), + query: (name: string) => (name === 'limit' ? limit : undefined), + }, + } as unknown as Context; +} + +describe('GET /people/edu', () => { + it('extracts the current education news list and applies limit before details', async () => { + const { default: server } = await import('@/setup.test'); + const secondDetailRequest = vi.fn(); + + server.use( + http.get(currentUrl, () => + HttpResponse.html(` + + 教育--人民网 + + + + + `) + ), + http.get(firstArticleUrl, () => + HttpResponse.html(` + + + 2026年08月03日08:15 +

    教育正文

    + + + `) + ), + http.get(secondArticleUrl, () => { + secondDetailRequest(); + return HttpResponse.html('

    第二篇正文

    '); + }) + ); + + const feed = (await route.handler(createCtx('1'))) as Data; + + expect(feed.title).toBe('教育--人民网'); + expect(feed.link).toBe(currentUrl); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '第一条教育新闻', + link: firstArticleUrl, + description: '

    教育正文

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T00:15:00.000Z'); + expect(secondDetailRequest).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts index ed958fc26579..b6630981b105 100644 --- a/lib/routes/people/index.ts +++ b/lib/routes/people/index.ts @@ -48,7 +48,7 @@ async function handler(ctx) { $(e).parent().remove(); }); - let items = $('.p6, div.p2j_list, div.headingNews, div.ej_list_box, .leftItem, div.p2j_con02 > div.fl') + let items = $('.p6, div.p2j_list, div.headingNews, div.ej_list_box, .leftItem, div.p2j_con02 > div.fl, div.jsnew_line') .find('a') .slice(0, limit) .toArray() From 49970c8276499ae47418157cd46eea52995dbf6f Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 3 Aug 2026 11:54:59 +0800 Subject: [PATCH 0485/1190] fix(route/people): restore legacy route compatibility --- lib/people-channels.spec.ts | 202 ++++++++++++++++++++++++++++++++++++ lib/people-edu.spec.ts | 3 +- lib/people-xjpjh.spec.ts | 138 ++++++++++++++++++++++++ lib/routes/people/index.ts | 88 +++++++++++----- lib/routes/people/xjpjh.ts | 79 +++++++------- 5 files changed, 442 insertions(+), 68 deletions(-) create mode 100644 lib/people-channels.spec.ts create mode 100644 lib/people-xjpjh.spec.ts diff --git a/lib/people-channels.spec.ts b/lib/people-channels.spec.ts new file mode 100644 index 000000000000..d331163e28b5 --- /dev/null +++ b/lib/people-channels.spec.ts @@ -0,0 +1,202 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { describe, expect, it } from 'vitest'; + +import { route } from '@/routes/people'; +import type { Data } from '@/types'; + +const rootUrl = 'http://politics.people.com.cn'; +const currentUrl = `${rootUrl}/GB/1024`; +const articleUrl = `${rootUrl}/n1/2026/0801/c1001-40771961.html`; + +function createCtx(site: string, category = '') { + return { + req: { + param: () => ({ site, category }), + query: (name: string) => (name === 'limit' ? '1' : undefined), + }, + } as unknown as Context; +} + +describe('GET /people/:site?/:category?', () => { + it('uses a maintained politics page when the channel homepage is forbidden', async () => { + const { default: server } = await import('@/setup.test'); + + server.use( + http.get(`${rootUrl}/GB/`, () => HttpResponse.text('Forbidden', { status: 403 })), + http.get(currentUrl, () => + HttpResponse.html(` + + 高层动态--时政--人民网 + + + + + `) + ), + http.get(articleUrl, () => + HttpResponse.html(` + + + 2026年08月01日10:30 +

    时政正文

    + + + `) + ) + ); + + const feed = (await route.handler(createCtx('politics'))) as Data; + + expect(feed.title).toBe('高层动态--时政--人民网'); + expect(feed.link).toBe(currentUrl); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '时政即时新闻', + link: articleUrl, + description: '

    时政正文

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-01T02:30:00.000Z'); + }); + + it('maps the retired society category to the current channel homepage', async () => { + const { default: server } = await import('@/setup.test'); + const societyRootUrl = 'http://society.people.com.cn/'; + const societyArticleUrl = `${societyRootUrl}n1/2026/0803/c1008-40772964.html`; + + server.use( + http.get(`${societyRootUrl}GB/1008`, () => HttpResponse.text('Retired category must not be used', { status: 500 })), + http.get(societyRootUrl, () => + HttpResponse.html(` + + 社会·法治--人民网 + + + + + `) + ), + http.get(societyArticleUrl, () => + HttpResponse.html(` + + + 2026年08月03日09:15 +

    社会正文

    + + + `) + ) + ); + + const feed = (await route.handler(createCtx('society', '1008'))) as Data; + + expect(feed.title).toBe('社会·法治--人民网'); + expect(feed.link).toBe(societyRootUrl); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '社会即时新闻', + link: societyArticleUrl, + description: '

    社会正文

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T01:15:00.000Z'); + }); + + it('follows an official channel migration and resolves article links against its destination', async () => { + const { default: server } = await import('@/setup.test'); + const legalRootUrl = 'http://legal.people.com.cn/'; + const societyRootUrl = 'http://society.people.com.cn/'; + const articleUrl = `${societyRootUrl}n1/2026/0803/c1008-40772812.html`; + + server.use( + http.get(`${legalRootUrl}GB/`, () => HttpResponse.text('Legacy path must not be used', { status: 500 })), + http.get(legalRootUrl, () => + HttpResponse.html(` + + + + + + `) + ), + http.get(societyRootUrl, () => + HttpResponse.html(` + + 社会·法治--人民网 + + + + + `) + ), + http.get(articleUrl, () => + HttpResponse.html(` + + + 2026年08月03日10:20 +

    法治正文

    + + + `) + ) + ); + + const feed = (await route.handler(createCtx('legal'))) as Data; + + expect(feed.title).toBe('社会·法治--人民网'); + expect(feed.link).toBe(societyRootUrl); + expect(feed.item).toHaveLength(1); + expect(feed.item?.[0]).toMatchObject({ + title: '法治即时新闻', + link: articleUrl, + description: '

    法治正文

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T02:20:00.000Z'); + }); + + it.each(['ftp://legal.people.com.cn/file', 'http://['])('ignores an unsafe or malformed channel migration to %s', async (redirectTarget) => { + const { default: server } = await import('@/setup.test'); + const legalRootUrl = 'http://legal.people.com.cn/'; + const legalArticleUrl = `${legalRootUrl}n1/2026/0803/c1008-40772809.html`; + + server.use( + http.get(legalRootUrl, () => + HttpResponse.html(` + + + + + 法治测试页--人民网 + + + + + + `) + ), + http.get(legalArticleUrl, () => + HttpResponse.html(` + 2026年08月03日10:30 +

    法治测试正文

    + `) + ) + ); + + const feed = (await route.handler(createCtx('legal'))) as Data; + + expect(feed.title).toBe('法治测试页--人民网'); + expect(feed.link).toBe(legalRootUrl); + expect(feed.item?.[0]).toMatchObject({ + title: '法治测试新闻', + link: legalArticleUrl, + description: '

    法治测试正文

    ', + }); + }); +}); diff --git a/lib/people-edu.spec.ts b/lib/people-edu.spec.ts index 6830675239d3..befa68ebb829 100644 --- a/lib/people-edu.spec.ts +++ b/lib/people-edu.spec.ts @@ -6,7 +6,7 @@ import { route } from '@/routes/people'; import type { Data } from '@/types'; const rootUrl = 'http://edu.people.com.cn'; -const currentUrl = `${rootUrl}/GB/`; +const currentUrl = `${rootUrl}/`; const firstArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772728.html`; const secondArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772725.html`; @@ -25,6 +25,7 @@ describe('GET /people/edu', () => { const secondDetailRequest = vi.fn(); server.use( + http.get(`${rootUrl}/GB/`, () => HttpResponse.text('Legacy path must not be used', { status: 500 })), http.get(currentUrl, () => HttpResponse.html(` diff --git a/lib/people-xjpjh.spec.ts b/lib/people-xjpjh.spec.ts new file mode 100644 index 000000000000..2ae668cf2e7b --- /dev/null +++ b/lib/people-xjpjh.spec.ts @@ -0,0 +1,138 @@ +import type { Context } from 'hono'; +import { http, HttpResponse } from 'msw'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import { route } from '@/routes/people/xjpjh'; +import type { Data } from '@/types'; +import cache from '@/utils/cache'; + +const rootUrl = 'http://jhsjk.people.cn'; +const defaultResultUrl = `${rootUrl}/result?keywords=&year=0`; +const firstArticleUrl = `${rootUrl}/article/40772030`; +const secondArticleUrl = `${rootUrl}/article/40772028`; +const thirdArticleUrl = `${rootUrl}/article/40772029`; + +function createCtx({ keyword, year, limit }: { keyword?: string; year?: string; limit?: string } = {}) { + return { + req: { + param: (name: string) => ({ keyword, year })[name], + query: (name: string) => (name === 'limit' ? limit : undefined), + }, + } as unknown as Context; +} + +function createResultHtml() { + return ` + + `; +} + +function createArticleHtml(content: string, date: string) { + return ` +
    来源:人民网 发布时间:${date}
    +

    ${content}

    + `; +} + +describe('GET /people/xjpjh/:keyword?/:year?', () => { + beforeEach(() => cache.clients.memoryCache?.clear()); + + it('selects only linked results and applies limit before fetching details', async () => { + const { default: server } = await import('@/setup.test'); + const thirdDetailRequest = vi.fn(); + + server.use( + http.get(`${rootUrl}/result`, ({ request }) => { + const url = new URL(request.url); + expect(url.searchParams.get('keywords')).toBe(''); + expect(url.searchParams.get('year')).toBe('0'); + return HttpResponse.html(createResultHtml()); + }), + http.get(`${rootUrl}/undefined`, () => HttpResponse.text('Summary rows must not be fetched', { status: 500 })), + http.get(firstArticleUrl, () => HttpResponse.html(createArticleHtml('第一篇正文', '2026-08-01'))), + http.get(secondArticleUrl, () => HttpResponse.html(createArticleHtml('第二篇正文', '2026-07-31'))), + http.get(thirdArticleUrl, () => { + thirdDetailRequest(); + return HttpResponse.html(createArticleHtml('第三篇正文', '2026-07-30')); + }) + ); + + const feed = (await route.handler(createCtx({ limit: '2' }))) as Data; + + expect(feed.title).toBe('习近平系列重要讲话-all-all'); + expect(feed.link).toBe(defaultResultUrl); + expect(feed.item).toHaveLength(2); + expect(feed.item?.[0]).toMatchObject({ + title: '第一篇讲话', + link: firstArticleUrl, + description: '

    第一篇正文

    ', + }); + expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-07-31T16:00:00.000Z'); + expect(feed.item?.[1]).toMatchObject({ + title: '第二篇讲话', + link: secondArticleUrl, + description: '

    第二篇正文

    ', + }); + expect(thirdDetailRequest).not.toHaveBeenCalled(); + }); + + it('passes keyword and calendar year directly to the current search page', async () => { + const { default: server } = await import('@/setup.test'); + const resultUrl = `${rootUrl}/result?keywords=%E7%BB%8F%E6%B5%8E&year=2026`; + + server.use( + http.get(`${rootUrl}/result`, ({ request }) => { + const url = new URL(request.url); + expect(url.searchParams.get('keywords')).toBe('经济'); + expect(url.searchParams.get('year')).toBe('2026'); + return HttpResponse.html(''); + }), + http.get(firstArticleUrl, () => HttpResponse.html(createArticleHtml('经济正文', '2026-08-01'))) + ); + + const feed = (await route.handler(createCtx({ keyword: '经济', year: '2026', limit: '1' }))) as Data; + + expect(feed.title).toBe('习近平系列重要讲话-经济-2026'); + expect(feed.link).toBe(resultUrl); + expect(feed.item?.[0]).toMatchObject({ + title: '经济讲话', + link: firstArticleUrl, + description: '

    经济正文

    ', + }); + }); + + it.each([ + ['a negative', '-1'], + ['a non-numeric', 'invalid'], + ['an excessive', '999'], + ])('keeps detail requests within the previous maximum for %s limit', async (_, limit) => { + const { default: server } = await import('@/setup.test'); + const detailRequest = vi.fn(); + const links = Array.from({ length: 11 }, (__, index) => `
  • 讲话 ${index + 1}
  • `).join(''); + + server.use( + http.get(`${rootUrl}/result`, () => HttpResponse.html(`
      ${links}
    `)), + http.get(new RegExp(`${rootUrl}/article/\\d+`), () => { + detailRequest(); + return HttpResponse.html(createArticleHtml('讲话正文', '2026-08-01')); + }) + ); + + const feed = (await route.handler(createCtx({ limit }))) as Data; + + expect(feed.item).toHaveLength(10); + expect(detailRequest).toHaveBeenCalledTimes(10); + }); + + it('rejects an invalid year', async () => { + await expect(route.handler(createCtx({ keyword: 'all', year: 'invalid' }))).rejects.toBeInstanceOf(InvalidParameterError); + await expect(route.handler(createCtx({ keyword: 'all', year: 'invalid' }))).rejects.toThrow('Invalid year'); + }); +}); diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts index b6630981b105..a732e652ed76 100644 --- a/lib/routes/people/index.ts +++ b/lib/routes/people/index.ts @@ -19,8 +19,8 @@ export const route: Route = { async function handler(ctx) { const { site = 'www' } = ctx.req.param(); - let { category = site === 'www' ? '59476' : '' } = ctx.req.param(); - category = site === 'cpc' && category === '24h' ? '64093/64387' : category; + const { category: requestedCategory = site === 'www' ? '59476' : '' } = ctx.req.param(); + const category = site === 'cpc' && requestedCategory === '24h' ? '64093/64387' : requestedCategory; const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; @@ -28,18 +28,10 @@ async function handler(ctx) { throw new InvalidParameterError('Invalid site'); } const rootUrl = `http://${site}.people.com.cn`; - const currentUrl = new URL(`GB/${category}`, rootUrl).href; - - const response = await ofetch(currentUrl, { - responseType: 'arrayBuffer', - }); - - // try to parse charset from meta tag - let decodedResponse = iconv.decode(Buffer.from(response), 'utf-8'); - const parsedCharset = decodedResponse.match(/]+)["']?/i); - const encoding = parsedCharset ? parsedCharset[1].toLowerCase() : 'utf-8'; - decodedResponse = encoding === 'utf-8' ? decodedResponse : iconv.decode(Buffer.from(response), encoding); - const $ = load(decodedResponse); + const path = site === 'politics' && !category ? 'GB/1024' : site === 'society' && category === '1008' ? '' : category ? `GB/${category}` : ''; + const requestedUrl = new URL(path, rootUrl).href; + const { $, url: currentUrl } = await fetchChannel(requestedUrl); + const articleRootUrl = new URL('/', currentUrl).href; $('em').remove(); $('.bshare-more, .page_n, .page').remove(); @@ -59,7 +51,7 @@ async function handler(ctx) { return { title: item.text(), - link: link.indexOf('http') === 0 ? link : new URL(link.replace(/^\.\./, ''), rootUrl).href, + link: link.indexOf('http') === 0 ? link : new URL(link.replace(/^\.\./, ''), articleRootUrl).href, }; }); @@ -67,17 +59,12 @@ async function handler(ctx) { items.map((item) => cache.tryGet(item.link, async () => { try { - const detailResponse = await ofetch(item.link, { - responseType: 'arrayBuffer', - }); - - const data = iconv.decode(Buffer.from(detailResponse), encoding); - const content = load(data); + const detailPage = await fetchPage(item.link); - content('.paper_num, #rwb_tjyd').remove(); + detailPage.$('.paper_num, #rwb_tjyd').remove(); - item.description = content('#rwb_zw, #rm_txt_zw').first().html(); - item.pubDate = timezone(parseDate(data.match(/(\d{4}年\d{2}月\d{2}日\d{2}:\d{2})/)?.[1] || '', 'YYYY年MM月DD日 HH:mm'), 8); + item.description = detailPage.$('#rwb_zw, #rm_txt_zw, .rm_txt_con, .show_text').first().html(); + item.pubDate = timezone(parseDate(detailPage.html.match(/(\d{4}年\d{2}月\d{2}日\d{2}:\d{2})/)?.[1] || '', 'YYYY年MM月DD日 HH:mm'), 8); } catch (error) { item.description = String(error); } @@ -93,3 +80,56 @@ async function handler(ctx) { item: items, }; } + +async function fetchChannel(url: string) { + const page = await fetchPage(url); + const refreshElement = page + .$('meta[http-equiv]') + .toArray() + .find((element) => page.$(element).attr('http-equiv')?.toLowerCase() === 'refresh'); + const refreshContent = refreshElement ? page.$(refreshElement).attr('content') : undefined; + const redirectSeparator = refreshContent?.match(/;\s*url\s*=/i); + const redirectTarget = + redirectSeparator?.index === undefined + ? undefined + : refreshContent + ?.slice(redirectSeparator.index + redirectSeparator[0].length) + .trim() + .replaceAll(/^['"]|['"]$/g, ''); + + if (!redirectTarget) { + return { ...page, url }; + } + + const redirectUrl = resolvePeopleRedirect(redirectTarget, url); + if (!redirectUrl) { + return { ...page, url }; + } + + return { ...(await fetchPage(redirectUrl)), url: redirectUrl }; +} + +async function fetchPage(url: string) { + const response = await ofetch(url, { + responseType: 'arrayBuffer', + }); + + // try to parse charset from meta tag + let html = iconv.decode(Buffer.from(response), 'utf-8'); + const parsedCharset = html.match(/]+)["']?/i); + const encoding = parsedCharset ? parsedCharset[1].toLowerCase() : 'utf-8'; + html = encoding === 'utf-8' ? html : iconv.decode(Buffer.from(response), encoding); + + return { $: load(html), html }; +} + +function resolvePeopleRedirect(target: string, sourceUrl: string) { + try { + const url = new URL(target, sourceUrl); + const isHttp = url.protocol === 'http:' || url.protocol === 'https:'; + const isPeopleHost = url.hostname === 'people.com.cn' || url.hostname.endsWith('.people.com.cn'); + return isHttp && isPeopleHost ? url.href : undefined; + } catch { + return; + } +} diff --git a/lib/routes/people/xjpjh.ts b/lib/routes/people/xjpjh.ts index 1f5b8ec3cbff..896e72ad5d77 100644 --- a/lib/routes/people/xjpjh.ts +++ b/lib/routes/people/xjpjh.ts @@ -1,8 +1,10 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; const host = 'http://jhsjk.people.cn'; @@ -21,70 +23,61 @@ export const route: Route = { }, radar: [ { - source: ['people.com.cn/'], - target: '/:site?/:category?', + source: ['jhsjk.people.cn/'], + target: '/xjpjh', }, ], name: '习近平系列重要讲话', maintainers: ['LogicJake'], handler, - url: 'people.com.cn/', + url: 'jhsjk.people.cn', }; async function handler(ctx) { - let keyword = ctx.req.param('keyword') || 'all'; - let year = ctx.req.param('year') || 0; - - let title = '习近平系列重要讲话'; - title = title + '-' + keyword; - if (keyword === 'all') { - keyword = ''; - } - if (year === 0) { - title += '-all'; - } else { - title = title + '-' + year; - year -= 1811; + const requestedKeyword = ctx.req.param('keyword'); + const requestedYear = ctx.req.param('year'); + if (requestedYear && requestedYear !== 'all' && !/^\d{4}$/.test(requestedYear)) { + throw new InvalidParameterError(`Invalid year '${requestedYear}'`); } - const link = `http://jhsjk.people.cn/result?keywords=${keyword}&year=${year}`; + const keyword = requestedKeyword && requestedKeyword !== 'all' ? requestedKeyword : ''; + const year = requestedYear && requestedYear !== 'all' ? requestedYear : '0'; + const requestedLimit = Number(ctx.req.query('limit') ?? 10); + const limit = Number.isSafeInteger(requestedLimit) && requestedLimit > 0 ? Math.min(requestedLimit, 10) : 10; + const title = `习近平系列重要讲话-${keyword || 'all'}-${year === '0' ? 'all' : year}`; + const resultUrl = new URL('/result', host); + resultUrl.searchParams.set('keywords', keyword); + resultUrl.searchParams.set('year', year); + const link = resultUrl.href; const response = await got.get(link); const $ = load(response.data); - const list = $('ul.list_14.p1_2.clearfix li') - .slice(0, 10) + const list = $('#news_list > li > a[href]') + .slice(0, limit) .toArray() .map((element) => { - const info = { - title: $(element).find('a').text(), - link: $(element).find('a').attr('href'), + const item = $(element); + return { + title: item.text(), + link: new URL(item.attr('href')!, host).href, }; - return info; }); const out = await Promise.all( - list.map(async (info) => { - const title = info.title; - const itemUrl = new URL(info.link, host).href; - - const cacheIn = await cache.get(itemUrl); - if (cacheIn) { - return JSON.parse(cacheIn); - } + list.map((item) => + cache.tryGet(item.link, async () => { + const response = await got.get(item.link); + const $ = load(response.data); + const publishedDate = response.data.match(/发布时间:(\d{4}-\d{2}-\d{2})/)?.[1]; - const response = await got.get(itemUrl); - const $ = load(response.data); - const description = $('div.d2txt_con.clearfix').html().trim(); - - const single = { - title, - link: itemUrl, - description, - }; - cache.set(itemUrl, JSON.stringify(single)); - return single; - }) + return { + ...item, + description: $('div.d2txt_con.clearfix').html(), + ...(publishedDate && { pubDate: parseDate(publishedDate) }), + }; + }) + ) ); return { From eabdca8f0c5f8841dfd8b41401fa22b955cf8454 Mon Sep 17 00:00:00 2001 From: TonyRL Date: Mon, 3 Aug 2026 16:51:12 +0800 Subject: [PATCH 0486/1190] chore: fix ci --- lib/gov-zhengce-govall.spec.ts | 251 ----------------------- lib/people-channels.spec.ts | 202 ------------------ lib/people-cpc.spec.ts | 75 ------- lib/people-edu.spec.ts | 71 ------- lib/people-liuyan.spec.ts | 141 ------------- lib/people-paper.spec.ts | 171 --------------- lib/people-xjpjh.spec.ts | 138 ------------- scripts/workflow/check-orphan-files.ts | 4 +- scripts/workflow/test-route/identify.mjs | 15 +- 9 files changed, 14 insertions(+), 1054 deletions(-) delete mode 100644 lib/gov-zhengce-govall.spec.ts delete mode 100644 lib/people-channels.spec.ts delete mode 100644 lib/people-cpc.spec.ts delete mode 100644 lib/people-edu.spec.ts delete mode 100644 lib/people-liuyan.spec.ts delete mode 100644 lib/people-paper.spec.ts delete mode 100644 lib/people-xjpjh.spec.ts diff --git a/lib/gov-zhengce-govall.spec.ts b/lib/gov-zhengce-govall.spec.ts deleted file mode 100644 index db5d135f7f41..000000000000 --- a/lib/gov-zhengce-govall.spec.ts +++ /dev/null @@ -1,251 +0,0 @@ -import type { Context } from 'hono'; -import { http, HttpResponse } from 'msw'; -import { describe, expect, it } from 'vitest'; - -import type { Data } from '@/types'; - -import { route } from './routes/gov/zhengce/govall'; - -const apiUrl = 'https://sousuoht.www.gov.cn/athena/forward/2B22E8E39E850E17F95A016A74FCB6B673336FA8B6FEC0E2955907EF9AEE06BE'; -const articleUrl = 'http://www.gov.cn/gongbao/content/2009/content_1322126.htm'; - -describe('gov.cn information search route', () => { - it('supports the documented legacy advanced-search parameters', async () => { - const { default: server } = await import('@/setup.test'); - let requestBody: any; - - server.use( - http.get('http://sousuo.gov.cn/list.htm', () => HttpResponse.text('')), - http.post(apiUrl, async ({ request }) => { - requestBody = await request.json(); - - expect(request.headers.get('athenaAppKey')).toBeTruthy(); - expect(request.headers.get('athenaAppName')).toBe(encodeURIComponent('国网搜索')); - - return HttpResponse.json({ - resultCode: { - code: 200, - }, - result: { - data: { - middle: { - list: [ - { - title: '中华人民共和国国务院令(第555号)
      流动人口计划生育工作条例', - title_no_tag: '中华人民共和国国务院令(第555号)
      流动人口计划生育工作条例', - url: articleUrl, - summary: '搜索接口摘要', - time: '2009-05-30 23:59:59', - }, - ], - }, - }, - }, - }); - }), - http.get(articleUrl, () => HttpResponse.text('

    文章全文

    ')) - ); - - const ctx = { - req: { - param: (name: string) => (name === 'advance' ? 'orpro=555¬pro=2&search_field=title' : undefined), - }, - } as unknown as Context; - const result = (await route.handler(ctx)) as Data; - - expect(requestBody).toMatchObject({ - code: '17da70961a7', - dataTypeId: '107', - orderBy: 'time', - searchBy: 'title', - pageNo: 1, - pageSize: 20, - isDefaultAdvanced: 1, - isAdvancedSearch: 1, - advancedFilters: [ - { - fieldName: 'containsAll', - searchWord: [], - }, - { - fieldName: 'containsOne', - searchWord: ['555'], - }, - { - fieldName: 'none', - searchWord: ['2'], - }, - ], - }); - expect(result.link).toMatch(/^https:\/\/sousuo\.www\.gov\.cn\/sousuo\/search\.shtml\?/); - expect(result.item).toEqual([ - { - title: '中华人民共和国国务院令(第555号) 流动人口计划生育工作条例', - link: articleUrl, - description: '

    文章全文

    ', - pubDate: new Date('2009-05-30T15:59:59.000Z'), - }, - ]); - }); - - it('maps legacy keyword and date filters to the Athena request', async () => { - const { default: server } = await import('@/setup.test'); - let requestBody: any; - - server.use( - http.post(apiUrl, async ({ request }) => { - requestBody = await request.json(); - - return HttpResponse.json({ - resultCode: { - code: 200, - }, - result: { - data: { - middle: { - list: [], - }, - }, - }, - }); - }) - ); - - const ctx = { - req: { - param: (name: string) => - name === 'advance' - ? 'allpro=%E5%8C%BB%E7%96%97+%E4%BF%9D%E9%9A%9C&inpro=%E5%AE%8C%E6%95%B4+%E7%9F%AD%E8%AF%AD&orpro=%E5%8C%BB%E4%BF%9D+%E7%A4%BE%E4%BF%9D¬pro=%E6%95%99%E8%82%B2&searchfield=content&pubmintimeYear=2009&pubmintimeMonth=5&pubmaxtimeYear=2009&pubmaxtimeMonth=5' - : undefined, - }, - } as unknown as Context; - await route.handler(ctx); - - expect(requestBody).toMatchObject({ - searchBy: 'all', - granularity: 'CUSTOM', - beginDateTime: Date.UTC(2009, 4, 1) - 8 * 60 * 60 * 1000, - endDateTime: Date.UTC(2009, 5, 1) - 8 * 60 * 60 * 1000 - 1, - advancedFilters: [ - { - fieldName: 'containsAll', - searchWord: ['医疗', '保障', '完整 短语'], - }, - { - fieldName: 'containsOne', - searchWord: ['医保', '社保'], - }, - { - fieldName: 'none', - searchWord: ['教育'], - }, - ], - }); - }); - - it('requests the latest items when advanced-search parameters are omitted', async () => { - const { default: server } = await import('@/setup.test'); - let requestBody: any; - - server.use( - http.post(apiUrl, async ({ request }) => { - requestBody = await request.json(); - - return HttpResponse.json({ - resultCode: { - code: 200, - }, - result: { - data: { - middle: { - list: [], - }, - }, - }, - }); - }) - ); - - const ctx = { - req: { - param: () => {}, - }, - } as unknown as Context; - const result = (await route.handler(ctx)) as Data; - - expect(requestBody).toMatchObject({ - allData: true, - pageNo: 1, - pageSize: 20, - searchBy: 'all', - }); - expect(requestBody).not.toHaveProperty('advancedFilters'); - expect(result.link).toContain('allData=true'); - }); - - it('uses API content when an article page cannot be fetched', async () => { - const { default: server } = await import('@/setup.test'); - const unavailableArticleUrl = 'https://www.gov.cn/test/unavailable-article.htm'; - - server.use( - http.post(apiUrl, () => - HttpResponse.json({ - resultCode: { - code: 200, - }, - result: { - data: { - middle: { - list: [ - { - title: '测试
    标题', - url: unavailableArticleUrl, - content: '接口正文', - }, - ], - }, - }, - }, - }) - ), - http.get(unavailableArticleUrl, () => new HttpResponse(null, { status: 500 })) - ); - - const ctx = { - req: { - param: () => {}, - }, - } as unknown as Context; - const result = (await route.handler(ctx)) as Data; - - expect(result.item).toEqual([ - { - title: '测试 标题', - link: unavailableArticleUrl, - description: '接口正文', - }, - ]); - }); - - it('reports an actionable error when the search API fails', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.post(apiUrl, () => - HttpResponse.json({ - resultCode: { - code: 1000, - }, - }) - ) - ); - - const ctx = { - req: { - param: () => {}, - }, - } as unknown as Context; - - await expect(route.handler(ctx)).rejects.toThrow('中国政府网搜索接口请求失败,错误代码:1000'); - }); -}); diff --git a/lib/people-channels.spec.ts b/lib/people-channels.spec.ts deleted file mode 100644 index d331163e28b5..000000000000 --- a/lib/people-channels.spec.ts +++ /dev/null @@ -1,202 +0,0 @@ -import type { Context } from 'hono'; -import { http, HttpResponse } from 'msw'; -import { describe, expect, it } from 'vitest'; - -import { route } from '@/routes/people'; -import type { Data } from '@/types'; - -const rootUrl = 'http://politics.people.com.cn'; -const currentUrl = `${rootUrl}/GB/1024`; -const articleUrl = `${rootUrl}/n1/2026/0801/c1001-40771961.html`; - -function createCtx(site: string, category = '') { - return { - req: { - param: () => ({ site, category }), - query: (name: string) => (name === 'limit' ? '1' : undefined), - }, - } as unknown as Context; -} - -describe('GET /people/:site?/:category?', () => { - it('uses a maintained politics page when the channel homepage is forbidden', async () => { - const { default: server } = await import('@/setup.test'); - - server.use( - http.get(`${rootUrl}/GB/`, () => HttpResponse.text('Forbidden', { status: 403 })), - http.get(currentUrl, () => - HttpResponse.html(` - - 高层动态--时政--人民网 - - - - - `) - ), - http.get(articleUrl, () => - HttpResponse.html(` - - - 2026年08月01日10:30 -

    时政正文

    - - - `) - ) - ); - - const feed = (await route.handler(createCtx('politics'))) as Data; - - expect(feed.title).toBe('高层动态--时政--人民网'); - expect(feed.link).toBe(currentUrl); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '时政即时新闻', - link: articleUrl, - description: '

    时政正文

    ', - }); - expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-01T02:30:00.000Z'); - }); - - it('maps the retired society category to the current channel homepage', async () => { - const { default: server } = await import('@/setup.test'); - const societyRootUrl = 'http://society.people.com.cn/'; - const societyArticleUrl = `${societyRootUrl}n1/2026/0803/c1008-40772964.html`; - - server.use( - http.get(`${societyRootUrl}GB/1008`, () => HttpResponse.text('Retired category must not be used', { status: 500 })), - http.get(societyRootUrl, () => - HttpResponse.html(` - - 社会·法治--人民网 - - - - - `) - ), - http.get(societyArticleUrl, () => - HttpResponse.html(` - - - 2026年08月03日09:15 -

    社会正文

    - - - `) - ) - ); - - const feed = (await route.handler(createCtx('society', '1008'))) as Data; - - expect(feed.title).toBe('社会·法治--人民网'); - expect(feed.link).toBe(societyRootUrl); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '社会即时新闻', - link: societyArticleUrl, - description: '

    社会正文

    ', - }); - expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T01:15:00.000Z'); - }); - - it('follows an official channel migration and resolves article links against its destination', async () => { - const { default: server } = await import('@/setup.test'); - const legalRootUrl = 'http://legal.people.com.cn/'; - const societyRootUrl = 'http://society.people.com.cn/'; - const articleUrl = `${societyRootUrl}n1/2026/0803/c1008-40772812.html`; - - server.use( - http.get(`${legalRootUrl}GB/`, () => HttpResponse.text('Legacy path must not be used', { status: 500 })), - http.get(legalRootUrl, () => - HttpResponse.html(` - - - - - - `) - ), - http.get(societyRootUrl, () => - HttpResponse.html(` - - 社会·法治--人民网 - - - - - `) - ), - http.get(articleUrl, () => - HttpResponse.html(` - - - 2026年08月03日10:20 -

    法治正文

    - - - `) - ) - ); - - const feed = (await route.handler(createCtx('legal'))) as Data; - - expect(feed.title).toBe('社会·法治--人民网'); - expect(feed.link).toBe(societyRootUrl); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '法治即时新闻', - link: articleUrl, - description: '

    法治正文

    ', - }); - expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T02:20:00.000Z'); - }); - - it.each(['ftp://legal.people.com.cn/file', 'http://['])('ignores an unsafe or malformed channel migration to %s', async (redirectTarget) => { - const { default: server } = await import('@/setup.test'); - const legalRootUrl = 'http://legal.people.com.cn/'; - const legalArticleUrl = `${legalRootUrl}n1/2026/0803/c1008-40772809.html`; - - server.use( - http.get(legalRootUrl, () => - HttpResponse.html(` - - - - - 法治测试页--人民网 - - - - - - `) - ), - http.get(legalArticleUrl, () => - HttpResponse.html(` - 2026年08月03日10:30 -

    法治测试正文

    - `) - ) - ); - - const feed = (await route.handler(createCtx('legal'))) as Data; - - expect(feed.title).toBe('法治测试页--人民网'); - expect(feed.link).toBe(legalRootUrl); - expect(feed.item?.[0]).toMatchObject({ - title: '法治测试新闻', - link: legalArticleUrl, - description: '

    法治测试正文

    ', - }); - }); -}); diff --git a/lib/people-cpc.spec.ts b/lib/people-cpc.spec.ts deleted file mode 100644 index e7c63696d62a..000000000000 --- a/lib/people-cpc.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { Context } from 'hono'; -import { http, HttpResponse } from 'msw'; -import { describe, expect, it, vi } from 'vitest'; - -import { route } from '@/routes/people'; -import type { Data } from '@/types'; - -const rootUrl = 'http://cpc.people.com.cn'; -const currentUrl = `${rootUrl}/GB/64093/64387`; -const firstArticleUrl = `${rootUrl}/n1/2026/0803/c64387-40772759.html`; -const secondArticleUrl = `${rootUrl}/n1/2026/0803/c64387-40772755.html`; - -function createCtx(limit?: string) { - return { - req: { - param: () => ({ site: 'cpc', category: '24h' }), - query: (name: string) => (name === 'limit' ? limit : undefined), - }, - } as unknown as Context; -} - -describe('GET /people/cpc/24h', () => { - it('uses the maintained CPC news list and modern article content selector', async () => { - const { default: server } = await import('@/setup.test'); - const secondDetailRequest = vi.fn(); - - server.use( - http.get(currentUrl, () => - HttpResponse.html(` - - 综合报道 - - - - - `) - ), - http.get(`${rootUrl}/GB/87228`, () => HttpResponse.text('Archived page must not be used', { status: 500 })), - http.get(firstArticleUrl, () => - HttpResponse.html(` - - - 2026年08月03日08:15 -

    正文一

    - - - `) - ), - http.get(secondArticleUrl, () => { - secondDetailRequest(); - return HttpResponse.html('

    正文二

    '); - }) - ); - - const feed = (await route.handler(createCtx('1'))) as Data; - - expect(feed.title).toBe('综合报道'); - expect(feed.link).toBe(currentUrl); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '第一条新闻', - link: firstArticleUrl, - description: '

    正文一

    ', - }); - expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T00:15:00.000Z'); - expect(secondDetailRequest).not.toHaveBeenCalled(); - }); -}); diff --git a/lib/people-edu.spec.ts b/lib/people-edu.spec.ts deleted file mode 100644 index befa68ebb829..000000000000 --- a/lib/people-edu.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { Context } from 'hono'; -import { http, HttpResponse } from 'msw'; -import { describe, expect, it, vi } from 'vitest'; - -import { route } from '@/routes/people'; -import type { Data } from '@/types'; - -const rootUrl = 'http://edu.people.com.cn'; -const currentUrl = `${rootUrl}/`; -const firstArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772728.html`; -const secondArticleUrl = `${rootUrl}/n1/2026/0803/c1006-40772725.html`; - -function createCtx(limit?: string) { - return { - req: { - param: () => ({ site: 'edu', category: '' }), - query: (name: string) => (name === 'limit' ? limit : undefined), - }, - } as unknown as Context; -} - -describe('GET /people/edu', () => { - it('extracts the current education news list and applies limit before details', async () => { - const { default: server } = await import('@/setup.test'); - const secondDetailRequest = vi.fn(); - - server.use( - http.get(`${rootUrl}/GB/`, () => HttpResponse.text('Legacy path must not be used', { status: 500 })), - http.get(currentUrl, () => - HttpResponse.html(` - - 教育--人民网 - - - - - `) - ), - http.get(firstArticleUrl, () => - HttpResponse.html(` - - - 2026年08月03日08:15 -

    教育正文

    - - - `) - ), - http.get(secondArticleUrl, () => { - secondDetailRequest(); - return HttpResponse.html('

    第二篇正文

    '); - }) - ); - - const feed = (await route.handler(createCtx('1'))) as Data; - - expect(feed.title).toBe('教育--人民网'); - expect(feed.link).toBe(currentUrl); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '第一条教育新闻', - link: firstArticleUrl, - description: '

    教育正文

    ', - }); - expect(new Date(feed.item?.[0].pubDate ?? '').toISOString()).toBe('2026-08-03T00:15:00.000Z'); - expect(secondDetailRequest).not.toHaveBeenCalled(); - }); -}); diff --git a/lib/people-liuyan.spec.ts b/lib/people-liuyan.spec.ts deleted file mode 100644 index 48c05934252a..000000000000 --- a/lib/people-liuyan.spec.ts +++ /dev/null @@ -1,141 +0,0 @@ -import type { Context } from 'hono'; -import { http, HttpResponse } from 'msw'; -import { describe, expect, it, vi } from 'vitest'; - -import InvalidParameterError from '@/errors/types/invalid-parameter'; -import { route } from '@/routes/people/liuyan'; -import type { Data } from '@/types'; - -const rootUrl = 'https://liuyan.people.com.cn'; -const apiUrl = `${rootUrl}/threads/queryThreadsList`; -const forumUrl = `${rootUrl}/threads/list?fid=539`; - -const baseItem = { - tid: 1001, - subject: '留言标题', - content: '留言正文 \n第二行', - nickName: '网友甲', - dateline: 1_785_000_000, - threadsCheckTime: 1_785_000_100, - forumName: '北京市委书记', - typeName: '建言', - domainName: '交通', - stateInfo: '办理中', - answerContent: null, - answerDateline: null, - answerOrganization: null, -}; - -function createCtx({ id, state, limit }: { id?: string; state?: string; limit?: string } = {}) { - return { - req: { - param: (name: string) => ({ id, state })[name], - query: (name: string) => (name === 'limit' ? limit : undefined), - }, - } as unknown as Context; -} - -async function registerApiMock(responseData: Array>, success = true, expectedState = '1') { - const { default: server } = await import('@/setup.test'); - const response = { result: success ? 'success' : 'error', responseData, success }; - const detailRequest = vi.fn(); - - server.use( - http.post(apiUrl, async ({ request }) => { - expect(request.headers.get('referer')).toBe(forumUrl); - const form = await request.formData(); - expect(form.get('fid')).toBe('539'); - expect(form.get('state')).toBe(expectedState); - expect(form.get('lastItem')).toBe('0'); - return new HttpResponse(JSON.stringify(response), { - headers: { 'Content-Type': 'text/html;charset=UTF-8' }, - }); - }), - http.post('http://liuyan.people.com.cn/threads/queryThreadsList', () => HttpResponse.json({}, { status: 500 })), - http.get(`${rootUrl}/threads/content`, ({ request }) => { - detailRequest(request.url); - return HttpResponse.html('
    '); - }), - http.get('http://liuyan.people.com.cn/threads/content', ({ request }) => { - detailRequest(request.url); - return HttpResponse.html('
    '); - }) - ); - - return detailRequest; -} - -describe('GET /people/liuyan/:id/:state?', () => { - it('builds escaped feed items directly from the list API and applies limit', async () => { - const detailRequest = await registerApiMock([ - baseItem, - { - ...baseItem, - tid: 1002, - subject: '第二条留言', - }, - ]); - - const feed = (await route.handler(createCtx({ id: '539', limit: '1' }))) as Data; - - expect(feed.title).toBe('北京市委书记 - 领导留言板 - 人民网'); - expect(feed.link).toBe(`${forumUrl}#state=1`); - expect(feed.item).toHaveLength(1); - expect(feed.item?.[0]).toMatchObject({ - title: '留言标题', - author: '网友甲', - link: `${rootUrl}/threads/content?tid=1001`, - category: ['北京市委书记', '建言', '交通', '办理中'], - }); - expect(feed.item?.[0].description).toContain('留言正文 <script>alert(1)</script>
    第二行'); - expect(feed.item?.[0].description).not.toContain('` }); + + expect(await biliCache.getRenderData('42')).toBe('access-中文&value'); + expect(cache.tryGet).toHaveBeenCalledWith('bili-web-render-data', expect.any(Function)); + expect(got).toHaveBeenCalledExactlyOnceWith('https://space.bilibili.com/42', { + headers: { Referer: 'https://www.bilibili.com/', Cookie: 'fake-test-cookie' }, + }); + }); + + it('preserves HTML entities and markup inside script text', async () => { + vi.mocked(got).mockResolvedValue({ data: '' }); + + expect(await biliCache.getRenderData('42')).toBe('a&b & value'); + }); + + it('uses the first matching element when the page contains duplicate IDs', async () => { + vi.mocked(got).mockResolvedValue({ + data: '', + }); + + expect(await biliCache.getRenderData('42')).toBe('first'); + }); + + it.each(['No render data', ''])('returns undefined when render data is absent or empty: %s', async (data) => { + vi.mocked(got).mockResolvedValue({ data }); + + expect(await biliCache.getRenderData('42')).toBeUndefined(); + }); + + it('continues to reject invalid JSON instead of silently returning an empty result', async () => { + vi.mocked(got).mockResolvedValue({ data: '' }); + + await expect(biliCache.getRenderData('42')).rejects.toThrow(SyntaxError); + }); +}); diff --git a/tests/cache-http.worker.test.ts b/tests/cache-http.worker.test.ts new file mode 100644 index 000000000000..851c530eac18 --- /dev/null +++ b/tests/cache-http.worker.test.ts @@ -0,0 +1,163 @@ +import { env } from 'cloudflare:workers'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import type * as WorkerCache from '../lib/utils/cache/index.worker'; + +vi.mock('../lib/config', () => ({ + config: { + cache: { + type: 'http', + routeExpire: 300, + contentExpire: 600, + }, + httpCache: { + url: 'https://cache.example.com/', + token: 'worker-cache-token', + }, + requestTimeout: 1000, + loggerLevel: 'error', + }, +})); + +type StoredValue = { ttl: number; value: string }; + +let cache: typeof WorkerCache.default; +let setKVNamespace: typeof WorkerCache.setKVNamespace; +const values = new Map(); + +const handleCacheRequest = (input: RequestInfo | URL, init?: RequestInit) => { + const url = new URL(input.toString()); + const key = url.pathname; + + if (init?.method === 'PUT') { + values.set(key, JSON.parse(init.body!.toString())); + return Promise.resolve(new Response(null, { status: 204 })); + } + if (init?.method === 'HEAD') { + return Promise.resolve(new Response(null, { status: values.has(key) ? 204 : 404 })); + } + + const stored = values.get(key); + return Promise.resolve(stored ? Response.json({ hit: true, value: stored.value }) : Response.json({ hit: false }, { status: 404 })); +}; + +const fetchMock = vi.fn(handleCacheRequest); + +beforeEach(async () => { + vi.resetModules(); + values.clear(); + fetchMock.mockReset().mockImplementation(handleCacheRequest); + vi.stubGlobal('fetch', fetchMock); + + const module = await import('../lib/utils/cache/index.worker'); + cache = module.default; + setKVNamespace = module.setKVNamespace; +}); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); +}); + +describe('worker HTTP cache', () => { + it('initializes without I/O and keeps using HTTP when a KV binding is present', async () => { + expect(cache.status.available).toBe(true); + expect(fetchMock).not.toHaveBeenCalled(); + + setKVNamespace(env.CACHE); + await cache.set('worker:http-selected', 'http-value'); + + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(await env.CACHE.get('worker:http-selected')).toBeNull(); + expect(await cache.get('worker:http-selected')).toBe('http-value'); + }); + + it('uses authenticated hashed keys for get, has, and set', async () => { + await cache.set('worker:http-protocol', { ok: true }, 120); + expect(await cache.get('worker:http-protocol')).toBe('{"ok":true}'); + expect(await cache.get('worker:http-protocol', false)).toBe('{"ok":true}'); + expect(await cache.has('worker:http-protocol')).toBe(true); + expect(await cache.has('worker:http-missing')).toBe(false); + + const [[putUrl, putInit], [getUrl], [nonRefreshingUrl], [headUrl, headInit]] = fetchMock.mock.calls; + expect(putUrl).toMatch(/^https:\/\/cache\.example\.com\/v1\/cache\/rsshub%3Ahttp-cache%3A[a-f0-9]{32}$/); + expect(putInit?.headers).toMatchObject({ authorization: 'Bearer worker-cache-token', 'content-type': 'application/json' }); + expect(JSON.parse(putInit!.body!.toString())).toEqual({ ttl: 120, value: '{"ok":true}' }); + expect(getUrl).toBe(`${putUrl}?refresh=1`); + expect(nonRefreshingUrl).toBe(putUrl); + expect(headUrl).toBe(putUrl); + expect(headInit?.method).toBe('HEAD'); + }); + + it('reads global entries without refreshing and defaults their writes to the route expiry', async () => { + await cache.globalCache.set('worker:http-global', { lastBuildDate: 'Sat, 5 Sep 2026 00:00:00 GMT' }); + expect(await cache.globalCache.get('worker:http-global')).toBe('{"lastBuildDate":"Sat, 5 Sep 2026 00:00:00 GMT"}'); + expect(await cache.globalCache.has('worker:http-global')).toBe(true); + expect(await cache.globalCache.has('worker:http-global-missing')).toBe(false); + + const [[putUrl, putInit], [getUrl], [headUrl, headInit]] = fetchMock.mock.calls; + expect(JSON.parse(putInit!.body!.toString()).ttl).toBe(300); + expect(getUrl).toBe(putUrl); + expect(headUrl).toBe(putUrl); + expect(headInit?.method).toBe('HEAD'); + }); + + it('claims through non-refreshing HTTP reads and only writes an unclaimed key', async () => { + expect(await cache.globalCache.claim('worker:http-claim', 60)).toBe(true); + expect(await cache.globalCache.claim('worker:http-claim', 60)).toBe(false); + + expect(fetchMock.mock.calls.map(([, init]) => init?.method)).toEqual(['GET', 'PUT', 'GET']); + expect(fetchMock.mock.calls.every(([url]) => !url.toString().includes('?refresh=1'))).toBe(true); + expect(JSON.parse(fetchMock.mock.calls[1][1]!.body!.toString())).toEqual({ ttl: 60, value: '1' }); + }); + + it('awaits a cache-miss write and returns parsed JSON on a subsequent hit', async () => { + const { promise: writeStarted, resolve: startWrite } = Promise.withResolvers(); + const { promise: writeReleased, resolve: releaseWrite } = Promise.withResolvers(); + fetchMock.mockImplementation(async (input, init) => { + if (init?.method === 'PUT') { + startWrite(); + await writeReleased; + } + return handleCacheRequest(input, init); + }); + + const getValue = vi.fn().mockResolvedValue({ cached: true }); + let finished = false; + const miss = (async () => { + const value = await cache.tryGet('worker:http-tryget', getValue); + finished = true; + return value; + })(); + await writeStarted; + await Promise.resolve(); + expect(finished).toBe(false); + + releaseWrite(); + expect(await miss).toEqual({ cached: true }); + expect(await cache.tryGet('worker:http-tryget', getValue, 600, false)).toEqual({ cached: true }); + expect(getValue).toHaveBeenCalledTimes(1); + + const [[missUrl], [, putInit], [hitUrl]] = fetchMock.mock.calls; + expect(missUrl).toMatch(/\?refresh=1$/); + expect(hitUrl).not.toContain('?refresh=1'); + expect(JSON.parse(putInit!.body!.toString())).toEqual({ ttl: 600, value: '{"cached":true}' }); + }); + + it('falls back to fresh values after HTTP authentication makes the cache unavailable', async () => { + vi.spyOn(console, 'error').mockImplementation(() => {}); + fetchMock.mockResolvedValueOnce(new Response(null, { status: 401 })); + expect(await cache.globalCache.get('worker:http-unauthorized')).toBeNull(); + expect(cache.status.available).toBe(false); + + const getValue = vi.fn().mockResolvedValue('fresh'); + expect(await cache.tryGet('worker:http-unavailable', getValue)).toBe('fresh'); + expect(await cache.tryGet('worker:http-unavailable', getValue)).toBe('fresh'); + expect(getValue).toHaveBeenCalledTimes(2); + expect(await cache.globalCache.get('worker:http-unavailable')).toBeUndefined(); + expect(await cache.globalCache.has('worker:http-unavailable')).toBe(false); + expect(await cache.globalCache.claim('worker:http-unavailable', 60)).toBe(true); + await cache.globalCache.set('worker:http-unavailable', 'ignored'); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index c7c9475dce76..3cf12c82174f 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -69,6 +69,6 @@ export default defineConfig({ }, }, test: { - include: ['lib/**/*.worker.test.ts'], + include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts'], }, }); From e4e8f37aa563d6f0e28852cdb498a6e51dedfbc7 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 6 Sep 2026 20:40:28 +0800 Subject: [PATCH 0713/1190] fix(worker): use native fetch for YouTube API requests --- lib/routes/youtube/api/google.ts | 11 ++- tests/youtube-google.test.ts | 123 +++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 tests/youtube-google.test.ts diff --git a/lib/routes/youtube/api/google.ts b/lib/routes/youtube/api/google.ts index 9292e62a19fa..757c37a19af4 100644 --- a/lib/routes/youtube/api/google.ts +++ b/lib/routes/youtube/api/google.ts @@ -7,6 +7,7 @@ import { config } from '@/config'; import NotFoundError from '@/errors/types/not-found'; import type { Data } from '@/types'; import cache from '@/utils/cache'; +import { isWorker } from '@/utils/is-worker'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; @@ -14,6 +15,8 @@ import { formatDescription, getChannelWithId, getChannelWithUsername, getPlaylis import { getSrtAttachmentBatch } from './subtitles'; const { OAuth2 } = googleAuth; +const workerFetch: typeof fetch = (input, init) => fetch(input, init); +const transportOptions = isWorker ? { fetchImplementation: workerFetch } : {}; dayjs.extend(duration); @@ -30,6 +33,7 @@ if (config.youtube && config.youtube.key) { youtube[index] = googleYoutube({ version: 'v3', auth: key, + ...transportOptions, }); count = index + 1; } @@ -53,7 +57,12 @@ const exec = async (func) => { let youtubeOAuth2Client; if (config.youtube && config.youtube.clientId && config.youtube.clientSecret && config.youtube.refreshToken) { - youtubeOAuth2Client = new OAuth2(config.youtube.clientId, config.youtube.clientSecret, 'https://developers.google.com/oauthplayground'); + youtubeOAuth2Client = new OAuth2({ + clientId: config.youtube.clientId, + clientSecret: config.youtube.clientSecret, + redirectUri: 'https://developers.google.com/oauthplayground', + transporterOptions: transportOptions, + }); youtubeOAuth2Client.setCredentials({ refresh_token: config.youtube.refreshToken }); } diff --git a/tests/youtube-google.test.ts b/tests/youtube-google.test.ts new file mode 100644 index 000000000000..9e279a75b733 --- /dev/null +++ b/tests/youtube-google.test.ts @@ -0,0 +1,123 @@ +import { once } from 'node:events'; +import { createServer } from 'node:http'; + +import { youtube as googleYoutube } from '@googleapis/youtube'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; + +const runtime = vi.hoisted(() => ({ isWorker: true })); + +vi.mock('../lib/config', () => ({ + config: { + youtube: { + key: 'test-api-key', + clientId: 'test-client-id', + clientSecret: 'test-client-secret', + refreshToken: 'test-refresh-token', + }, + cache: { routeExpire: 300 }, + }, +})); +vi.mock('../lib/utils/is-worker', () => ({ + get isWorker() { + return runtime.isWorker; + }, +})); +vi.mock('../lib/utils/cache', () => ({ + default: { + tryGet: vi.fn((_key, getValue) => getValue()), + get: vi.fn(), + set: vi.fn(), + }, +})); +vi.mock('../lib/utils/ofetch', () => ({ default: vi.fn() })); + +const nativeFetch = vi.fn(); +const fallbackRequest = vi.fn(); +const localServer = createServer((request, response) => { + fallbackRequest(request.url); + response.writeHead(200, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ items: [{ id: 'fallback-result' }] })); +}); +let rootUrl: string; + +beforeAll(async () => { + localServer.listen(0, '127.0.0.1'); + await once(localServer, 'listening'); + const address = localServer.address(); + if (!address || typeof address === 'string') { + throw new Error('The local API server did not open a TCP port'); + } + rootUrl = `http://127.0.0.1:${address.port}`; +}); + +afterAll(async () => { + localServer.close(); + await once(localServer, 'close'); +}); + +beforeEach(() => { + vi.resetModules(); + runtime.isWorker = true; + nativeFetch.mockReset(); + fallbackRequest.mockClear(); + for (const key of ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy']) { + vi.stubEnv(key, ''); + } +}); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.unstubAllEnvs(); +}); + +describe('YouTube Google transport', () => { + it.each(['channels', 'playlistItems', 'videos'])('uses the current Worker fetch and parses %s responses as JSON', async (resource) => { + const { exec } = await import('../lib/routes/youtube/api/google'); + nativeFetch.mockResolvedValue(Response.json({ items: [{ id: 'native-result' }] })); + vi.stubGlobal('fetch', nativeFetch); + + const response = await exec((client) => client[resource].list({ part: 'snippet', id: 'test-id' }, { rootUrl, retry: false })); + + expect(response.data.items[0].id).toBe('native-result'); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(expect.any(URL), expect.objectContaining({ method: 'GET' })); + const url = new URL(nativeFetch.mock.calls[0][0].toString()); + expect(url.pathname).toBe(`/youtube/v3/${resource}`); + expect(url.searchParams.get('key')).toBe('test-api-key'); + expect(fallbackRequest).not.toHaveBeenCalled(); + }); + + it('uses Worker fetch for OAuth refresh and authenticated subscription requests', async () => { + const { youtubeOAuth2Client } = await import('../lib/routes/youtube/api/google'); + Object.assign(youtubeOAuth2Client.endpoints, { oauth2TokenUrl: `${rootUrl}/token` }); + nativeFetch.mockResolvedValueOnce(Response.json({ access_token: 'test-access-token', expires_in: 3600, token_type: 'Bearer' })).mockResolvedValueOnce(Response.json({ items: [{ id: 'test-subscription' }] })); + vi.stubGlobal('fetch', nativeFetch); + + expect((await youtubeOAuth2Client.getAccessToken()).token).toBe('test-access-token'); + const response = await googleYoutube('v3').subscriptions.list({ auth: youtubeOAuth2Client, part: ['snippet'], mine: true }, { rootUrl, retry: false }); + expect(response.data.items).toEqual([{ id: 'test-subscription' }]); + + expect(nativeFetch).toHaveBeenCalledTimes(2); + const [[tokenUrl, tokenOptions], [subscriptionsUrl, subscriptionsOptions]] = nativeFetch.mock.calls; + expect(new URL(tokenUrl.toString()).pathname).toBe('/token'); + expect(tokenOptions?.method).toBe('POST'); + expect(new URLSearchParams(tokenOptions?.body?.toString()).get('refresh_token')).toBe('test-refresh-token'); + expect(new URL(subscriptionsUrl.toString()).pathname).toBe('/youtube/v3/subscriptions'); + expect(new Headers(subscriptionsOptions?.headers).get('authorization')).toBe('Bearer test-access-token'); + expect(fallbackRequest).not.toHaveBeenCalled(); + }); + + it('keeps the SDK default transport in Node', async () => { + runtime.isWorker = false; + const { exec } = await import('../lib/routes/youtube/api/google'); + nativeFetch.mockRejectedValue(new Error('Unexpected global fetch in Node')); + vi.stubGlobal('fetch', nativeFetch); + + const response = await exec((client) => client.channels.list({ part: 'snippet', id: 'test-id' }, { rootUrl, retry: false })); + + expect(response.status).toBe(200); + expect(response.data.items).toEqual([{ id: 'fallback-result' }]); + expect(fallbackRequest).toHaveBeenCalledOnce(); + expect(new URL(fallbackRequest.mock.calls[0][0], rootUrl).pathname).toBe('/youtube/v3/channels'); + expect(nativeFetch).not.toHaveBeenCalled(); + }); +}); From 900e36244b053d75b2342a23921b247ec48683ce Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 6 Sep 2026 21:17:53 +0800 Subject: [PATCH 0714/1190] fix(worker): skip YouTube subtitle extraction --- lib/routes/youtube/api/subtitles.ts | 13 +++++- tests/youtube-subtitles.test.ts | 66 +++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/youtube-subtitles.test.ts diff --git a/lib/routes/youtube/api/subtitles.ts b/lib/routes/youtube/api/subtitles.ts index 929db4dd9229..e85ac457a18c 100644 --- a/lib/routes/youtube/api/subtitles.ts +++ b/lib/routes/youtube/api/subtitles.ts @@ -2,6 +2,7 @@ import pMap from 'p-map'; import { getSubtitles } from 'youtube-caption-extractor'; import cache from '@/utils/cache'; +import { isWorker } from '@/utils/is-worker'; function pad(n: number, width: number = 2) { return String(n).padStart(width, '0'); @@ -35,8 +36,12 @@ ${seg.text} .join('\n'); } -export const getSubtitlesByVideoId = (videoId: string) => - cache.tryGet(`youtube:getSubtitlesByVideoId:${videoId}`, async () => { +export const getSubtitlesByVideoId = (videoId: string) => { + // Subtitle extraction can exceed the Workers memory limit. + if (isWorker) { + return Promise.resolve(''); + } + return cache.tryGet(`youtube:getSubtitlesByVideoId:${videoId}`, async () => { try { const subtitles = await getSubtitles({ videoID: videoId }); const srt = convertToSrt(subtitles); @@ -46,6 +51,7 @@ export const getSubtitlesByVideoId = (videoId: string) => return ''; } }); +}; const createSubtitleDataUrl = (srt: string): string => `data:text/plain;charset=utf-8,${encodeURIComponent(srt)}`; @@ -74,6 +80,9 @@ export const getSrtAttachment = async (videoId: string): Promise { + if (isWorker) { + return {}; + } const results = await pMap( videoIds, async (videoId) => { diff --git a/tests/youtube-subtitles.test.ts b/tests/youtube-subtitles.test.ts new file mode 100644 index 000000000000..aa7f28c7f379 --- /dev/null +++ b/tests/youtube-subtitles.test.ts @@ -0,0 +1,66 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { getSrtAttachment, getSrtAttachmentBatch, getSubtitlesByVideoId } from '../lib/routes/youtube/api/subtitles'; + +const mocks = vi.hoisted(() => ({ + isWorker: false, + cached: new Map(), + tryGet: vi.fn(), + getSubtitles: vi.fn(), +})); + +vi.mock('../lib/utils/is-worker', () => ({ + get isWorker() { + return mocks.isWorker; + }, +})); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('youtube-caption-extractor', () => ({ getSubtitles: mocks.getSubtitles })); + +const expectedSrt = '1\n00:00:00,250 --> 00:00:01,750\nHello & 字幕\n\n2\n00:01:01,125 --> 00:01:03,625\nSecond caption\n'; +const cachedSrt = '1\n00:00:00,000 --> 00:00:01,000\nPreviously cached\n'; + +beforeEach(() => { + mocks.isWorker = false; + mocks.cached.clear(); + mocks.tryGet.mockReset().mockImplementation((key: string, getValue: () => Promise) => (mocks.cached.has(key) ? Promise.resolve(mocks.cached.get(key)) : getValue())); + mocks.getSubtitles.mockReset().mockResolvedValue([ + { start: '0.25', dur: '1.5', text: 'Hello & 字幕' }, + { start: '61.125', dur: '2.5', text: 'Second caption' }, + ]); +}); + +describe('YouTube subtitles by runtime', () => { + it('skips all Worker subtitle paths before reading even existing cached subtitles', async () => { + mocks.isWorker = true; + mocks.cached.set('youtube:getSubtitlesByVideoId:cached', cachedSrt); + + expect(await getSubtitlesByVideoId('fresh')).toBe(''); + expect(await getSubtitlesByVideoId('cached')).toBe(''); + expect(await getSrtAttachment('cached')).toEqual([]); + expect(await getSrtAttachmentBatch(['fresh', 'cached'])).toEqual({}); + + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.getSubtitles).not.toHaveBeenCalled(); + }); + + it('still extracts and converts subtitle segments to SRT on Node', async () => { + expect(await getSubtitlesByVideoId('fresh')).toBe(expectedSrt); + + expect(mocks.tryGet).toHaveBeenCalledExactlyOnceWith('youtube:getSubtitlesByVideoId:fresh', expect.any(Function)); + expect(mocks.getSubtitles).toHaveBeenCalledExactlyOnceWith({ videoID: 'fresh' }); + }); + + it('preserves Node SRT attachments for fetched and cached videos', async () => { + mocks.cached.set('youtube:getSubtitlesByVideoId:cached', cachedSrt); + + const attachments = await getSrtAttachmentBatch(['fresh', 'cached']); + expect(attachments).toEqual({ + fresh: [{ url: `data:text/plain;charset=utf-8,${encodeURIComponent(expectedSrt)}`, mime_type: 'text/srt', title: 'Subtitles' }], + cached: [{ url: `data:text/plain;charset=utf-8,${encodeURIComponent(cachedSrt)}`, mime_type: 'text/srt', title: 'Subtitles' }], + }); + expect(await getSrtAttachment('cached')).toEqual(attachments.cached); + expect(mocks.getSubtitles).toHaveBeenCalledExactlyOnceWith({ videoID: 'fresh' }); + expect(mocks.tryGet).toHaveBeenCalledWith('youtube:getSubtitlesByVideoId:cached', expect.any(Function)); + }); +}); From 1d3432aa00c7f8f07ba21d44bda32bcdaa58b1f2 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 6 Sep 2026 21:50:49 +0800 Subject: [PATCH 0715/1190] fix(worker): replace JSDOM with data parsers and browser sessions --- lib/routes/dongqiudi/utils.ts | 26 +- lib/routes/hotukdeals/hottest.ts | 21 +- lib/routes/lofter/tag.ts | 15 +- lib/routes/nintendo/utils.ts | 12 +- lib/routes/pixiv/novel-api/content/nsfw.ts | 23 +- lib/routes/qq/kg/cache.ts | 25 +- lib/routes/qq/kg/user.ts | 21 +- lib/routes/xueqiu/column.ts | 20 +- lib/routes/yamibo/bbs/forum.ts | 54 +-- lib/routes/yamibo/bbs/thread.ts | 1 + lib/routes/yamibo/utils.ts | 158 +++++---- lib/routes/zhihu/activities.ts | 4 +- lib/routes/zhihu/answers.ts | 4 +- lib/routes/zhihu/browser.ts | 36 ++ lib/routes/zhihu/posts.ts | 4 +- lib/routes/zhihu/question.ts | 4 +- lib/routes/zhihu/topic.ts | 4 +- lib/routes/zhihu/utils.ts | 136 +------ lib/routes/zhihu/zhuanlan.ts | 4 +- lib/utils/parse-script-data.test.ts | 134 +++++++ lib/utils/parse-script-data.ts | 390 +++++++++++++++++++++ lib/utils/playwright.ts | 66 ++-- lib/utils/playwright.worker.ts | 60 ++-- package.json | 3 +- pnpm-lock.yaml | 78 +++-- tests/lofter-tag.test.ts | 63 ++++ tests/nuxt-routes.test.ts | 92 +++++ tests/page-script-data-routes.test.ts | 208 +++++++++++ tests/playwright-lifecycle.worker.test.ts | 71 ++++ tests/yamibo-browser.test.ts | 72 ++++ tests/zhihu-browser.test.ts | 91 +++++ vitest.workers.config.ts | 2 +- 32 files changed, 1522 insertions(+), 380 deletions(-) create mode 100644 lib/routes/zhihu/browser.ts create mode 100644 lib/utils/parse-script-data.test.ts create mode 100644 lib/utils/parse-script-data.ts create mode 100644 tests/lofter-tag.test.ts create mode 100644 tests/nuxt-routes.test.ts create mode 100644 tests/page-script-data-routes.test.ts create mode 100644 tests/playwright-lifecycle.worker.test.ts create mode 100644 tests/yamibo-browser.test.ts create mode 100644 tests/zhihu-browser.test.ts diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index 67916893e449..23f4fa8b4c44 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -1,11 +1,20 @@ import { load } from 'cheerio'; -import { JSDOM } from 'jsdom'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptData } from '@/utils/parse-script-data'; import timezone from '@/utils/timezone'; +const readNuxtData = (response: string) => { + const $ = load(response); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .find((source) => /\b__NUXT__\s*=/.test(source)); + return script ? parseScriptData<{ data?: any[] } | undefined>(script, '__NUXT__')?.data?.[0] : undefined; +}; + const processVideo = (content) => { content('div.video').each((i, v) => { let link = new URL(v.attribs.src); @@ -69,11 +78,10 @@ export const processFeed = async (type, id) => { const apiUrl = 'https://api.dongqiudi.com/v3/archive/app/channel/feeds'; const { data: response } = await got(link); - const { window } = new JSDOM(response, { - runScripts: 'dangerously', - }); - - const nuxtData = window.__NUXT__.data[0]; + const nuxtData = readNuxtData(response); + if (!nuxtData) { + throw new Error('Unable to extract Dongqiudi page data'); + } let name; let image; if (type === 'team') { @@ -131,11 +139,7 @@ export const processFeed = async (type, id) => { }; export const processFeedType2 = (item, response) => { - const dom = new JSDOM(response, { - runScripts: 'dangerously', - }); - - const data = dom.window.__NUXT__?.data?.[0]?.article; + const data = readNuxtData(response)?.article; // filter out undefined item if (!data) { diff --git a/lib/routes/hotukdeals/hottest.ts b/lib/routes/hotukdeals/hottest.ts index 54b89464514e..cda2e96386d0 100644 --- a/lib/routes/hotukdeals/hottest.ts +++ b/lib/routes/hotukdeals/hottest.ts @@ -1,7 +1,8 @@ -import { JSDOM } from 'jsdom'; +import { load } from 'cheerio'; import type { Route } from '@/types'; import got from '@/utils/got'; +import { parseScriptData } from '@/utils/parse-script-data'; export const route: Route = { path: '/hottest', @@ -30,10 +31,20 @@ export const route: Route = { async function handler() { const data = await got.get('https://www.hotukdeals.com/'); - const dom = new JSDOM(data.data, { - runScripts: 'dangerously', - }); - const threads = dom.window.__INITIAL_STATE__.widgets.hottestWidget.threads; + const $ = load(data.data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .filter((source) => source.includes('__INITIAL_STATE__')) + .join('\n'); + const { widgets } = parseScriptData<{ + widgets: { + hottestWidget: { + threads: Array<{ title: string; mainImage: { path: string; name: string }; temperature: number; displayPrice: string; url: string }>; + }; + }; + }>(script, '__INITIAL_STATE__'); + const threads = widgets.hottestWidget.threads; return { title: 'hotukdeals hottest', diff --git a/lib/routes/lofter/tag.ts b/lib/routes/lofter/tag.ts index 00ea505dc7ee..5d294971a0b2 100644 --- a/lib/routes/lofter/tag.ts +++ b/lib/routes/lofter/tag.ts @@ -1,11 +1,11 @@ import { load } from 'cheerio'; -import { JSDOM } from 'jsdom'; import { config } from '@/config'; import ConfigNotFoundError from '@/errors/types/config-not-found'; import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptCallback } from '@/utils/parse-script-data'; export const route: Route = { path: '/tag/:name?/:type?', @@ -83,18 +83,7 @@ async function handler(ctx) { }, }); - const dom = new JSDOM( - ``, - { - runScripts: 'dangerously', - } - ); - const data = dom.window.dwr.engine.data[2]; + const data = parseScriptCallback(response.data, 'dwr.engine._remoteHandleCallback'); const title = { diff --git a/lib/routes/nintendo/utils.ts b/lib/routes/nintendo/utils.ts index 4dadd685e326..22be8f0966e9 100644 --- a/lib/routes/nintendo/utils.ts +++ b/lib/routes/nintendo/utils.ts @@ -3,10 +3,10 @@ import 'dayjs/locale/zh-cn.js'; import { load } from 'cheerio'; import dayjs from 'dayjs'; import localizedFormat from 'dayjs/plugin/localizedFormat.js'; -import { JSDOM } from 'jsdom'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptData } from '@/utils/parse-script-data'; import { renderEshopCnDescription } from './templates/eshop-cn'; @@ -15,10 +15,12 @@ dayjs.extend(localizedFormat); function nuxtReader(data) { let nuxt; try { - const dom = new JSDOM(data, { - runScripts: 'dangerously', - }); - nuxt = dom.window.__NUXT__.data[0]; + const $ = load(data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .find((source) => /\b__NUXT__\s*=/.test(source)); + nuxt = parseScriptData<{ data: any[] }>(script ?? '', '__NUXT__').data[0]; } catch { throw new Error('Nuxt 框架信息提取失败,请报告这个问题'); } diff --git a/lib/routes/pixiv/novel-api/content/nsfw.ts b/lib/routes/pixiv/novel-api/content/nsfw.ts index 1083600f6544..c482debd7b30 100644 --- a/lib/routes/pixiv/novel-api/content/nsfw.ts +++ b/lib/routes/pixiv/novel-api/content/nsfw.ts @@ -1,8 +1,9 @@ -import { JSDOM, VirtualConsole } from 'jsdom'; +import { load } from 'cheerio'; import queryString from 'query-string'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptData } from '@/utils/parse-script-data'; import { maskHeader } from '../../constants'; import got from '../../pixiv-got'; @@ -22,16 +23,16 @@ export async function getNSFWNovelContent(novelId: string, token: string): Promi }), }); - const virtualConsole = new VirtualConsole().on('error', () => void 0); - - const { window } = new JSDOM(response.data, { - runScripts: 'dangerously', - virtualConsole, - }); - - const novelDetail: NSFWNovelDetail | undefined = window.pixiv?.novel; - - window.close(); + const $ = load(response.data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .filter((source) => source.includes('pixiv')) + .join('\n'); + if (!script) { + throw new Error('No novel data found'); + } + const novelDetail = parseScriptData(script, 'window.pixiv.novel'); if (!novelDetail) { throw new Error('No novel data found'); diff --git a/lib/routes/qq/kg/cache.ts b/lib/routes/qq/kg/cache.ts index 617fbe3744e6..a440b54c7407 100644 --- a/lib/routes/qq/kg/cache.ts +++ b/lib/routes/qq/kg/cache.ts @@ -1,7 +1,8 @@ -import { JSDOM } from 'jsdom'; +import { load } from 'cheerio'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { parseScriptData } from '@/utils/parse-script-data'; export default { getPlayInfo: async (ctx, shareId, ksong_mid = '') => { @@ -9,10 +10,24 @@ export default { const cache_key = ksong_mid ? `ksong:${ksong_mid}` : link; const data = await cache.tryGet(cache_key, async () => { const response = await got(link); - const { window } = new JSDOM(response.data, { - runScripts: 'dangerously', - }); - const data = window.__DATA__; + const $ = load(response.data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .filter((source) => source.includes('__DATA__')) + .join('\n'); + const data = parseScriptData<{ + detail: { + song_name: string; + content: string; + nick: string; + cover: string; + playurl: string; + ksong_mid: string; + ctime: number; + comments: Array<{ nick: string; content: string; ctime: number; comment_id: string }>; + }; + }>(script, 'window.__DATA__'); const name = data.detail.song_name; const description = data.detail.content; const author = data.detail.nick; diff --git a/lib/routes/qq/kg/user.ts b/lib/routes/qq/kg/user.ts index 44c7ec22a9f4..4f2d6e9e333e 100644 --- a/lib/routes/qq/kg/user.ts +++ b/lib/routes/qq/kg/user.ts @@ -1,8 +1,9 @@ -import { JSDOM } from 'jsdom'; +import { load } from 'cheerio'; import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptData } from '@/utils/parse-script-data'; import cache from './cache'; @@ -29,10 +30,20 @@ async function handler(ctx) { const url = `https://node.kg.qq.com/personal?uid=${userId}`; const response = await got(url); - const { window } = new JSDOM(response.data, { - runScripts: 'dangerously', - }); - const data = window.__DATA__; + const $ = load(response.data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .filter((source) => source.includes('__DATA__')) + .join('\n'); + const data = parseScriptData<{ + data: { + nickname: string; + head_img_url: string; + ugclist: Array<{ shareid: string; ksong_mid: string; title?: string; ctime: number; avatar: string }>; + }; + share: { content: string }; + }>(script, 'window.__DATA__'); const author = data.data.nickname; const authorimg = data.data.head_img_url; const ugcList = data.data.ugclist; diff --git a/lib/routes/xueqiu/column.ts b/lib/routes/xueqiu/column.ts index 0a40492e2884..22823cf7b64e 100644 --- a/lib/routes/xueqiu/column.ts +++ b/lib/routes/xueqiu/column.ts @@ -1,9 +1,10 @@ -import { JSDOM } from 'jsdom'; +import { load } from 'cheerio'; import { CookieJar } from 'tough-cookie'; import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { parseScriptData } from '@/utils/parse-script-data'; const cookieJar = new CookieJar(); const baseUrl = 'https://xueqiu.com'; @@ -43,10 +44,13 @@ async function handler(ctx) { const pageData = await got(pageUrl, { cookieJar, }); - const { window } = new JSDOM(pageData.data, { - runScripts: 'dangerously', - }); - const SNOWMAN_TARGET = window.SNOWMAN_TARGET; + const $ = load(pageData.data); + const script = $('script') + .toArray() + .map((element) => $(element).text()) + .filter((source) => source.includes('SNOWMAN_TARGET')) + .join('\n'); + const snowmanTarget = parseScriptData<{ screen_name: string; description: string }>(script, 'SNOWMAN_TARGET'); const { data } = await got(`${baseUrl}/statuses/original/timeline.json`, { cookieJar, @@ -65,13 +69,13 @@ async function handler(ctx) { description: item.description, pubDate: parseDate(item.created_at, 'x'), link: `${baseUrl}${item.target}`, - author: SNOWMAN_TARGET.screen_name, + author: snowmanTarget.screen_name, })); return { - title: `${SNOWMAN_TARGET.screen_name} - 雪球`, + title: `${snowmanTarget.screen_name} - 雪球`, link: pageUrl, - description: SNOWMAN_TARGET.description, + description: snowmanTarget.description, item: items, }; } diff --git a/lib/routes/yamibo/bbs/forum.ts b/lib/routes/yamibo/bbs/forum.ts index e0f348e1908b..6bc6d19deff3 100644 --- a/lib/routes/yamibo/bbs/forum.ts +++ b/lib/routes/yamibo/bbs/forum.ts @@ -7,7 +7,7 @@ import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; -import { bbsOrigin, fetchThread, generateDescription, getDate } from '../utils'; +import { bbsOrigin, generateDescription, getDate, ThreadFetcher } from '../utils'; export const route: Route = { name: 'BBS - 板块', @@ -22,6 +22,7 @@ export const route: Route = { handler, features: { antiCrawler: true, + requirePuppeteer: true, requireConfig: [ { optional: true, @@ -85,32 +86,37 @@ async function handler(ctx: Context): Promise { }; }); - items = await pMap( - items, - async (item) => - await cache.tryGet(item.link!, async () => { - let description: string | undefined; - const { data } = await fetchThread(item.id!); - if (data && !data.startsWith('`, - { - runScripts: 'dangerously', - } - ); - const locationValue = dom.window.locationValue; - if (locationValue) { - const searchParams = new URLSearchParams(locationValue); - const _dsign = searchParams.get('_dsign'); - if (_dsign) { - options = { - ...options, - _dsign, - }; + async close() { + this.closed = true; + try { + await this.pending; + } catch { + // Article failures have already been reported to their callers. + } + if (this.browser) { + let browser: BrowserPage; + try { + browser = await this.browser; + } catch { + // openBrowser cleans up failed initialization before rejecting. + return; } + await browser.destroy(); } - return await fetchThread(tid, options, retry + 1); } +} - return { - link, - data, - }; +export async function fetchThread(tid: string, options?: ThreadOptions): Promise { + const fetcher = new ThreadFetcher(); + try { + return await fetcher.fetchThread(tid, options); + } finally { + await fetcher.close(); + } } export function generateDescription($item: Cheerio, postId: string) { diff --git a/lib/routes/zhihu/activities.ts b/lib/routes/zhihu/activities.ts index d1514742d82b..ca654ad09b20 100644 --- a/lib/routes/zhihu/activities.ts +++ b/lib/routes/zhihu/activities.ts @@ -17,11 +17,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', optional: true, }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/answers.ts b/lib/routes/zhihu/answers.ts index b55fea079523..56fc5094b7e0 100644 --- a/lib/routes/zhihu/answers.ts +++ b/lib/routes/zhihu/answers.ts @@ -13,11 +13,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', optional: true, }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/browser.ts b/lib/routes/zhihu/browser.ts new file mode 100644 index 000000000000..7863a0d64f09 --- /dev/null +++ b/lib/routes/zhihu/browser.ts @@ -0,0 +1,36 @@ +import { getPlaywrightPage } from '@/utils/playwright'; + +const allowedResources = new Set(['document', 'script', 'xhr', 'fetch']); + +export const getBrowserCredentials = async (url: string, configuredCookies: string) => { + const { page, context, destroy } = await getPlaywrightPage(url, { noGoto: true, closeTimeout: 0 }); + try { + const cookies = configuredCookies + .split(';') + .map((pair) => pair.trim()) + .filter((pair) => pair.indexOf('=') > 0) + .map((pair) => { + const separator = pair.indexOf('='); + return { name: pair.slice(0, separator), value: pair.slice(separator + 1), domain: '.zhihu.com', path: '/' }; + }); + if (cookies.length) { + await context.addCookies(cookies); + } + await page.route('**/*', (route) => (allowedResources.has(route.request().resourceType()) ? route.continue() : route.abort())); + await page.goto(url, { waitUntil: 'domcontentloaded' }); + await page.waitForFunction(() => document.cookie.split(';').some((pair) => pair.trimStart().startsWith('__zse_ck=')), undefined, { timeout: 10000 }); + + const browserCookies = await context.cookies('https://www.zhihu.com/'); + const dc0 = browserCookies.find((cookie) => cookie.name === 'd_c0')?.value; + const zseCk = browserCookies.find((cookie) => cookie.name === '__zse_ck')?.value; + if (!dc0 || !zseCk) { + throw new Error('The browser did not receive the required cookies'); + } + const ua = await page.evaluate(() => window.navigator.userAgent); + return { dc0, zseCk, ua }; + } catch { + throw new Error('zhihu: browser access did not provide valid credentials. Configure ZHIHU_COOKIES with an accessible session containing d_c0 and __zse_ck.'); + } finally { + await destroy(); + } +}; diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 1366b4c9c929..528a4c2067f3 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -15,11 +15,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', optional: true, }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index 126b5897a92d..716748de030f 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.ts @@ -13,11 +13,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', optional: true, }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/topic.ts b/lib/routes/zhihu/topic.ts index 3ced96358b91..b057baa6f30e 100644 --- a/lib/routes/zhihu/topic.ts +++ b/lib/routes/zhihu/topic.ts @@ -14,10 +14,10 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index 455c82e0b7b0..428da066fa77 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -1,12 +1,8 @@ import { load } from 'cheerio'; -import { JSDOM, VirtualConsole } from 'jsdom'; -import { VM } from 'vm2'; import { config } from '@/config'; import cache from '@/utils/cache'; -import { generateHeaders } from '@/utils/header-generator'; import md5 from '@/utils/md5'; -import ofetch from '@/utils/ofetch'; import { encrypt as g_encrypt } from './execlib/x-zse-96-v3'; @@ -70,126 +66,10 @@ const getCookieValueFrom = (cookieStr: string | undefined, key: string) => export const getCookieValueByKey = (key: string) => getCookieValueFrom(config.zhihu.cookies, key); -let isUnreachableRuntimeErrorGuarded = false; const pendingZseCredentials = new Map>(); -const preventUnreachableRuntimeError = () => { - if (isUnreachableRuntimeErrorGuarded) { - return; - } - isUnreachableRuntimeErrorGuarded = true; - process.on('unhandledRejection', (reason) => { - const error = reason as { name?: string; message?: string } | undefined; - if (error?.name === 'RuntimeError' && error.message === 'unreachable') { - return; - } - throw reason; - }); -}; - -const generateZseCk = async (url: string, apiPath: string, configuredDc0: string) => { - preventUnreachableRuntimeError(); - - // `__zse_ck` is checked against the user-agent that generated it. - const ua = generateHeaders()['user-agent']; - const headers = { 'user-agent': ua }; - - let dc0 = configuredDc0; - if (!dc0) { - const seed = await ofetch.raw('https://www.zhihu.com/explore', { - headers, - redirect: 'manual', - ignoreResponseError: true, - }); - dc0 = - (seed.headers.getSetCookie?.() ?? []) - .find((line) => line.startsWith('d_c0=')) - ?.split(';', 1)[0] - .slice('d_c0='.length) - .split('|', 1)[0] - .replace(/=+$/, '') || ''; - } - if (!dc0) { - throw new Error('zhihu: failed to obtain a guest d_c0 cookie'); - } - - const challenge = await ofetch.raw(`https://www.zhihu.com${apiPath}`, { - headers: { - ...headers, - cookie: `d_c0=${dc0}; __zse_ck=005_x-x`, - referer: url, - 'x-requested-with': 'fetch', - }, - ignoreResponseError: true, - }); - const html = challenge._data ?? ''; - const meta = html.match(/id="zh-zse-ck"[^>]*content="([^"]*)"/)?.[1]; - const hash = html.match(/zse-ck\/v4\/([a-f0-9]+)\.js/)?.[1]; - if (!meta || !hash) { - throw new Error('zhihu: challenge page did not contain an URL to __zse_ck meta/script'); - } - - const vmScript = await ofetch(`https://static.zhihu.com/zse-ck/v4/${hash}.js`, { - headers, - parseResponse: (text) => text, - }); - const dom = new JSDOM(``, { - url, - referrer: 'https://www.zhihu.com/', - runScripts: 'outside-only', - pretendToBeVisual: true, - virtualConsole: new VirtualConsole(), - }); - const { window } = dom; - Object.defineProperties(window.navigator, { - userAgent: { value: ua, configurable: true }, - webdriver: { value: false, configurable: true }, - }); - window.TextEncoder = TextEncoder; - window.TextDecoder = TextDecoder as typeof window.TextDecoder; - window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary'); - window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64'); - Object.assign(window, { __g: {} }); - - const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie'); - const setCookie = cookieDescriptor?.set; - if (!cookieDescriptor?.get || !setCookie) { - window.close(); - throw new Error('zhihu: JSDOM did not provide document.cookie accessors'); - } - const tokenPromise = new Promise((resolve) => { - Object.defineProperty(window.document, 'cookie', { - configurable: true, - get: cookieDescriptor.get, - set(value: string) { - setCookie.call(window.document, value); - const token = value.match(/__zse_ck=([^;]+)/)?.[1]; - if (token?.includes('-')) { - resolve(token); - } - }, - }); - }); - - let zseCk: string | undefined; - try { - // Zhihu's challenge is intentionally delivered as executable JavaScript. - new VM({ timeout: 3000, sandbox: window }).run(vmScript); - const timeout = new Promise((resolve) => { - setTimeout(resolve, 3000); - }); - zseCk = await Promise.race([tokenPromise, timeout]); - } finally { - window.close(); - } - if (!zseCk) { - throw new Error('zhihu: WASM VM did not produce a __zse_ck'); - } - return { dc0, zseCk, ua }; -}; - -const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: string) => { - const cacheKey = `zhihu:zse-ck:v4:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; +const getGeneratedZseCredentials = (url: string, configuredDc0: string) => { + const cacheKey = `zhihu:browser-credentials:v1:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; const pending = pendingZseCredentials.get(cacheKey); if (pending) { return pending; @@ -197,7 +77,15 @@ const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: const created = (async () => { try { - return await cache.tryGet(cacheKey, () => generateZseCk(url, apiPath, configuredDc0), config.cache.contentExpire, false); + return await cache.tryGet( + cacheKey, + async () => { + const { getBrowserCredentials } = await import('./browser'); + return getBrowserCredentials(url, configuredDc0 ? config.zhihu.cookies || '' : ''); + }, + config.cache.contentExpire, + false + ); } finally { pendingZseCredentials.delete(cacheKey); } @@ -231,7 +119,7 @@ export const getSignedHeader = async (url: string, apiPath: string) => { if (configuredDc0 && configuredZseCk) { cookieStr = configured; } else { - const credentials = await getGeneratedZseCredentials(url, apiPath, configuredDc0); + const credentials = await getGeneratedZseCredentials(url, configuredDc0); // Login cookies only belong to the configured d_c0 session. Do not mix // an isolated z_c0 with a newly-created guest session. cookieStr = configuredDc0 ? mergeGeneratedCookies(configured, credentials.dc0, credentials.zseCk) : `__zse_ck=${credentials.zseCk}; d_c0=${credentials.dc0}`; diff --git a/lib/routes/zhihu/zhuanlan.ts b/lib/routes/zhihu/zhuanlan.ts index d37ffa2b9c7d..7dd8bfbcb231 100644 --- a/lib/routes/zhihu/zhuanlan.ts +++ b/lib/routes/zhihu/zhuanlan.ts @@ -15,10 +15,10 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: '', + description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', }, ], - requirePuppeteer: false, + requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/utils/parse-script-data.test.ts b/lib/utils/parse-script-data.test.ts new file mode 100644 index 000000000000..da79f3a03ea8 --- /dev/null +++ b/lib/utils/parse-script-data.test.ts @@ -0,0 +1,134 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { parseScriptCallback, parseScriptData } from './parse-script-data'; + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe('serialized script data', () => { + it.each(['__DATA__', 'window.__DATA__', 'globalThis.__DATA__', 'self.__DATA__'])('normalizes the global target %s', (target) => { + expect(parseScriptData(String.raw`window.__DATA__ = { title: "Hello\u0020世界", tags: ["a", "b"] };`, target)).toEqual({ title: 'Hello 世界', tags: ['a', 'b'] }); + }); + + it('resolves variables, nested assignments, and data-only unary and logical expressions', () => { + const source = 'var title="A & B", unused=analytics(); window.pixiv=window.pixiv||{}; pixiv.novel={title, count:-2, available:!0, absent:void 0};'; + expect(parseScriptData(source, 'window.pixiv.novel')).toEqual({ title: 'A & B', count: -2, available: true, absent: undefined }); + }); + + it('reads parent-object assignments and static bracket targets', () => { + expect(parseScriptData('window.pixiv={novel:{title:"Book"}};', 'self["pixiv"].novel')).toEqual({ title: 'Book' }); + }); + + it('reads Nuxt IIFE parameters and object mutations without changing outer bindings', () => { + const source = 'var a="outer";window.__NUXT__=(function(a,b,c){a.title=b;a.tags=c;return {data:[a,a],error:null}}({},"News",["tag"]));'; + const value = parseScriptData<{ data: Array<{ title: string; tags: string[] }>; error: null }>(source, '__NUXT__'); + expect(value).toEqual({ + data: [ + { title: 'News', tags: ['tag'] }, + { title: 'News', tags: ['tag'] }, + ], + error: null, + }); + expect(value.data[0]).toBe(value.data[1]); + expect(parseScriptData(source, 'a')).toBe('outer'); + }); + + it('preserves repeated var declarations and evaluates unused IIFE arguments in order', () => { + const source = 'var a={value:1};var a;window.__DATA__=(function(x){return x})(a,a.value=2);'; + expect(parseScriptData(source, '__DATA__')).toEqual({ value: 2 }); + }); + + it('does not call unrelated application code or access the host global object', () => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + vi.stubGlobal('__scriptDataSideEffect', 'unchanged'); + + const value = parseScriptData('globalThis.__scriptDataSideEffect="changed";fetch("https://example.invalid/");window.__DATA__={ok:true};', '__DATA__'); + + expect(value).toEqual({ ok: true }); + expect(fetchSpy).not.toHaveBeenCalled(); + expect(Reflect.get(globalThis, '__scriptDataSideEffect')).toBe('unchanged'); + }); + + it.each([ + 'window.__DATA__ = fetch("https://example.invalid/");', + 'window.__DATA__ = Function("return 42")();', + 'window.__DATA__ = { get secret() { return fetch("https://example.invalid/"); } };', + 'window.__DATA__ = (function(){ while(true){} return {}; })();', + 'window.__DATA__ = (function(){ return {}; })(fetch("https://example.invalid/"));', + ])('rejects unsupported expressions required by the target without executing them', (source) => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + expect(() => parseScriptData(source, '__DATA__')).toThrow(/Unsupported|ordinary/); + expect(fetchSpy).not.toHaveBeenCalled(); + }); + + it.each(['__proto__', 'constructor', 'prototype'])('rejects the unsafe property %s', (key) => { + expect(() => parseScriptData(`window.__DATA__={"${key}":{polluted:true}};`, '__DATA__')).toThrow('Unsafe script data property'); + expect(() => parseScriptData(`var value={};value["${key}"]={polluted:true};window.__DATA__=value;`, '__DATA__')).toThrow('Unsafe script data property'); + expect(Object.hasOwn(Object.prototype, 'polluted')).toBe(false); + }); + + it.each(['window.__DATA__()', 'window[computed]', 'window.__DATA__; unrelated()', 'window.__proto__'])('rejects unsupported target paths: %s', (target) => { + expect(() => parseScriptData('window.__DATA__={};', target)).toThrow(); + }); + + it('reports missing targets and unresolved variable dependencies', () => { + expect(() => parseScriptData('unrelated();', '__DATA__')).toThrow('target was not found'); + expect(() => parseScriptData('var data=unrelated();window.__DATA__=data;', '__DATA__')).toThrow('Unsupported script data expression'); + }); + + it.each(['if(true){window.__DATA__.value=2;}', 'Object.assign(window.__DATA__,{value:2});', 'window.__DATA__.value++;', 'window.__DATA__[unknown()]={};'])( + 'rejects unsupported code that could change extracted data: %s', + (suffix) => { + expect(() => parseScriptData(`window.__DATA__={value:1};${suffix}`, '__DATA__')).toThrow('Unsupported script data'); + } + ); + + it.each(['if(true){window.__DATA__.newField=2;}', 'var ignored=Object.assign(window.__DATA__,{value:2});', 'window.ignored=Object.assign(window.__DATA__,{value:2});'])( + 'rejects mutations hidden in new properties and unused assignments: %s', + (suffix) => { + expect(() => parseScriptData(`window.__DATA__={};${suffix}`, '__DATA__')).toThrow('Unsupported script data'); + } + ); + + it('rejects cyclic data and excessively large sources or sparse arrays', () => { + expect(() => parseScriptData('var a={};a.self=a;window.__DATA__=a;', '__DATA__')).toThrow('Cyclic script data'); + expect(() => parseScriptData(' '.repeat(2_000_001), '__DATA__')).toThrow('size limit'); + expect(() => parseScriptData('window.__DATA__=(function(){var a=[];a[100000]={};return a})();', '__DATA__')).toThrow('bounded numeric indexes'); + }); +}); + +describe('serialized callback data', () => { + it('extracts a DWR callback argument without invoking the callback', () => { + const callback = vi.fn(); + vi.stubGlobal('dwr', { engine: { _remoteHandleCallback: callback } }); + const source = '(function(){if(!window.dwr)return;var s0=[],s1={};s1.post={title:"Entry"};s0[0]=s1;dwr.engine._remoteHandleCallback("493053","0",s0);})();'; + + expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback')).toEqual([{ post: { title: 'Entry' } }]); + expect(callback).not.toHaveBeenCalled(); + }); + + it('supports empty public DWR responses and explicit argument positions', () => { + const source = '//#DWR-INSERT\n//#DWR-REPLY\ndwr.engine._remoteHandleCallback("493053","0",[]);'; + expect(parseScriptCallback(source, 'window.dwr.engine._remoteHandleCallback')).toEqual([]); + expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback', 0)).toBe('493053'); + }); + + it('evaluates all callback arguments in order and does not keep an earlier successful capture after an invalid one', () => { + const source = 'var a={value:1};dwr.engine._remoteHandleCallback(a.value=2,"0",a);'; + expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback')).toEqual({ value: 2 }); + expect(() => parseScriptCallback(`${source}dwr.engine._remoteHandleCallback("0","0",unknown());`, 'dwr.engine._remoteHandleCallback')).toThrow(); + }); + + it('rejects missing callbacks and external calls used as the data argument', () => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + expect(() => parseScriptCallback('unrelated();', 'dwr.engine._remoteHandleCallback')).toThrow('callback was not found'); + expect(() => parseScriptCallback('dwr.engine._remoteHandleCallback("0","0",fetch("https://example.invalid/"));', 'dwr.engine._remoteHandleCallback')).toThrow(); + expect(() => parseScriptCallback('', 'callback', -1)).toThrow('non-negative integer'); + expect(() => parseScriptCallback('dwr["engine._remoteHandleCallback"]("0","0",[]);', 'dwr.engine._remoteHandleCallback')).toThrow('callback was not found'); + expect(fetchSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/utils/parse-script-data.ts b/lib/utils/parse-script-data.ts new file mode 100644 index 000000000000..883b74630a8f --- /dev/null +++ b/lib/utils/parse-script-data.ts @@ -0,0 +1,390 @@ +import { type AnyNode, parse } from 'acorn'; + +type DataValue = string | number | boolean | null | undefined | DataValue[] | DataObject | ScriptDataError; +type DataObject = { [key: string]: DataValue }; +type Scope = { values: DataObject; parent?: Scope }; +type Reference = { object: DataObject | DataValue[]; key: string }; + +const globalNames = new Set(['window', 'globalThis', 'self']); +const unsafeKeys = new Set(['__proto__', 'prototype', 'constructor']); +const maxScriptLength = 2_000_000; +const maxSteps = 1e5; +const maxDepth = 100; + +class ScriptDataError extends Error {} +class UnsafePropertyError extends ScriptDataError {} + +const propertyKey = (value: unknown): string => { + if (typeof value !== 'string' && typeof value !== 'number') { + throw new ScriptDataError('Script data property keys must be strings or numbers'); + } + const key = String(value); + if (unsafeKeys.has(key)) { + throw new UnsafePropertyError(`Unsafe script data property: ${key}`); + } + return key; +}; + +const staticPath = (node: AnyNode): string[] => { + if (node.type === 'Identifier') { + return [propertyKey(node.name)]; + } + if (node.type === 'MemberExpression' && !node.optional) { + const key = !node.computed && node.property.type === 'Identifier' ? node.property.name : node.property.type === 'Literal' ? node.property.value : undefined; + return [...staticPath(node.object), propertyKey(key)]; + } + throw new ScriptDataError('Script data targets must be static property paths'); +}; + +const normalizePath = (path: string[]) => (globalNames.has(path[0]) ? path.slice(1) : path); + +const targetPath = (target: string): string[] => { + const program = parse(target, { ecmaVersion: 'latest' }); + if (program.body.length !== 1 || program.body[0].type !== 'ExpressionStatement') { + throw new ScriptDataError('Script data targets must be a single static property path'); + } + const path = normalizePath(staticPath(program.body[0].expression)); + if (!path.length) { + throw new ScriptDataError('Script data targets must name a property'); + } + return path; +}; + +const isObject = (value: DataValue): value is DataObject | DataValue[] => typeof value === 'object' && value !== null && !(value instanceof ScriptDataError); + +const readProperty = (object: DataValue, key: string): DataValue => { + if (object instanceof ScriptDataError) { + throw object; + } + if (!isObject(object)) { + throw new ScriptDataError('Cannot read a property of non-object script data'); + } + return Object.hasOwn(object, key) ? (object as DataObject)[key] : undefined; +}; + +/** Reads serialized data with a limited AST vocabulary; it never executes JavaScript. */ +class ScriptDataReader { + private root: Scope = { values: Object.create(null) }; + private steps = 0; + private poisonedAssignments = new WeakSet(); + private captured = false; + private callbackValue: DataValue; + + constructor( + private target: string[], + private argumentIndex?: number + ) {} + + private step(depth: number) { + if (++this.steps > maxSteps || depth > maxDepth) { + throw new UnsafePropertyError('Script data exceeds the parsing complexity limit'); + } + } + + private identifier(name: string, scope: Scope): DataValue { + propertyKey(name); + for (let current: Scope | undefined = scope; current; current = current.parent) { + if (Object.hasOwn(current.values, name)) { + const value = current.values[name]; + if (value instanceof ScriptDataError) { + throw value; + } + return value; + } + } + if (globalNames.has(name)) { + return this.root.values; + } + if (name === 'undefined') { + return undefined; + } + throw new ScriptDataError(`Unknown script data variable: ${name}`); + } + + private reference(node: AnyNode, scope: Scope, depth: number): Reference { + if (node.type === 'Identifier') { + const key = propertyKey(node.name); + if (globalNames.has(key)) { + throw new ScriptDataError('Cannot replace script data global aliases'); + } + let current = scope; + while (current.parent && !Object.hasOwn(current.values, key)) { + current = current.parent; + } + return { object: current.values, key }; + } + if (node.type !== 'MemberExpression' || node.optional) { + throw new ScriptDataError('Unsupported script data assignment target'); + } + const object = this.evaluate(node.object, scope, depth + 1); + const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : this.evaluate(node.property, scope, depth + 1)); + if (!isObject(object)) { + throw new ScriptDataError('Cannot assign a property of non-object script data'); + } + if (Array.isArray(object) && (!/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= maxSteps)) { + throw new ScriptDataError('Script data arrays require bounded numeric indexes'); + } + return { object, key }; + } + + private evaluate(node: AnyNode, scope: Scope, depth: number): DataValue { + this.step(depth); + switch (node.type) { + case 'Literal': + if (node.regex || node.bigint) { + break; + } + return node.value as string | number | boolean | null; + case 'Identifier': + return this.identifier(node.name, scope); + case 'ArrayExpression': + return node.elements.map((element) => (element ? this.evaluate(element, scope, depth + 1) : undefined)); + case 'ObjectExpression': { + const value: DataObject = Object.create(null); + for (const property of node.properties) { + if (property.type !== 'Property' || property.kind !== 'init' || property.method) { + throw new ScriptDataError('Only ordinary script data object properties are supported'); + } + const key = propertyKey(!property.computed && property.key.type === 'Identifier' ? property.key.name : this.evaluate(property.key, scope, depth + 1)); + value[key] = this.evaluate(property.value, scope, depth + 1); + } + return value; + } + case 'MemberExpression': { + const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : this.evaluate(node.property, scope, depth + 1)); + return readProperty(this.evaluate(node.object, scope, depth + 1), key); + } + case 'UnaryExpression': { + const value = this.evaluate(node.argument, scope, depth + 1); + if (node.operator === 'void') { + return undefined; + } + if (node.operator === '!') { + return !value; + } + if (typeof value === 'number' && (node.operator === '-' || node.operator === '+')) { + return node.operator === '-' ? -value : value; + } + break; + } + case 'LogicalExpression': { + const left = this.evaluate(node.left, scope, depth + 1); + if ((node.operator === '||' && left) || (node.operator === '&&' && !left) || (node.operator === '??' && left !== undefined && left !== null)) { + return left; + } + return this.evaluate(node.right, scope, depth + 1); + } + case 'AssignmentExpression': { + const { object, key } = this.reference(node.left, scope, depth + 1); + try { + if (node.operator !== '=') { + throw new ScriptDataError('Only simple script data assignments are supported'); + } + const value = this.evaluate(node.right, scope, depth + 1); + (object as DataObject)[key] = value; + return value; + } catch (error) { + if (error instanceof ScriptDataError) { + (object as DataObject)[key] = error; + this.poisonedAssignments.add(node); + } + throw error; + } + } + case 'SequenceExpression': { + let value: DataValue; + for (const expression of node.expressions) { + value = this.evaluate(expression, scope, depth + 1); + } + return value; + } + case 'CallExpression': { + if (this.argumentIndex !== undefined && this.matchesCallback(node.callee)) { + this.captured = false; + this.callbackValue = undefined; + const args = node.arguments.map((argument) => this.evaluate(argument, scope, depth + 1)); + if (this.argumentIndex >= args.length) { + throw new ScriptDataError('Script data callback is missing its data argument'); + } + this.callbackValue = args[this.argumentIndex]; + this.captured = true; + return undefined; + } + const fn = node.callee; + if ((fn.type !== 'FunctionExpression' && fn.type !== 'ArrowFunctionExpression') || fn.async || fn.generator) { + break; + } + const args = node.arguments.map((argument) => this.evaluate(argument, scope, depth + 1)); + const local: Scope = { values: Object.create(null), parent: scope }; + for (const [index, parameter] of fn.params.entries()) { + if (parameter.type !== 'Identifier' || globalNames.has(parameter.name)) { + throw new ScriptDataError('Script data IIFEs require simple parameters'); + } + const key = propertyKey(parameter.name); + local.values[key] = args[index]; + } + return fn.body.type === 'BlockStatement' ? this.statements(fn.body.body, local, depth + 1, true)?.value : this.evaluate(fn.body, local, depth + 1); + } + default: + break; + } + throw new ScriptDataError(`Unsupported script data expression: ${node.type}`); + } + + private matchesCallback(node: AnyNode): boolean { + try { + const path = normalizePath(staticPath(node)); + return path.length === this.target.length && path.every((key, index) => key === this.target[index]); + } catch (error) { + if (error instanceof UnsafePropertyError) { + throw error; + } + return false; + } + } + + private isCallbackGuard(node: AnyNode): boolean { + if (this.argumentIndex === undefined || node.type !== 'IfStatement' || node.alternate || node.test.type !== 'UnaryExpression' || node.test.operator !== '!') { + return false; + } + const consequent = node.consequent.type === 'BlockStatement' && node.consequent.body.length === 1 ? node.consequent.body[0] : node.consequent; + if (consequent.type !== 'ReturnStatement' || consequent.argument) { + return false; + } + const path = normalizePath(staticPath(node.test.argument)); + return path.length > 0 && path.length < this.target.length && path.every((key, index) => key === this.target[index]); + } + + private referencesKnownData(node: AnyNode, scope: Scope, depth: number): boolean { + this.step(depth); + if (node.type === 'Identifier' || node.type === 'MemberExpression') { + try { + const path = staticPath(node); + let value = this.identifier(path[0], scope); + for (const key of path.slice(1)) { + if (!isObject(value) || !Object.hasOwn(value, key)) { + return value !== this.root.values; + } + value = readProperty(value, key); + } + return true; + } catch (error) { + if (error instanceof UnsafePropertyError) { + throw error; + } + if (node.type === 'Identifier') { + return false; + } + } + } + return Object.values(node).some((value) => { + const children = Array.isArray(value) ? value : [value]; + return children.some((child) => child && typeof child === 'object' && typeof child.type === 'string' && this.referencesKnownData(child, scope, depth + 1)); + }); + } + + private statements(nodes: AnyNode[], scope: Scope, depth: number, strict: boolean): { value: DataValue } | undefined { + for (const node of nodes) { + this.step(depth); + try { + switch (node.type) { + case 'VariableDeclaration': + for (const declaration of node.declarations) { + if (declaration.id.type !== 'Identifier' || globalNames.has(declaration.id.name)) { + throw new ScriptDataError('Script data declarations require simple variable names'); + } + const key = propertyKey(declaration.id.name); + try { + if (declaration.init || !Object.hasOwn(scope.values, key)) { + scope.values[key] = declaration.init ? this.evaluate(declaration.init, scope, depth + 1) : undefined; + } + } catch (error) { + if (strict || !(error instanceof ScriptDataError) || error instanceof UnsafePropertyError || (declaration.init && this.referencesKnownData(declaration.init, scope, depth + 1))) { + throw error; + } + scope.values[key] = error; + } + } + break; + case 'ExpressionStatement': + this.evaluate(node.expression, scope, depth + 1); + break; + case 'ReturnStatement': + return { value: node.argument ? this.evaluate(node.argument, scope, depth + 1) : undefined }; + case 'EmptyStatement': + break; + default: + if (!this.isCallbackGuard(node)) { + throw new ScriptDataError(`Unsupported script data statement: ${node.type}`); + } + } + } catch (error) { + // Ignore unrelated top-level application code; serializer IIFEs must be entirely supported. + if (strict || !(error instanceof ScriptDataError) || error instanceof UnsafePropertyError) { + throw error; + } + // Failed simple assignments already poison their destination. Other unsupported code + // must not silently leave stale data when it references a known serialized value. + const isPoisonedAssignment = + node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' && this.poisonedAssignments.has(node.expression) && !this.referencesKnownData(node.expression.right, scope, depth + 1); + if (!isPoisonedAssignment && this.referencesKnownData(node, scope, depth + 1)) { + throw error; + } + } + } + return undefined; + } + + private validate(value: DataValue, ancestors = new Set(), depth = 0): void { + this.step(depth); + if (value instanceof ScriptDataError) { + throw value; + } + if (isObject(value)) { + if (ancestors.has(value)) { + throw new ScriptDataError('Cyclic script data is not supported'); + } + ancestors.add(value); + for (const child of Object.values(value)) { + this.validate(child, ancestors, depth + 1); + } + ancestors.delete(value); + } + } + + read(source: string): DataValue { + if (source.length > maxScriptLength) { + throw new ScriptDataError('Script data source exceeds the size limit'); + } + this.statements(parse(source, { ecmaVersion: 'latest' }).body, this.root, 0, false); + let value: DataValue = this.root.values; + if (this.argumentIndex === undefined) { + for (const key of this.target) { + if (!isObject(value) || !Object.hasOwn(value, key)) { + if (value instanceof ScriptDataError) { + throw value; + } + throw new ScriptDataError('Script data target was not found'); + } + value = readProperty(value, key); + } + } else { + if (!this.captured) { + throw new ScriptDataError('Script data callback was not found or could not be parsed'); + } + value = this.callbackValue; + } + this.validate(value); + return value; + } +} + +export const parseScriptData = (source: string, target: string): T => new ScriptDataReader(targetPath(target)).read(source) as T; + +/** Extracts a serialized callback argument without invoking the callback or any external function. */ +export const parseScriptCallback = (source: string, callbackPath: string, argumentIndex = 2): T => { + if (!Number.isSafeInteger(argumentIndex) || argumentIndex < 0) { + throw new ScriptDataError('Script data callback argument index must be a non-negative integer'); + } + return new ScriptDataReader(targetPath(callbackPath), argumentIndex).read(source) as T; +}; diff --git a/lib/utils/playwright.ts b/lib/utils/playwright.ts index f87d00f1f18e..8c6c4d7d8ca5 100644 --- a/lib/utils/playwright.ts +++ b/lib/utils/playwright.ts @@ -98,8 +98,13 @@ const launchBrowser = async (currentProxy?: ProxyState | null) => { } else { browser = await chromium.launch(getLaunchOptions(currentProxy)); } - const context = await browser.newContext(getContextOptions()); - return { browser, context }; + try { + const context = await browser.newContext(getContextOptions()); + return { browser, context }; + } catch (error) { + await browser.close(); + throw error; + } }; const getBrowserlessEndpoint = (endpoint: string, launchOptions: BrowserlessLaunchOptions) => { @@ -109,9 +114,11 @@ const getBrowserlessEndpoint = (endpoint: string, launchOptions: BrowserlessLaun }; const scheduleClose = (browser: Browser, timeout = 30000) => - setTimeout(() => { - void browser.close(); - }, timeout); + timeout === 0 + ? undefined + : setTimeout(() => { + void browser.close(); + }, timeout); /** * @returns Playwright browser context (native `newPage()` shares state across calls) @@ -132,6 +139,7 @@ export const setBrowserBinding = (_binding: any) => {}; export const getPlaywrightPage = async ( url: string, instanceOptions: { + // Set to zero only when the caller always awaits destroy() in finally. closeTimeout?: number; gotoConfig?: GotoOptions; noGoto?: boolean; @@ -161,34 +169,38 @@ export const getPlaywrightPage = async ( await browser.close(); }; - const page = await context.newPage(); + try { + const page = await context.newPage(); - if (hasProxy && currentProxyState) { - logger.debug(`Proxying request in playwright via ${currentProxyState.uri}: ${url}`); - } + if (hasProxy && currentProxyState) { + logger.debug(`Proxying request in playwright via ${currentProxyState.uri}: ${url}`); + } - if (instanceOptions.onBeforeLoad) { - await instanceOptions.onBeforeLoad(page, context); - } + if (instanceOptions.onBeforeLoad) { + await instanceOptions.onBeforeLoad(page, context); + } - if (!instanceOptions.noGoto) { - try { - await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); - } catch (error) { - if (hasProxy && currentProxyState && proxy.multiProxy) { - logger.warn(`Playwright navigation failed with proxy ${currentProxyState.uri}, marking as failed: ${error}`); - proxy.markProxyFailed(currentProxyState.uri); + if (!instanceOptions.noGoto) { + try { + await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); + } catch (error) { + if (hasProxy && currentProxyState && proxy.multiProxy) { + logger.warn(`Playwright navigation failed with proxy ${currentProxyState.uri}, marking as failed: ${error}`); + proxy.markProxyFailed(currentProxyState.uri); + } + throw error; } - await destroy(); - throw error; } - } - return { - context, - destroy, - page, - }; + return { + context, + destroy, + page, + }; + } catch (error) { + await destroy(); + throw error; + } }; export { type Page } from 'patchright'; diff --git a/lib/utils/playwright.worker.ts b/lib/utils/playwright.worker.ts index 9ee89908a33b..a4aa2f885c73 100644 --- a/lib/utils/playwright.worker.ts +++ b/lib/utils/playwright.worker.ts @@ -21,17 +21,21 @@ const getBrowserBinding = () => { const launchBrowser = async () => { const browser = await launch(getBrowserBinding(), { keep_alive: 60000 }); - const context = await browser.newContext({ - ignoreHTTPSErrors: true, - }); - return { browser, context }; + try { + const context = await browser.newContext({ ignoreHTTPSErrors: true }); + return { browser, context }; + } catch (error) { + await browser.close(); + throw error; + } }; -const scheduleClose = (browser: Browser) => { - setTimeout(() => { - void browser.close(); - }, 30000); -}; +const scheduleClose = (browser: Browser, timeout = 30000) => + timeout === 0 + ? undefined + : setTimeout(() => { + void browser.close(); + }, timeout); /** * @returns Playwright browser context (native `newPage()` shares state across calls) @@ -48,6 +52,8 @@ export default async function outPlaywright() { export const getPlaywrightPage = async ( url: string, instanceOptions: { + // Set to zero only when the caller always awaits destroy() in finally. + closeTimeout?: number; gotoConfig?: GotoOptions; noGoto?: boolean; onBeforeLoad?: (page: Page, context?: Awaited>['context']) => Promise | void; @@ -56,29 +62,25 @@ export const getPlaywrightPage = async ( logger.debug(`Launching Cloudflare Browser for: ${url}`); const { browser, context } = await launchBrowser(); - scheduleClose(browser); - const page = await context.newPage(); - - if (instanceOptions.onBeforeLoad) { - await instanceOptions.onBeforeLoad(page, context); - } - - if (!instanceOptions.noGoto) { - try { + const closeTimer = scheduleClose(browser, instanceOptions.closeTimeout); + const destroy = async () => { + clearTimeout(closeTimer); + await browser.close(); + }; + try { + const page = await context.newPage(); + if (instanceOptions.onBeforeLoad) { + await instanceOptions.onBeforeLoad(page, context); + } + if (!instanceOptions.noGoto) { await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); - } catch (error) { - logger.error(`Playwright navigation failed: ${error}`); - throw error; } + return { context, destroy, page }; + } catch (error) { + logger.error(`Playwright page setup failed: ${error}`); + await destroy(); + throw error; } - - return { - context, - destroy: async () => { - await context.close(); - }, - page, - }; }; export { type Page } from '@cloudflare/playwright'; diff --git a/package.json b/package.json index db5b39ef05c4..7d533ac4c094 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.12.0", "@sentry/node": "10.73.0", + "acorn": "8.18.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", @@ -100,7 +101,6 @@ "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", - "jsdom": "30.0.1", "json-bigint": "1.0.0", "jsrsasign": "11.1.5", "lru-cache": "11.5.2", @@ -161,7 +161,6 @@ "@types/fs-extra": "11.0.4", "@types/html-to-text": "9.0.4", "@types/js-beautify": "1.14.3", - "@types/jsdom": "30.0.0", "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18519a721225..401346481d78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,6 +91,9 @@ importers: '@sentry/node': specifier: 10.73.0 version: 10.73.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) + acorn: + specifier: 8.18.0 + version: 8.18.0 cheerio: specifier: 1.2.0 version: 1.2.0 @@ -157,9 +160,6 @@ importers: ip-regex: specifier: 5.0.0 version: 5.0.0 - jsdom: - specifier: 30.0.1 - version: 30.0.1(@noble/hashes@2.3.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -335,9 +335,6 @@ importers: '@types/js-beautify': specifier: 1.14.3 version: 1.14.3 - '@types/jsdom': - specifier: 30.0.0 - version: 30.0.0 '@types/json-bigint': specifier: 1.0.4 version: 1.0.4 @@ -2812,9 +2809,6 @@ packages: '@types/js-beautify@1.14.3': resolution: {integrity: sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==} - '@types/jsdom@30.0.0': - resolution: {integrity: sha512-uAHGxujGE0cDaKGdK28zgDotFtNA7MKq5DXl8LrfdxdCI8VHcg15oJz+amHTChPNI5JpgEPQWc2xFdrw3em/nQ==} - '@types/json-bigint@1.0.4': resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} @@ -3741,7 +3735,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, integrity: sha512-OG2C++3YYIzflFjA9irnexBs9eUS6KUOViAssV4oSo4W8HAl86TV+DoFYhdSXVaFd4z5htsM3zzs64WJybH4cQ==, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.54: @@ -6049,9 +6043,6 @@ packages: unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - undici-types@8.10.0: - resolution: {integrity: sha512-ibvdovq3nCFs8Msrd95BW+zUOq+aOVbT+wpHUoPWhztbHEoPc6oof51iFDB6Es8lTKvNvVW9jNSAB8dwrKTMGg==} - undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} @@ -6549,6 +6540,7 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.2 + optional: true '@asamuzakjp/dom-selector@8.3.0': dependencies: @@ -6556,6 +6548,7 @@ snapshots: css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 + optional: true '@asteasolutions/zod-to-openapi@9.1.0(zod@4.5.4)': dependencies: @@ -6624,6 +6617,7 @@ snapshots: '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 + optional: true '@bufbuild/protobuf@2.14.0': {} @@ -6698,12 +6692,14 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@6.1.0': {} + '@csstools/color-helpers@6.1.0': + optional: true '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: @@ -6711,16 +6707,20 @@ snapshots: '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-tokenizer': 4.0.0 + optional: true '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 + optional: true - '@csstools/css-tokenizer@4.0.0': {} + '@csstools/css-tokenizer@4.0.0': + optional: true '@dabh/diagnostics@2.0.8': dependencies: @@ -7041,6 +7041,7 @@ snapshots: '@exodus/bytes@1.15.1(@noble/hashes@2.3.0)': optionalDependencies: '@noble/hashes': 2.3.0 + optional: true '@googleapis/youtube@35.0.0': dependencies: @@ -8210,13 +8211,6 @@ snapshots: '@types/js-beautify@1.14.3': {} - '@types/jsdom@30.0.0': - dependencies: - '@types/node': 26.4.0 - '@types/tough-cookie': 4.0.5 - parse5: 8.0.1 - undici-types: 8.10.0 - '@types/json-bigint@1.0.4': {} '@types/json-schema@7.0.15': {} @@ -8688,6 +8682,7 @@ snapshots: bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 + optional: true big-integer@1.6.52: {} @@ -8916,6 +8911,7 @@ snapshots: dependencies: mdn-data: 2.27.1 source-map-js: 1.2.1 + optional: true css-what@6.2.2: {} @@ -8940,6 +8936,7 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' + optional: true dayjs@1.11.23: {} @@ -8960,7 +8957,8 @@ snapshots: decamelize@6.0.1: {} - decimal.js@10.6.0: {} + decimal.js@10.6.0: + optional: true decode-named-character-reference@1.3.0: dependencies: @@ -9748,6 +9746,7 @@ snapshots: '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' + optional: true html-escaper@2.0.2: {} @@ -9963,7 +9962,8 @@ snapshots: is-plain-object@5.0.0: {} - is-potential-custom-element-name@1.0.1: {} + is-potential-custom-element-name@1.0.1: + optional: true is-promise@2.2.2: {} @@ -10062,6 +10062,7 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' + optional: true jsesc@3.1.0: {} @@ -10388,7 +10389,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdn-data@2.27.1: {} + mdn-data@2.27.1: + optional: true mdn-data@2.29.0: {} @@ -11002,6 +11004,7 @@ snapshots: parse5@8.0.1: dependencies: entities: 8.0.0 + optional: true parseley@0.13.1: dependencies: @@ -11276,7 +11279,8 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: {} + require-from-string@2.0.2: + optional: true require-in-the-middle@8.0.1: dependencies: @@ -11402,6 +11406,7 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 + optional: true scslre@0.3.0: dependencies: @@ -11596,7 +11601,8 @@ snapshots: dependencies: has-flag: 4.0.0 - symbol-tree@3.2.4: {} + symbol-tree@3.2.4: + optional: true system-architecture@0.1.0: {} @@ -11692,6 +11698,7 @@ snapshots: tr46@6.0.0: dependencies: punycode: 2.3.1 + optional: true tree-kill@1.2.2: {} @@ -11812,8 +11819,6 @@ snapshots: '@quansync/fs': 1.0.0 quansync: 1.0.0 - undici-types@8.10.0: {} - undici-types@8.3.0: {} undici@6.28.0: {} @@ -12006,6 +12011,7 @@ snapshots: w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 + optional: true web-streams-polyfill@3.3.3: {} @@ -12013,7 +12019,8 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@8.0.1: {} + webidl-conversions@8.0.1: + optional: true whatwg-encoding@3.1.1: dependencies: @@ -12021,7 +12028,8 @@ snapshots: whatwg-mimetype@4.0.0: {} - whatwg-mimetype@5.0.0: {} + whatwg-mimetype@5.0.0: + optional: true whatwg-url@16.0.1(@noble/hashes@2.3.0): dependencies: @@ -12030,6 +12038,7 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' + optional: true whatwg-url@17.1.0(@noble/hashes@2.3.0): dependencies: @@ -12038,6 +12047,7 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' + optional: true whatwg-url@5.0.0: dependencies: @@ -12171,7 +12181,8 @@ snapshots: dependencies: lodash: 4.18.1 - xml-name-validator@5.0.0: {} + xml-name-validator@5.0.0: + optional: true xml2js@0.6.2: dependencies: @@ -12180,7 +12191,8 @@ snapshots: xmlbuilder@11.0.1: {} - xmlchars@2.2.0: {} + xmlchars@2.2.0: + optional: true xxhash-wasm@1.1.0: {} diff --git a/tests/lofter-tag.test.ts b/tests/lofter-tag.test.ts new file mode 100644 index 000000000000..a3308d1c1fa7 --- /dev/null +++ b/tests/lofter-tag.test.ts @@ -0,0 +1,63 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/lofter/tag'; +import got from '../lib/utils/got'; + +vi.mock('../lib/config', () => ({ config: { lofter: { cookies: 'test-cookie' } } })); +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +const context = { req: { param: (name: string) => ({ name: 'Photography', type: 'date' })[name] } }; + +describe('Lofter tag DWR response', () => { + it('reads DWR object references and preserves feed content without executing the response', async () => { + // A representative DWR protocol response; no private feed data is included. + vi.mocked(got).mockResolvedValue({ + data: `//#DWR-INSERT +//#DWR-REPLY +var s0=[],s1={},s2={},s3={}; +s0[0]=s1;s1.post=s2;s2.blogInfo=s3; +s3.blogNickName="Photographer"; +s2.blogPageUrl="https://example.lofter.com/post/1"; +s2.title=""; +s2.digest="

    A & B

    "; +s2.publishTime=1704067200000; +s2.tagList=["Photography"]; +s2.photoLinks='[{"orign":"https://example.com/photo.jpg"}]'; +s2.embed='{"h256Url":"https://example.com/video.mp4","video_img_url":"https://example.com/poster.jpg"}'; +dwr.engine._remoteHandleCallback('493053','0',s0);`, + }); + + const feed = await (route.handler as (ctx: typeof context) => Promise)(context); + + expect(feed.title).toBe('Photography - 日榜 | LOFTER'); + expect(feed.item).toEqual([ + { + author: 'Photographer', + link: 'https://example.lofter.com/post/1', + title: 'Photographer:A & B', + pubDate: new Date('2024-01-01T00:00:00.000Z'), + description: + '

    A & B

    ', + category: ['Photography'], + }, + ]); + expect(got).toHaveBeenCalledWith(expect.objectContaining({ headers: { Referer: 'https://www.lofter.com/tag/Photography', Cookie: 'test-cookie' } })); + }); + + it('reads the empty callback response returned by the public endpoint', async () => { + vi.mocked(got).mockResolvedValue({ data: "//#DWR-INSERT\n//#DWR-REPLY\ndwr.engine._remoteHandleCallback('493053','0',[]);" }); + + const feed = await (route.handler as (ctx: typeof context) => Promise)(context); + expect(feed.item).toEqual([]); + }); + + it('rejects executable callback arguments', async () => { + vi.mocked(got).mockResolvedValue({ data: "dwr.engine._remoteHandleCallback('493053','0',fetch('https://example.com'));" }); + + await expect((route.handler as (ctx: typeof context) => Promise)(context)).rejects.toThrow(); + }); +}); diff --git a/tests/nuxt-routes.test.ts b/tests/nuxt-routes.test.ts new file mode 100644 index 000000000000..676e1612d912 --- /dev/null +++ b/tests/nuxt-routes.test.ts @@ -0,0 +1,92 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { processFeed, processFeedType2 } from '../lib/routes/dongqiudi/utils'; +import nintendo from '../lib/routes/nintendo/utils'; +import cache from '../lib/utils/cache'; +import got from '../lib/utils/got'; + +vi.mock('../lib/utils/cache', () => ({ + default: { tryGet: vi.fn((_key, getValue) => getValue()) }, +})); +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe('Nintendo Nuxt data', () => { + it('reads serialized object references and escaped content without executing page scripts', () => { + const html = ` + `; + + expect(nintendo.nuxtReader(html)).toEqual({ + newsList: [{ title: 'News 中文', content: '

    A&B

    ' }], + newsData: { title: 'News 中文', content: '

    A&B

    ' }, + }); + }); + + it('preserves cached article content, category, and publication time', async () => { + const link = 'https://www.nintendoswitch.com.cn/topics/test'; + vi.mocked(got).mockResolvedValue({ + data: '', + }); + + const items = await nintendo.ProcessNewsChina([{ link, description: '' }], cache); + + expect(cache.tryGet).toHaveBeenCalledWith(link, expect.any(Function)); + expect(items[0]).toEqual({ + link, + description: '

    Article

    ', + category: ['News'], + pubDate: new Date('2024-01-01T00:00:00.000Z'), + }); + }); + + it.each(['No page data', ''])('rejects missing or executable page data', (html) => { + expect(() => nintendo.nuxtReader(html)).toThrow('Nuxt 框架信息提取失败'); + }); +}); + +describe('Dongqiudi Nuxt data', () => { + const articleHtml = ` + `; + + it('preserves article content cleanup and the author', () => { + const item: { title: string; description?: string; author?: string } = { title: 'Match' }; + + processFeedType2(item, articleHtml); + + expect(item).toEqual({ + title: 'Match', + description: '

    Match report

    Related', + author: 'Reporter', + }); + }); + + it.each(['No Nuxt data', '', ''])('leaves an item unchanged when article data is absent', (html) => { + const item = { title: 'Match' }; + processFeedType2(item, html); + expect(item).toEqual({ title: 'Match' }); + }); + + it.each([ + ['team', 'teamInfo:{name:"Club",logo:"club.png"}', 'Club', 'club.png'], + ['player', 'detail:{base_info:{person_name:"Player",person_logo:"player.png"}}', 'Player', 'player.png'], + ])('keeps the %s metadata, API request, and article cache', async (type, metadata, name, image) => { + vi.mocked(got) + .mockResolvedValueOnce({ data: `` }) + .mockResolvedValueOnce({ data: { data: { articles: [{ id: 42, title: 'Match', category: 'Football', show_time: 1_704_067_200 }] } } }) + .mockResolvedValueOnce({ data: articleHtml }); + + const feed = await processFeed(type, '123'); + + expect(feed.title).toBe(`${name} - 相关新闻`); + expect(feed.image).toBe(image); + expect(feed.item).toHaveLength(1); + expect(feed.item[0]).toMatchObject({ author: 'Reporter', category: ['Football'], pubDate: new Date('2024-01-01T00:00:00.000Z') }); + expect(cache.tryGet).toHaveBeenCalledWith('https://www.dongqiudi.com/articles/42.html', expect.any(Function)); + expect(got).toHaveBeenNthCalledWith(2, 'https://api.dongqiudi.com/v3/archive/app/channel/feeds', { + searchParams: { id: '123', type, size: 20, platform: 'web', version: '' }, + }); + }); +}); diff --git a/tests/page-script-data-routes.test.ts b/tests/page-script-data-routes.test.ts new file mode 100644 index 000000000000..5fe5a9d9ebc5 --- /dev/null +++ b/tests/page-script-data-routes.test.ts @@ -0,0 +1,208 @@ +import type { Context } from 'hono'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route as hotukdealsRoute } from '../lib/routes/hotukdeals/hottest'; +import { getNSFWNovelContent } from '../lib/routes/pixiv/novel-api/content/nsfw'; +import { parseNovelContent } from '../lib/routes/pixiv/novel-api/content/utils'; +import playCache from '../lib/routes/qq/kg/cache'; +import { route as qqRoute } from '../lib/routes/qq/kg/user'; +import { route as xueqiuRoute } from '../lib/routes/xueqiu/column'; +import type { Data, Route } from '../lib/types'; + +const mocks = vi.hoisted(() => ({ + request: Object.assign(vi.fn(), { get: vi.fn() }), + pixivRequest: vi.fn(), + tryGet: vi.fn(), +})); + +vi.mock('../lib/utils/got', () => ({ default: mocks.request })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/routes/pixiv/pixiv-got', () => ({ default: mocks.pixivRequest })); +vi.mock('../lib/routes/pixiv/novel-api/content/utils', () => ({ parseNovelContent: vi.fn() })); + +const invoke = async (route: Route, params: Record = {}) => (await route.handler({ req: { param: (name: string) => params[name] } } as unknown as Context)) as Data; +const page = (source: string) => ``; + +// QQ and Xueqiu use the JSON-assignment format observed in public responses on +// 2026-09-06. Keep only the consumed fields and replace public account data. +const playDetail = { + song_name: 'Song & title', + content: 'Line one
    Line two & three', + nick: 'Singer', + cover: 'https://example.com/cover.jpg', + playurl: 'https://example.com/song.m4a', + ksong_mid: 'song-1', + ctime: 1_700_000_000, + comments: [{ nick: 'Listener', content: 'Nice song', ctime: 1_700_000_001, comment_id: 'comment-1' }], +}; + +// Pixiv requires authentication and HotUKDeals returned 403 during inspection. +// Their fixtures cover the existing consumed-data contract, not live success. +const novelDetail = { + id: '42', + title: 'Novel title', + caption: '

    Caption & summary

    ', + text: 'A line\n[uploadedimage:7]', + userId: 'author-1', + rating: { bookmark: 3, view: 12, like: 2 }, + cdate: '2026-09-01T10:00:00+09:00', + isOriginal: true, + aiType: 0, + tags: ['fiction'], + coverUrl: 'https://example.com/novel.jpg', + images: { '7': { urls: { original: 'https://example.com/image.jpg' } }, '8': {} }, + seriesId: 'series-1', + seriesTitle: 'Series title', +}; + +beforeEach(() => { + vi.resetAllMocks(); + const entries = new Map(); + mocks.tryGet.mockImplementation(async (key, getValue) => { + if (!entries.has(key)) { + entries.set(key, await getValue()); + } + return entries.get(key); + }); + mocks.request.mockRejectedValue(new Error('Unexpected upstream request')); + mocks.request.get.mockRejectedValue(new Error('Unexpected upstream request')); + mocks.pixivRequest.mockRejectedValue(new Error('Unexpected Pixiv request')); + vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('External network access is disabled'))); +}); + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe('routes that parse embedded page data', () => { + it('preserves QQ user feed metadata, audio attachments and play-detail cache keys', async () => { + const userData = { + data: { + nickname: 'Singer', + head_img_url: 'https://example.com/avatar.jpg', + ugclist: [{ shareid: 'share-1', ksong_mid: 'song-1', title: 'Song & title', ctime: 1_600_000_000, avatar: 'https://example.com/fallback.jpg' }], + }, + share: { content: 'Singer profile' }, + }; + mocks.request.mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify(userData)};`) }).mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify({ detail: playDetail })};`) }); + + const result = await invoke(qqRoute, { userId: 'singer-1' }); + + expect(result).toMatchObject({ + title: 'Singer - 全民K歌', + link: 'https://node.kg.qq.com/personal?uid=singer-1', + description: 'Singer profile', + image: 'https://example.com/avatar.jpg', + allowEmpty: true, + itunes_author: 'Singer', + itunes_category: '全民K歌', + item: [ + { + title: 'Song & title', + description: playDetail.content, + link: 'https://node.kg.qq.com/play?s=share-1', + guid: 'ksong:song-1', + author: 'Singer', + pubDate: new Date(1_700_000_000_000), + itunes_item_image: playDetail.cover, + enclosure_url: playDetail.playurl, + enclosure_type: 'audio/x-m4a', + }, + ], + }); + expect(mocks.tryGet).toHaveBeenCalledWith('ksong:song-1', expect.any(Function)); + expect(mocks.request.mock.calls.map(([url]) => url)).toEqual(['https://node.kg.qq.com/personal?uid=singer-1', 'https://node.kg.qq.com/play?s=share-1']); + }); + + it('preserves QQ comments and reuses a play-detail cache entry', async () => { + mocks.request.mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify({ detail: playDetail })};`) }); + + const first = await playCache.getPlayInfo(undefined, 'share-1', 'song-1'); + const second = await playCache.getPlayInfo(undefined, 'share-1', 'song-1'); + + expect(first).toMatchObject({ name: playDetail.song_name, description: playDetail.content, author: 'Singer', ksong_mid: 'song-1', comments: playDetail.comments }); + expect(second).toBe(first); + expect(mocks.request).toHaveBeenCalledExactlyOnceWith('https://node.kg.qq.com/play?s=share-1'); + }); + + it('preserves the Xueqiu cookie jar, first-page request and author data', async () => { + mocks.request + .mockResolvedValueOnce({ data: '' }) + .mockResolvedValueOnce({ data: page('window.SNOWMAN_TARGET = {"screen_name":"Author & name","description":"A profile & text"};') }) + .mockResolvedValueOnce({ data: { list: [{ title: 'Article', description: '

    Body

    ', created_at: 1_700_000_000_000, target: '/author/123' }] } }); + + const result = await invoke(xueqiuRoute, { id: 'author-1' }); + + expect(result).toEqual({ + title: 'Author & name - 雪球', + link: 'https://xueqiu.com/author-1/column', + description: 'A profile & text', + item: [{ title: 'Article', description: '

    Body

    ', pubDate: new Date(1_700_000_000_000), link: 'https://xueqiu.com/author/123', author: 'Author & name' }], + }); + const cookieJar = mocks.request.mock.calls[0][1].cookieJar; + expect(mocks.request).toHaveBeenNthCalledWith(2, 'https://xueqiu.com/author-1/column', { cookieJar }); + expect(mocks.request).toHaveBeenNthCalledWith(3, 'https://xueqiu.com/statuses/original/timeline.json', { cookieJar, searchParams: { user_id: 'author-1', page: 1 } }); + }); + + it('preserves HotUKDeals titles, image URLs, prices and temperatures', async () => { + const state = { widgets: { hottestWidget: { threads: [{ title: 'A & B deal', mainImage: { path: 'images', name: 'deal' }, temperature: 123, displayPrice: '£12.99', url: 'https://example.com/deal' }] } } }; + mocks.request.get.mockResolvedValueOnce({ data: page(`window.__INITIAL_STATE__ = ${JSON.stringify(state)};`) }); + + expect(await invoke(hotukdealsRoute)).toEqual({ + title: 'hotukdeals hottest', + link: 'https://www.hotukdeals.com/', + item: [{ title: 'A & B deal', description: '
    123° A & B deal
    £12.99', link: 'https://example.com/deal' }], + }); + expect(mocks.request.get).toHaveBeenCalledExactlyOnceWith('https://www.hotukdeals.com/'); + }); + + it.each(['parent object', 'nested assignment'])('preserves authenticated Pixiv novel data from a %s', async (form) => { + const source = form === 'parent object' ? `window.pixiv = {novel: ${JSON.stringify(novelDetail)}};` : `window.pixiv = {};'); + mocks.content.mockResolvedValue('
    Thread content
    '); + mocks.getPlaywrightPage.mockResolvedValue({ + page: { goto: mocks.goto, route: mocks.route, waitForSelector: mocks.waitForSelector, content: mocks.content }, + context: { addCookies: mocks.addCookies }, + destroy: mocks.destroy, + }); +}); + +describe('Yamibo browser fallback', () => { + it('keeps ordinary HTML requests free of browser work', async () => { + mocks.ofetch.mockResolvedValue('
    Ordinary HTML
    '); + const result = await fetchThread('123', { ordertype: '1' }); + expect(result).toEqual({ link: 'https://bbs.yamibo.com/forum.php?mod=viewthread&tid=123&ordertype=1', data: '
    Ordinary HTML
    ' }); + expect(mocks.ofetch).toHaveBeenCalledWith(result.link, { headers: { cookie: 'EeqY_2132_saltkey=test-salt; EeqY_2132_auth=test-auth' } }); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it('reuses one browser and serializes navigation for concurrent article requests', async () => { + const fetcher = new ThreadFetcher(); + let active = 0; + let maximum = 0; + mocks.goto.mockImplementation(async () => { + active++; + maximum = Math.max(maximum, active); + await Promise.resolve(); + active--; + }); + try { + const results = await Promise.all([fetcher.fetchThread('123'), fetcher.fetchThread('456')]); + expect(results.map((result) => result.data)).toEqual(Array.from({ length: 2 }, () => '
    Thread content
    ')); + expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); + expect(mocks.goto).toHaveBeenCalledTimes(2); + expect(maximum).toBe(1); + expect(mocks.addCookies).toHaveBeenCalledWith([ + { name: 'EeqY_2132_saltkey', value: 'test-salt', url: 'https://bbs.yamibo.com' }, + { name: 'EeqY_2132_auth', value: 'test-auth', url: 'https://bbs.yamibo.com' }, + ]); + } finally { + await fetcher.close(); + } + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('closes the browser when the expected thread content never appears', async () => { + mocks.waitForSelector.mockRejectedValue(new Error('Page timeout')); + await expect(fetchThread('123')).rejects.toThrow('browser access did not return thread content'); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); +}); diff --git a/tests/zhihu-browser.test.ts b/tests/zhihu-browser.test.ts new file mode 100644 index 000000000000..f00e51bc754e --- /dev/null +++ b/tests/zhihu-browser.test.ts @@ -0,0 +1,91 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + cookies: '', + getPlaywrightPage: vi.fn(), + tryGet: vi.fn(), + destroy: vi.fn(), + addCookies: vi.fn(), + browserCookies: vi.fn(), + goto: vi.fn(), + route: vi.fn(), + waitForFunction: vi.fn(), + evaluate: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ + config: { + zhihu: { + get cookies() { + return mocks.cookies; + }, + }, + cache: { contentExpire: 600 }, + }, +})); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); + +beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + mocks.cookies = ''; + mocks.tryGet.mockImplementation((_key, create) => create()); + mocks.waitForFunction.mockResolvedValue(undefined); + mocks.browserCookies.mockResolvedValue([ + { name: 'd_c0', value: 'browser-dc0' }, + { name: '__zse_ck', value: 'browser-zse' }, + ]); + mocks.evaluate.mockResolvedValue('Browser user agent'); + mocks.getPlaywrightPage.mockResolvedValue({ + page: { goto: mocks.goto, route: mocks.route, waitForFunction: mocks.waitForFunction, evaluate: mocks.evaluate }, + context: { addCookies: mocks.addCookies, cookies: mocks.browserCookies }, + destroy: mocks.destroy, + }); +}); + +describe('Zhihu browser credentials', () => { + it('uses configured credentials without starting a browser', async () => { + mocks.cookies = 'd_c0=configured; __zse_ck=existing; z_c0=login'; + const { getSignedHeader } = await import('../lib/routes/zhihu/utils'); + const headers = await getSignedHeader('https://www.zhihu.com/people/example', '/api/v4/members/example'); + expect(headers.cookie).toBe(mocks.cookies); + expect(headers['x-zse-96']).toMatch(/^2\.0_/); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + expect(mocks.tryGet).not.toHaveBeenCalled(); + }); + + it('keeps browser cookies and their native user agent together', async () => { + mocks.cookies = 'z_c0=isolated-login'; + const { getSignedHeader } = await import('../lib/routes/zhihu/utils'); + const headers = await getSignedHeader('https://www.zhihu.com/people/example', '/api/v4/members/example'); + expect(headers.cookie).toBe('__zse_ck=browser-zse; d_c0=browser-dc0'); + expect(headers['user-agent']).toBe('Browser user agent'); + expect(mocks.addCookies).not.toHaveBeenCalled(); + expect(mocks.goto).toHaveBeenCalledWith('https://www.zhihu.com/people/example', { waitUntil: 'domcontentloaded' }); + expect(mocks.browserCookies).toHaveBeenCalledWith('https://www.zhihu.com/'); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('transfers the configured session without truncating cookie values', async () => { + const { getBrowserCredentials } = await import('../lib/routes/zhihu/browser'); + await getBrowserCredentials('https://www.zhihu.com/people/example', 'd_c0=seed==;z_c0=login=value'); + expect(mocks.addCookies).toHaveBeenCalledWith([ + { name: 'd_c0', value: 'seed==', domain: '.zhihu.com', path: '/' }, + { name: 'z_c0', value: 'login=value', domain: '.zhihu.com', path: '/' }, + ]); + const handler = mocks.route.mock.calls[0][1]; + const continueRequest = vi.fn(); + const abortRequest = vi.fn(); + await handler({ request: () => ({ resourceType: () => 'image' }), continue: continueRequest, abort: abortRequest }); + expect(abortRequest).toHaveBeenCalledOnce(); + expect(continueRequest).not.toHaveBeenCalled(); + }); + + it('closes the browser and gives an actionable error when cookies are unavailable', async () => { + mocks.waitForFunction.mockRejectedValue(new Error('Page timeout')); + const { getBrowserCredentials } = await import('../lib/routes/zhihu/browser'); + await expect(getBrowserCredentials('https://www.zhihu.com/people/example', '')).rejects.toThrow('Configure ZHIHU_COOKIES'); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); +}); diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index 3cf12c82174f..a4578e0de036 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -69,6 +69,6 @@ export default defineConfig({ }, }, test: { - include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts'], + include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts'], }, }); From e18b1d34633f79cfa3998de8b967a3bea0becc38 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 6 Sep 2026 23:29:11 +0800 Subject: [PATCH 0716/1190] fix(zhihu): keep API requests in initialized browser sessions --- lib/routes/zhihu/activities.ts | 229 +++++++++++------------ lib/routes/zhihu/answers.ts | 51 +++--- lib/routes/zhihu/browser.ts | 87 +++++++-- lib/routes/zhihu/posts.ts | 89 ++++----- lib/routes/zhihu/question.ts | 56 +++--- lib/routes/zhihu/sign.ts | 10 + lib/routes/zhihu/topic.ts | 128 ++++++------- lib/routes/zhihu/utils.ts | 112 ++++-------- lib/routes/zhihu/zhuanlan.ts | 154 +++++++--------- tests/zhihu-browser.test.ts | 325 +++++++++++++++++++++++++++++---- 10 files changed, 730 insertions(+), 511 deletions(-) create mode 100644 lib/routes/zhihu/sign.ts diff --git a/lib/routes/zhihu/activities.ts b/lib/routes/zhihu/activities.ts index ca654ad09b20..c7e17cd63cef 100644 --- a/lib/routes/zhihu/activities.ts +++ b/lib/routes/zhihu/activities.ts @@ -2,10 +2,9 @@ import sanitizeHtml from 'sanitize-html'; import type { Route } from '@/types'; import { ViewType } from '@/types'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/people/activities/:id', @@ -37,133 +36,125 @@ export const route: Route = { handler, }; -async function handler(ctx) { +function handler(ctx) { const id = ctx.req.param('id'); // second: get real data from zhihu const apiPath = `/api/v3/moments/${id}/activities?limit=5&desktop=true&ws_qiangzhisafe=0`; - const signedHeader = await getSignedHeader(`https://www.zhihu.com/people/${id}`, apiPath); - - const response = await got(`https://www.zhihu.com${apiPath}`, { - headers: { - ...header, - ...signedHeader, - Referer: `https://www.zhihu.com/people/${id}`, - // Authorization: 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20', // hard-coded in js - }, - }); - - const data = response.data.data; - - return { - title: `${data[0].actor.name}的知乎动态`, - link: `https://www.zhihu.com/people/${id}/activities`, - image: data[0].actor.avatar_url, - description: data[0].actor.headline || data[0].actor.description, - item: data.map((item) => { - const detail = item.target; - let title; - let description; - let url; - const images: string[] = []; - let text = ''; - let link = ''; - let author = ''; - - switch (item.target.type) { - case 'answer': - title = detail.question.title; - author = detail.author.name; - description = processImage(detail.content); - url = `https://www.zhihu.com/question/${detail.question.id}/answer/${detail.id}`; - break; - case 'article': - title = detail.title; - author = detail.author.name; - description = processImage(detail.content); - url = `https://zhuanlan.zhihu.com/p/${detail.id}`; - break; - case 'pin': - title = sanitizeHtml(detail.excerpt_title); - author = detail.author.name; - for (const contentItem of detail.content) { - switch (contentItem.type) { - case 'text': - text = `

    ${contentItem.own_text}

    `; - - break; - - case 'image': - images.push(`

    `); - - break; - - case 'link': - link = `

    ${contentItem.title}

    `; - - break; - - case 'video': - link = `

    ${contentItem.own_text}

    `; + + break; + + case 'image': + images.push(`

    `); + + break; + + case 'link': + link = `

    ${contentItem.title}

    `; + + break; + + case 'video': + link = `

    `; - break; - case 'link_card': - link = `

    `; - break; + break; + case 'link_card': + link = `

    `; + break; - default: - throw new Error(`Unknown type: ${contentItem.type}`); + default: + throw new Error(`Unknown type: ${contentItem.type}`); + } } - } - description = `${text}${link}${images.join('')}`; - url = `https://www.zhihu.com/pin/${detail.id}`; - break; - case 'question': - title = detail.title; - author = detail.author.name; - description = processImage(detail.detail); - url = `https://www.zhihu.com/question/${detail.id}`; - break; - case 'collection': - title = detail.title; - url = `https://www.zhihu.com/collection/${detail.id}`; - break; - case 'column': - title = detail.title; - description = `

    ${detail.intro}

    `; - url = `https://zhuanlan.zhihu.com/${detail.id}`; - break; - case 'topic': - title = detail.name; - description = `

    ${detail.introduction}

    话题关注者人数:${detail.followers_count}

    `; - url = `https://www.zhihu.com/topic/${detail.id}`; - break; - case 'live': - title = detail.subject; - description = detail.description.replaceAll(/\n|\r/g, '
    '); - url = `https://www.zhihu.com/lives/${detail.id}`; - break; - case 'roundtable': - title = detail.name; - description = detail.description; - url = `https://www.zhihu.com/roundtable/${detail.id}`; - break; - default: - description = `未知类型 ${item.target.type},请点击链接提交issue`; - } - - return { - title: `${data[0].actor.name}${item.action_text}: ${title}`, - author, - description, - pubDate: parseDate(item.created_time * 1000), - link: url, - }; - }), - }; + description = `${text}${link}${images.join('')}`; + url = `https://www.zhihu.com/pin/${detail.id}`; + break; + case 'question': + title = detail.title; + author = detail.author.name; + description = processImage(detail.detail); + url = `https://www.zhihu.com/question/${detail.id}`; + break; + case 'collection': + title = detail.title; + url = `https://www.zhihu.com/collection/${detail.id}`; + break; + case 'column': + title = detail.title; + description = `

    ${detail.intro}

    `; + url = `https://zhuanlan.zhihu.com/${detail.id}`; + break; + case 'topic': + title = detail.name; + description = `

    ${detail.introduction}

    话题关注者人数:${detail.followers_count}

    `; + url = `https://www.zhihu.com/topic/${detail.id}`; + break; + case 'live': + title = detail.subject; + description = detail.description.replaceAll(/\n|\r/g, '
    '); + url = `https://www.zhihu.com/lives/${detail.id}`; + break; + case 'roundtable': + title = detail.name; + description = detail.description; + url = `https://www.zhihu.com/roundtable/${detail.id}`; + break; + default: + description = `未知类型 ${item.target.type},请点击链接提交issue`; + } + + return { + title: `${data[0].actor.name}${item.action_text}: ${title}`, + author, + description, + pubDate: parseDate(item.created_time * 1000), + link: url, + }; + }), + }; + }); } diff --git a/lib/routes/zhihu/answers.ts b/lib/routes/zhihu/answers.ts index 56fc5094b7e0..e31ebb2c49c5 100644 --- a/lib/routes/zhihu/answers.ts +++ b/lib/routes/zhihu/answers.ts @@ -1,8 +1,7 @@ import type { Route } from '@/types'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/people/answers/:id', @@ -33,42 +32,34 @@ export const route: Route = { handler, }; -async function handler(ctx) { +function handler(ctx) { const id = ctx.req.param('id'); // second: get real data from zhihu const apiPath = `/api/v4/members/${id}/answers?limit=7&include=data[*].is_normal,content`; - const signedHeader = await getSignedHeader(`https://www.zhihu.com/people/${id}`, apiPath); + return withZhihuClient(`https://www.zhihu.com/people/${id}`, async (client) => { + const response = await client.get(apiPath); + const data = response.data; + const items = data.map((item) => { + const title = item.question.title; + const url = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; + const author = item.author.name; + const description = processImage(item.content); - const response = await got(`https://www.zhihu.com${apiPath}`, { - headers: { - ...header, - ...signedHeader, - Referer: `https://www.zhihu.com/people/${id}/activities`, - // Authorization: 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20', // hard-coded in js - }, - }); - - const data = response.data.data; - const items = data.map((item) => { - const title = item.question.title; - const url = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; - const author = item.author.name; - const description = processImage(item.content); + return { + title, + author, + description, + pubDate: parseDate(item.created_time * 1000), + link: url, + }; + }); return { - title, - author, - description, - pubDate: parseDate(item.created_time * 1000), - link: url, + title: `${data[0].author.name}的知乎回答`, + link: `https://www.zhihu.com/people/${id}/answers`, + item: items, }; }); - - return { - title: `${data[0].author.name}的知乎回答`, - link: `https://www.zhihu.com/people/${id}/answers`, - item: items, - }; } diff --git a/lib/routes/zhihu/browser.ts b/lib/routes/zhihu/browser.ts index 7863a0d64f09..6b30a09b3c5f 100644 --- a/lib/routes/zhihu/browser.ts +++ b/lib/routes/zhihu/browser.ts @@ -1,9 +1,22 @@ import { getPlaywrightPage } from '@/utils/playwright'; +import { getSignedHeaders } from './sign'; +import type { ZhihuClient } from './utils'; + +const origin = 'https://www.zhihu.com'; const allowedResources = new Set(['document', 'script', 'xhr', 'fetch']); -export const getBrowserCredentials = async (url: string, configuredCookies: string) => { +const parseApiResponse = (body: string): T => { + try { + return JSON.parse(body) as T; + } catch { + throw new Error('zhihu: browser API request did not return JSON'); + } +}; + +export const createBrowserClient = async (url: string, configuredCookies: string, initialApiPath?: string): Promise }> => { const { page, context, destroy } = await getPlaywrightPage(url, { noGoto: true, closeTimeout: 0 }); + let phase = 'cookie setup'; try { const cookies = configuredCookies .split(';') @@ -17,20 +30,72 @@ export const getBrowserCredentials = async (url: string, configuredCookies: stri await context.addCookies(cookies); } await page.route('**/*', (route) => (allowedResources.has(route.request().resourceType()) ? route.continue() : route.abort())); - await page.goto(url, { waitUntil: 'domcontentloaded' }); - await page.waitForFunction(() => document.cookie.split(';').some((pair) => pair.trimStart().startsWith('__zse_ck=')), undefined, { timeout: 10000 }); - const browserCookies = await context.cookies('https://www.zhihu.com/'); - const dc0 = browserCookies.find((cookie) => cookie.name === 'd_c0')?.value; - const zseCk = browserCookies.find((cookie) => cookie.name === '__zse_ck')?.value; - if (!dc0 || !zseCk) { + if (cookies.every((cookie) => cookie.name !== 'd_c0' || !cookie.value)) { + // Content pages may not issue the guest identity cookie themselves. + phase = 'guest cookie initialization'; + await page.goto(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); + const guestCookies = await context.cookies(origin); + if (guestCookies.every((cookie) => cookie.name !== 'd_c0' || !cookie.value)) { + throw new Error('The browser did not receive a guest d_c0 cookie'); + } + } + + phase = 'page navigation'; + const response = await page.goto(initialApiPath ? `${origin}${initialApiPath}` : url, { waitUntil: 'domcontentloaded' }); + // Some APIs return JSON without issuing __zse_ck. Only HTML responses + // need the browser's page scripts to finish initializing that cookie. + const isJson = response?.headers()['content-type']?.includes('application/json'); + if (!isJson) { + phase = 'session cookie wait'; + await page.waitForFunction(() => document.cookie.split(';').some((pair) => pair.trimStart().startsWith('__zse_ck=')), undefined, { timeout: 10000 }); + } + + phase = 'session cookie validation'; + const browserCookies = await context.cookies(origin); + const requiredCookies = isJson ? ['d_c0'] : ['d_c0', '__zse_ck']; + if (requiredCookies.some((name) => browserCookies.every((cookie) => cookie.name !== name || !cookie.value))) { throw new Error('The browser did not receive the required cookies'); } - const ua = await page.evaluate(() => window.navigator.userAgent); - return { dc0, zseCk, ua }; + let initialResponse = initialApiPath && isJson && response && response.status() >= 200 && response.status() < 300 ? { apiPath: initialApiPath, body: await response.text() } : undefined; + + return { + get: async (apiPath: string): Promise => { + if (initialResponse?.apiPath === apiPath) { + const { body } = initialResponse; + initialResponse = undefined; + return parseApiResponse(body); + } + // Keep API requests in Chrome so they use the same network, + // cookies and user-agent as the session that created them. + if (new URL(page.url()).origin !== origin) { + await page.goto(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); + } + const currentCookies = await context.cookies(origin); + const dc0 = currentCookies.find((cookie) => cookie.name === 'd_c0')?.value; + if (!dc0) { + throw new Error('zhihu: the browser session lost its d_c0 cookie'); + } + const result = await page.evaluate( + async ({ apiUrl, headers }) => { + const response = await fetch(apiUrl, { headers, credentials: 'include', signal: window.AbortSignal.timeout(30000) }); + return { status: response.status, body: await response.text() }; + }, + { apiUrl: `${origin}${apiPath}`, headers: getSignedHeaders(apiPath, dc0) } + ); + if (result.status < 200 || result.status >= 300) { + throw new Error(`zhihu: browser API request failed with HTTP ${result.status}`); + } + return parseApiResponse(result.body); + }, + getPage: async () => { + await page.goto(url, { waitUntil: 'domcontentloaded' }); + return page.content(); + }, + close: destroy, + }; } catch { - throw new Error('zhihu: browser access did not provide valid credentials. Configure ZHIHU_COOKIES with an accessible session containing d_c0 and __zse_ck.'); - } finally { await destroy(); + throw new Error(`zhihu: browser session failed during ${phase}. Configure ZHIHU_COOKIES with an accessible session containing d_c0 and __zse_ck.`); } }; diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 528a4c2067f3..6db09d96fb02 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -1,10 +1,9 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import type { Articles } from './types'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/posts/:usertype/:id', @@ -38,63 +37,51 @@ export const route: Route = { | people | org |`, }; -async function handler(ctx) { +function handler(ctx) { const id = ctx.req.param('id'); const usertype = ctx.req.param('usertype'); - const userProfile = await cache.tryGet(`zhihu:posts:profile:${id}`, async () => { - // Read the profile from the API instead of scraping the user's HTML - // homepage, which is now rate-limited (403) more aggressively than the API. - const profileApiPath = `/api/v4/members/${id}`; + return withZhihuClient(`https://www.zhihu.com/${usertype}/${id}/posts`, async (client) => { + const userProfile = await cache.tryGet(`zhihu:posts:profile:${id}`, async () => { + // Read the profile from the API instead of scraping the user's HTML + // homepage, which is now rate-limited (403) more aggressively than the API. + const profileApiPath = `/api/v4/members/${id}`; - const result = await ofetch(`https://www.zhihu.com${profileApiPath}`, { - headers: { - ...header, - ...(await getSignedHeader(`https://www.zhihu.com/${usertype}/${id}/`, profileApiPath)), - Referer: `https://www.zhihu.com/${usertype}/${id}/`, - }, + const result = await client.get(profileApiPath); + + return { + name: result.name, + headline: result.headline, + avatarUrl: result.avatar_url, + }; }); - return { - name: result.name, - headline: result.headline, - avatarUrl: result.avatar_url, - }; - }); + const apiPath = `/api/v4/members/${id}/articles?${new URLSearchParams({ + include: + 'data[*].comment_count,suggest_edit,is_normal,thumbnail_extra_info,thumbnail,can_comment,comment_permission,admin_closed_comment,content,voteup_count,created,updated,upvoted_followees,voting,review_info,reaction_instruction,is_labeled,label_info;data[*].vessay_info;data[*].author.badge[?(type=best_answerer)].topics;data[*].author.vip_info;', + offset: '0', + limit: '20', + sort_by: 'created', + })}`; - const apiPath = `/api/v4/members/${id}/articles?${new URLSearchParams({ - include: - 'data[*].comment_count,suggest_edit,is_normal,thumbnail_extra_info,thumbnail,can_comment,comment_permission,admin_closed_comment,content,voteup_count,created,updated,upvoted_followees,voting,review_info,reaction_instruction,is_labeled,label_info;data[*].vessay_info;data[*].author.badge[?(type=best_answerer)].topics;data[*].author.vip_info;', - offset: '0', - limit: '20', - sort_by: 'created', - })}`; + const articleResponse = await client.get(apiPath); - const signedHeader = await getSignedHeader(`https://www.zhihu.com/${usertype}/${id}/posts`, apiPath); + const items = articleResponse.data.map((item) => ({ + title: item.title, + description: processImage(item.content), + link: `https://zhuanlan.zhihu.com/p/${item.id}`, + pubDate: parseDate(item.created, 'X'), + updated: parseDate(item.updated, 'X'), + author: item.author.name, + })); - const articleResponse = await ofetch(`https://www.zhihu.com${apiPath}`, { - headers: { - ...header, - ...signedHeader, - Referer: `https://www.zhihu.com/${usertype}/${id}/posts`, - }, + return { + title: `${userProfile.name} 的知乎文章`, + link: `https://www.zhihu.com/${usertype}/${id}/posts`, + description: userProfile.headline, + image: userProfile.avatarUrl.split('?', 1)[0], + // banner: userData?.coverUrl?.split('?')[0], + item: items, + }; }); - - const items = articleResponse.data.map((item) => ({ - title: item.title, - description: processImage(item.content), - link: `https://zhuanlan.zhihu.com/p/${item.id}`, - pubDate: parseDate(item.created, 'X'), - updated: parseDate(item.updated, 'X'), - author: item.author.name, - })); - - return { - title: `${userProfile.name} 的知乎文章`, - link: `https://www.zhihu.com/${usertype}/${id}/posts`, - description: userProfile.headline, - image: userProfile.avatarUrl.split('?', 1)[0], - // banner: userData?.coverUrl?.split('?')[0], - item: items, - }; } diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index 716748de030f..cdd9b669044c 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.ts @@ -1,8 +1,7 @@ import type { Route } from '@/types'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/question/:questionId/:sortBy?', @@ -34,14 +33,13 @@ export const route: Route = { handler, }; -async function handler(ctx) { +function handler(ctx) { const { questionId, sortBy = 'default', // default,created,updated } = ctx.req.param(); // second: get real data from zhihu - const rootUrl = 'https://www.zhihu.com'; const apiPath = `/api/v4/questions/${questionId}/answers?${new URLSearchParams({ include: 'data[*].is_normal,admin_closed_comment,reward_info,is_collapsed,annotation_action,annotation_detail,collapse_reason,is_sticky,collapsed_by,suggest_edit,comment_count,can_comment,content,editable_content,attachment,voteup_count,reshipment_settings,comment_permission,created_time,updated_time,review_info,relevant_info,question,excerpt,is_labeled,paid_info,paid_info_content,relationship.is_authorized,is_author,voting,is_thanked,is_nothelp,is_recognized;data[*].mark_infos[*].url;data[*].author.follower_count,badge[*].topics;data[*].settings.table_of_content.enabled&offset=0', @@ -50,36 +48,26 @@ async function handler(ctx) { platform: 'desktop', })}`; - const signedHeader = await getSignedHeader(`https://www.zhihu.com/question/${questionId}`, apiPath); + return withZhihuClient(`https://www.zhihu.com/question/${questionId}`, async (client) => { + const response = await client.get(apiPath); + const listRes = response.data; - const response = await got({ - method: 'get', - url: rootUrl + apiPath, - headers: { - ...header, - ...signedHeader, - Referer: `https://www.zhihu.com/question/${questionId}`, - // Authorization: 'oauth c3cef7c66a1843f8b3a9e6a1e3160e20', // previously hard-coded in js, outdated - }, - }); - - const listRes = response.data.data; + return { + title: `知乎-${listRes[0].question.title}`, + link: `https://www.zhihu.com/question/${questionId}`, + item: listRes.map((item) => { + const title = `${item.author.name}的回答:${item.excerpt}`; + const description = `${item.author.name}的回答

    ${processImage(item.content)}`; - return { - title: `知乎-${listRes[0].question.title}`, - link: `https://www.zhihu.com/question/${questionId}`, - item: listRes.map((item) => { - const title = `${item.author.name}的回答:${item.excerpt}`; - const description = `${item.author.name}的回答

    ${processImage(item.content)}`; - - return { - title, - description, - author: item.author.name, - pubDate: parseDate(item.updated_time * 1000), - guid: item.id.toString(), - link: `https://www.zhihu.com/question/${questionId}/answer/${item.id}`, - }; - }), - }; + return { + title, + description, + author: item.author.name, + pubDate: parseDate(item.updated_time * 1000), + guid: item.id.toString(), + link: `https://www.zhihu.com/question/${questionId}/answer/${item.id}`, + }; + }), + }; + }); } diff --git a/lib/routes/zhihu/sign.ts b/lib/routes/zhihu/sign.ts new file mode 100644 index 000000000000..125907c52cad --- /dev/null +++ b/lib/routes/zhihu/sign.ts @@ -0,0 +1,10 @@ +import md5 from '@/utils/md5'; + +import { encrypt } from './execlib/x-zse-96-v3'; + +export const getSignedHeaders = (apiPath: string, dc0: string) => ({ + 'x-api-version': '3.0.91', + 'x-zse-96': '2.0_' + encrypt(md5(`101_3_3.0+${apiPath}+${dc0}`)), + 'x-app-za': 'OS=Web', + 'x-zse-93': '101_3_3.0', +}); diff --git a/lib/routes/zhihu/topic.ts b/lib/routes/zhihu/topic.ts index b057baa6f30e..1249c6ed2bd8 100644 --- a/lib/routes/zhihu/topic.ts +++ b/lib/routes/zhihu/topic.ts @@ -1,9 +1,8 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/topic/:topicId/:isTop?', @@ -34,91 +33,80 @@ export const route: Route = { handler, }; -async function handler(ctx) { +function handler(ctx) { const { topicId, isTop = false } = ctx.req.param(); const link = `https://www.zhihu.com/topic/${topicId}/${isTop ? 'top-answers' : 'newest'}`; - const topicMeta = await cache.tryGet(`zhihu:topic:${topicId}`, async () => { - const url = `https://www.zhihu.com/topic/${topicId}`; - const apiPath = `/api/v4/topics/${topicId}/intro?include=content.meta.content.photos`; - const signedHeader = await getSignedHeader(url, apiPath); - const { data: response } = await got('https://www.zhihu.com' + apiPath, { - headers: signedHeader, + return withZhihuClient(link, async (client) => { + const topicMeta = await cache.tryGet(`zhihu:topic:${topicId}`, () => { + const apiPath = `/api/v4/topics/${topicId}/intro?include=content.meta.content.photos`; + return client.get(apiPath); }); - return response; - }); - - const apiPath = `/api/v5.1/topics/${topicId}/feeds/${isTop ? 'top_activity' : 'timeline_activity'}`; - const signedHeader = await getSignedHeader(link, apiPath); - const { data: response } = await got(`https://www.zhihu.com${apiPath}`, { - headers: { - ...header, - ...signedHeader, - Referer: link, - }, - }); + const apiPath = `/api/v5.1/topics/${topicId}/feeds/${isTop ? 'top_activity' : 'timeline_activity'}`; + const response = await client.get(apiPath); - const items = response.data.map(({ target: item }) => { - const type = item.type; - let title = ''; - let description: string; - let link = ''; - let pubDate: Date; - let author = ''; + const items = response.data.map(({ target: item }) => { + const type = item.type; + let title = ''; + let description: string; + let link = ''; + let pubDate: Date; + let author = ''; - switch (type) { - case 'answer': - title = `${item.question.title}-${item.author.name}的回答:${item.excerpt}`; - description = `${item.question.title}
    ${item.author.name}的回答
    ${processImage(item.content)}`; - link = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; - pubDate = parseDate(item.updated_time * 1000); - author = item.author.name; - break; + switch (type) { + case 'answer': + title = `${item.question.title}-${item.author.name}的回答:${item.excerpt}`; + description = `${item.question.title}
    ${item.author.name}的回答
    ${processImage(item.content)}`; + link = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; + pubDate = parseDate(item.updated_time * 1000); + author = item.author.name; + break; - case 'question': - title = item.title; - description = item.title; - link = `https://www.zhihu.com/question/${item.id}`; - pubDate = parseDate(item.created * 1000); - break; + case 'question': + title = item.title; + description = item.title; + link = `https://www.zhihu.com/question/${item.id}`; + pubDate = parseDate(item.created * 1000); + break; - case 'article': - title = item.title; - description = processImage(item.content); - link = item.url; - pubDate = parseDate(item.created * 1000); - break; + case 'article': + title = item.title; + description = processImage(item.content); + link = item.url; + pubDate = parseDate(item.created * 1000); + break; - case 'zvideo': - title = item.title; - description = `${item.description}
    + case 'zvideo': + title = item.title; + description = `${item.description}
    `; - link = item.url; - pubDate = parseDate(item.created_at * 1000); - break; + link = item.url; + pubDate = parseDate(item.created_at * 1000); + break; - default: - description = '未知类型,请点击链接提交issue'; - pubDate = parseDate(Date.now()); - } + default: + description = '未知类型,请点击链接提交issue'; + pubDate = parseDate(Date.now()); + } + + return { + title, + description, + author, + pubDate, + guid: item.id.toString(), + link, + }; + }); return { - title, - description, - author, - pubDate, - guid: item.id.toString(), + title: `知乎话题-${topicId}-${isTop ? '精华' : '讨论'}`, + description: topicMeta.content.introduction, link, + item: items, }; }); - - return { - title: `知乎话题-${topicId}-${isTop ? '精华' : '讨论'}`, - description: topicMeta.content.introduction, - link, - item: items, - }; } diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index 428da066fa77..330c5f59c2b9 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -1,10 +1,10 @@ import { load } from 'cheerio'; import { config } from '@/config'; -import cache from '@/utils/cache'; -import md5 from '@/utils/md5'; +import ofetch from '@/utils/ofetch'; -import { encrypt as g_encrypt } from './execlib/x-zse-96-v3'; +import type { createBrowserClient } from './browser'; +import { getSignedHeaders } from './sign'; export const header = { 'x-api-version': '3.0.91', @@ -66,78 +66,44 @@ const getCookieValueFrom = (cookieStr: string | undefined, key: string) => export const getCookieValueByKey = (key: string) => getCookieValueFrom(config.zhihu.cookies, key); -const pendingZseCredentials = new Map>(); - -const getGeneratedZseCredentials = (url: string, configuredDc0: string) => { - const cacheKey = `zhihu:browser-credentials:v1:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; - const pending = pendingZseCredentials.get(cacheKey); - if (pending) { - return pending; - } - - const created = (async () => { - try { - return await cache.tryGet( - cacheKey, - async () => { - const { getBrowserCredentials } = await import('./browser'); - return getBrowserCredentials(url, configuredDc0 ? config.zhihu.cookies || '' : ''); - }, - config.cache.contentExpire, - false - ); - } finally { - pendingZseCredentials.delete(cacheKey); - } - })(); - pendingZseCredentials.set(cacheKey, created); - return created; +export type ZhihuClient = { + get(apiPath: string): Promise; + getPage(): Promise; }; -const mergeGeneratedCookies = (configured: string, dc0: string, zseCk: string) => { - const remaining = configured - .split(';') - .map((pair) => pair.trim()) - .filter((pair) => { - const name = pair.split('=', 1)[0]; - return name && name !== 'd_c0' && name !== '__zse_ck'; +export const withZhihuClient = async (pageUrl: string, callback: (client: ZhihuClient) => Promise): Promise => { + const configured = config.zhihu.cookies || ''; + const dc0 = getCookieValueFrom(configured, 'd_c0'); + const hasConfiguredSession = !!dc0 && !!getCookieValueFrom(configured, '__zse_ck'); + let browserPromise: ReturnType | undefined; + const startBrowser = async (apiPath?: string) => { + const { createBrowserClient } = await import('./browser'); + return createBrowserClient(pageUrl, dc0 ? configured : '', apiPath); + }; + const getBrowser = (apiPath?: string) => (browserPromise ??= startBrowser(apiPath)); + + try { + return await callback({ + get: async (apiPath: string): Promise => { + if (!apiPath.startsWith('/api/')) { + throw new Error('zhihu: expected an API path'); + } + if (hasConfiguredSession) { + return ofetch(`https://www.zhihu.com${apiPath}`, { + headers: { ...getSignedHeaders(apiPath, dc0), cookie: configured, Referer: pageUrl }, + }); + } + return (await getBrowser(apiPath)).get(apiPath); + }, + getPage: async () => (hasConfiguredSession ? ofetch(pageUrl, { headers: { cookie: configured, Referer: pageUrl }, parseResponse: (text) => text }) : (await getBrowser()).getPage()), }); - return [`__zse_ck=${zseCk}`, `d_c0=${dc0}`, ...remaining].join('; '); -}; - -export const getSignedHeader = async (url: string, apiPath: string) => { - const configured = config?.zhihu?.cookies || ''; - - const configuredDc0 = getCookieValueFrom(configured, 'd_c0'); - const configuredZseCk = getCookieValueFrom(configured, '__zse_ck'); - - // A configured pair may have been generated with a different user-agent, so - // preserve the previous behavior and trust it as-is. Generated credentials - // always return their matching user-agent. - let cookieStr: string; - let ua: string | undefined; - if (configuredDc0 && configuredZseCk) { - cookieStr = configured; - } else { - const credentials = await getGeneratedZseCredentials(url, configuredDc0); - // Login cookies only belong to the configured d_c0 session. Do not mix - // an isolated z_c0 with a newly-created guest session. - cookieStr = configuredDc0 ? mergeGeneratedCookies(configured, credentials.dc0, credentials.zseCk) : `__zse_ck=${credentials.zseCk}; d_c0=${credentials.dc0}`; - ua = credentials.ua; + } finally { + let browser: Awaited> | undefined; + try { + browser = await browserPromise; + } catch { + // Failed initialization already closes its browser before rejecting. + } + await browser?.close(); } - - // Sign with the same `d_c0` that is sent, otherwise the backend rejects the - // request. Refer to https://github.com/srx-2000/spider_collection/issues/18 - const dc0 = getCookieValueFrom(cookieStr, 'd_c0'); - const xzse93 = '101_3_3.0'; - const f = `${xzse93}+${apiPath}+${dc0}`; - const xzse96 = '2.0_' + g_encrypt(md5(f)); - - return { - cookie: cookieStr, - ...(ua && { 'user-agent': ua }), - 'x-zse-96': xzse96, - 'x-app-za': 'OS=Web', - 'x-zse-93': xzse93, - }; }; diff --git a/lib/routes/zhihu/zhuanlan.ts b/lib/routes/zhihu/zhuanlan.ts index 7dd8bfbcb231..3013c97cc02d 100644 --- a/lib/routes/zhihu/zhuanlan.ts +++ b/lib/routes/zhihu/zhuanlan.ts @@ -1,10 +1,9 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { getSignedHeader, header, processImage } from './utils'; +import { processImage, withZhihuClient } from './utils'; export const route: Route = { path: '/zhuanlan/:id', @@ -34,7 +33,7 @@ export const route: Route = { handler, }; -async function handler(ctx) { +function handler(ctx) { const id = ctx.req.param('id'); // 知乎专栏链接存在两种格式, 一种以 'zhuanlan.' 开头, 另一种新增的以 'c_' 结尾 let url = `https://zhuanlan.zhihu.com/${id}`; @@ -42,94 +41,73 @@ async function handler(ctx) { url = `https://www.zhihu.com/column/${id}`; } - const signedHeader = await getSignedHeader(url, `/api/v4/columns/${id}/items`); - const listRes = await got({ - method: 'get', - url: `https://www.zhihu.com/api/v4/columns/${id}/items`, - headers: { - ...signedHeader, - Referer: `https://zhuanlan.zhihu.com/${id}`, - }, - }); - - const pinnedRes = await got({ - method: 'get', - url: `https://www.zhihu.com/api/v4/columns/${id}/pinned-items/v2`, - headers: { - ...header, - ...signedHeader, - Referer: `https://zhuanlan.zhihu.com/${id}`, - }, - }); + return withZhihuClient(url, async (client) => { + const listRes = await client.get(`/api/v4/columns/${id}/items`); + const pinnedRes = await client.get(`/api/v4/columns/${id}/pinned-items/v2`); + const list = [...listRes.data, ...pinnedRes.data]; + + const html = await client.getPage(); + const $ = load(html); + const title = $('.css-zyehvu').text(); + const description = $('.css-1bnklpv').text(); + + const item = list.map((item) => { + // 当专栏内文章内容不含任何文字时, 返回空字符, 以免直接报错 + let description = ''; + if (item.content) { + description = processImage(item.content); + } + + let title: string; + let link: string; + let author: string; + let pubDate: Date; + + switch (item.type) { + case 'answer': + title = item.question.title; + author = item.question.author ? item.question.author.name : ''; + link = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; + pubDate = parseDate(item.created_time * 1000); + + break; + + case 'article': + title = item.title; + link = item.url; + author = item.author.name; + pubDate = parseDate(item.created * 1000); + + break; + + case 'zvideo': + // 如果类型是zvideo,id即为视频地址参数 + title = item.title; + link = `https://www.zhihu.com/zvideo/${item.id}`; + author = item.author.name; + pubDate = parseDate(item.created_at * 1000); + // 判断是否存在视频简介 + description = item.description ? `${item.description}

    视频内容请跳转至原页面观看` : `视频内容请跳转至原页面观看`; + + break; + + default: + throw new Error(`Unknown type: ${item.type}`); + } + return { + title, + link, + description, + pubDate, + author, + }; + }); - listRes.data.data = [...listRes.data.data, ...pinnedRes.data.data]; - - const infoRes = await got(url, { - headers: { - ...signedHeader, - Referer: url, - }, - }); - const $ = load(infoRes.data); - const title = $('.css-zyehvu').text(); - const description = $('.css-1bnklpv').text(); - - const item = listRes.data.data.map((item) => { - // 当专栏内文章内容不含任何文字时, 返回空字符, 以免直接报错 - let description = ''; - if (item.content) { - description = processImage(item.content); - } - - let title: string; - let link: string; - let author: string; - let pubDate: Date; - - switch (item.type) { - case 'answer': - title = item.question.title; - author = item.question.author ? item.question.author.name : ''; - link = `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`; - pubDate = parseDate(item.created_time * 1000); - - break; - - case 'article': - title = item.title; - link = item.url; - author = item.author.name; - pubDate = parseDate(item.created * 1000); - - break; - - case 'zvideo': - // 如果类型是zvideo,id即为视频地址参数 - title = item.title; - link = `https://www.zhihu.com/zvideo/${item.id}`; - author = item.author.name; - pubDate = parseDate(item.created_at * 1000); - // 判断是否存在视频简介 - description = item.description ? `${item.description}

    视频内容请跳转至原页面观看` : `视频内容请跳转至原页面观看`; - - break; - - default: - throw new Error(`Unknown type: ${item.type}`); - } return { - title, - link, description, - pubDate, - author, + item, + title: `知乎专栏-${title}`, + link: url, }; }); - - return { - description, - item, - title: `知乎专栏-${title}`, - link: url, - }; } diff --git a/tests/zhihu-browser.test.ts b/tests/zhihu-browser.test.ts index f00e51bc754e..80a199d90e32 100644 --- a/tests/zhihu-browser.test.ts +++ b/tests/zhihu-browser.test.ts @@ -1,9 +1,9 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ cookies: '', + ofetch: vi.fn(), getPlaywrightPage: vi.fn(), - tryGet: vi.fn(), destroy: vi.fn(), addCookies: vi.fn(), browserCookies: vi.fn(), @@ -11,6 +11,8 @@ const mocks = vi.hoisted(() => ({ route: vi.fn(), waitForFunction: vi.fn(), evaluate: vi.fn(), + content: vi.fn(), + url: vi.fn(), })); vi.mock('../lib/config', () => ({ @@ -20,72 +22,325 @@ vi.mock('../lib/config', () => ({ return mocks.cookies; }, }, - cache: { contentExpire: 600 }, }, })); -vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); +const origin = 'https://www.zhihu.com'; +const pageUrl = `${origin}/people/example`; +const apiPath = '/api/v4/members/example/activities?limit=20'; +const feedData = { data: [{ id: 'api-item' }] }; +const pageHtml = 'Profile content'; +const nativeFetch = vi.fn(); +const navigationResponse = (contentType = 'text/html', status = 200, body = pageHtml) => ({ + headers: () => ({ 'content-type': contentType }), + status: () => status, + text: () => Promise.resolve(body), +}); + beforeEach(() => { vi.resetModules(); - vi.clearAllMocks(); + vi.resetAllMocks(); mocks.cookies = ''; - mocks.tryGet.mockImplementation((_key, create) => create()); + mocks.goto.mockImplementation((url) => { + mocks.url.mockReturnValue(url); + return Promise.resolve(navigationResponse()); + }); mocks.waitForFunction.mockResolvedValue(undefined); mocks.browserCookies.mockResolvedValue([ { name: 'd_c0', value: 'browser-dc0' }, { name: '__zse_ck', value: 'browser-zse' }, ]); - mocks.evaluate.mockResolvedValue('Browser user agent'); + mocks.evaluate.mockImplementation((callback, argument) => callback(argument)); + mocks.content.mockResolvedValue(pageHtml); + mocks.url.mockReturnValue(pageUrl); mocks.getPlaywrightPage.mockResolvedValue({ - page: { goto: mocks.goto, route: mocks.route, waitForFunction: mocks.waitForFunction, evaluate: mocks.evaluate }, + page: { goto: mocks.goto, route: mocks.route, waitForFunction: mocks.waitForFunction, evaluate: mocks.evaluate, content: mocks.content, url: mocks.url }, context: { addCookies: mocks.addCookies, cookies: mocks.browserCookies }, destroy: mocks.destroy, }); + nativeFetch.mockImplementation(() => Promise.resolve(Response.json(feedData))); + vi.stubGlobal('fetch', nativeFetch); + vi.stubGlobal('window', { AbortSignal }); +}); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); }); -describe('Zhihu browser credentials', () => { - it('uses configured credentials without starting a browser', async () => { - mocks.cookies = 'd_c0=configured; __zse_ck=existing; z_c0=login'; - const { getSignedHeader } = await import('../lib/routes/zhihu/utils'); - const headers = await getSignedHeader('https://www.zhihu.com/people/example', '/api/v4/members/example'); - expect(headers.cookie).toBe(mocks.cookies); - expect(headers['x-zse-96']).toMatch(/^2\.0_/); +describe('Zhihu client session', () => { + it('uses complete configured credentials for API and HTML requests without starting a browser', async () => { + mocks.cookies = 'd_c0=configured==; __zse_ck=existing; z_c0=login=value'; + mocks.ofetch.mockResolvedValueOnce(feedData).mockResolvedValueOnce(pageHtml); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + const result = await withZhihuClient(pageUrl, async (client) => ({ data: await client.get(apiPath), html: await client.getPage() })); + + expect(result).toEqual({ data: feedData, html: pageHtml }); + expect(mocks.ofetch).toHaveBeenNthCalledWith(1, `${origin}${apiPath}`, { + headers: expect.objectContaining({ cookie: mocks.cookies, Referer: pageUrl, 'x-zse-96': expect.stringMatching(/^2\.0_/) }), + }); + expect(mocks.ofetch).toHaveBeenNthCalledWith(2, pageUrl, { + headers: { cookie: mocks.cookies, Referer: pageUrl }, + parseResponse: expect.any(Function), + }); + expect(mocks.ofetch.mock.calls[1][1].parseResponse(pageHtml)).toBe(pageHtml); expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); - expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(nativeFetch).not.toHaveBeenCalled(); }); - it('keeps browser cookies and their native user agent together', async () => { - mocks.cookies = 'z_c0=isolated-login'; - const { getSignedHeader } = await import('../lib/routes/zhihu/utils'); - const headers = await getSignedHeader('https://www.zhihu.com/people/example', '/api/v4/members/example'); - expect(headers.cookie).toBe('__zse_ck=browser-zse; d_c0=browser-dc0'); - expect(headers['user-agent']).toBe('Browser user agent'); + it.each(['', 'z_c0=isolated-login', 'd_c0=; z_c0=isolated-login'])('initializes a guest session before the target without transferring an unrelated login: %s', async (cookies) => { + mocks.cookies = cookies; + mocks.browserCookies.mockResolvedValueOnce([{ name: 'd_c0', value: 'browser-dc0' }]); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); + expect(mocks.addCookies).not.toHaveBeenCalled(); - expect(mocks.goto).toHaveBeenCalledWith('https://www.zhihu.com/people/example', { waitUntil: 'domcontentloaded' }); - expect(mocks.browserCookies).toHaveBeenCalledWith('https://www.zhihu.com/'); + expect(mocks.goto.mock.calls).toEqual([ + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + [`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }], + ]); + expect(mocks.browserCookies).toHaveBeenCalledTimes(3); + expect(mocks.browserCookies).toHaveBeenCalledWith(origin); + expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(pageUrl, { noGoto: true, closeTimeout: 0 }); expect(mocks.destroy).toHaveBeenCalledOnce(); + expect(mocks.ofetch).not.toHaveBeenCalled(); }); - it('transfers the configured session without truncating cookie values', async () => { - const { getBrowserCredentials } = await import('../lib/routes/zhihu/browser'); - await getBrowserCredentials('https://www.zhihu.com/people/example', 'd_c0=seed==;z_c0=login=value'); + it('keeps a configured identity and login intact and requests the API through the browser native fetch', async () => { + mocks.cookies = 'd_c0=seed==;z_c0=login=value'; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, (client) => client.get(apiPath)); + expect(mocks.addCookies).toHaveBeenCalledWith([ { name: 'd_c0', value: 'seed==', domain: '.zhihu.com', path: '/' }, { name: 'z_c0', value: 'login=value', domain: '.zhihu.com', path: '/' }, ]); + expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }); + expect(mocks.evaluate).toHaveBeenCalledOnce(); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, { + headers: expect.objectContaining({ 'x-zse-96': expect.stringMatching(/^2\.0_/) }), + credentials: 'include', + signal: expect.any(AbortSignal), + }); + const headers = new Headers(nativeFetch.mock.calls[0][1]?.headers); + expect(headers.has('cookie')).toBe(false); + expect(headers.has('user-agent')).toBe(false); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); const handler = mocks.route.mock.calls[0][1]; - const continueRequest = vi.fn(); - const abortRequest = vi.fn(); - await handler({ request: () => ({ resourceType: () => 'image' }), continue: continueRequest, abort: abortRequest }); - expect(abortRequest).toHaveBeenCalledOnce(); - expect(continueRequest).not.toHaveBeenCalled(); + await Promise.all( + ['document', 'script', 'xhr', 'fetch', 'image'].map(async (resourceType) => { + const continueRequest = vi.fn(); + const abortRequest = vi.fn(); + await handler({ request: () => ({ resourceType: () => resourceType }), continue: continueRequest, abort: abortRequest }); + expect(continueRequest).toHaveBeenCalledTimes(resourceType === 'image' ? 0 : 1); + expect(abortRequest).toHaveBeenCalledTimes(resourceType === 'image' ? 1 : 0); + }) + ); + }); + + it('reuses one browser for API and HTML reads and signs each API request with its current identity', async () => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + const signature = vi.spyOn(await import('../lib/routes/zhihu/sign'), 'getSignedHeaders'); + mocks.browserCookies + .mockResolvedValueOnce([{ name: 'd_c0', value: 'guest-dc0' }]) + .mockResolvedValueOnce([ + { name: 'd_c0', value: 'guest-dc0' }, + { name: '__zse_ck', value: 'browser-zse' }, + ]) + .mockResolvedValueOnce([{ name: 'd_c0', value: 'first-dc0' }]) + .mockResolvedValueOnce([{ name: 'd_c0', value: 'updated-dc0' }]); + + const html = await withZhihuClient(pageUrl, async (client) => { + expect(await client.get(apiPath)).toEqual(feedData); + expect(await client.get('/api/v4/members/example')).toEqual(feedData); + expect(mocks.destroy).not.toHaveBeenCalled(); + return client.getPage(); + }); + + expect(html).toBe(pageHtml); + expect(signature).toHaveBeenNthCalledWith(1, apiPath, 'first-dc0'); + expect(signature).toHaveBeenNthCalledWith(2, '/api/v4/members/example', 'updated-dc0'); + expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); + expect(mocks.goto).toHaveBeenLastCalledWith(pageUrl, { waitUntil: 'domcontentloaded' }); + expect(mocks.content).toHaveBeenCalledOnce(); + expect(nativeFetch).toHaveBeenCalledTimes(2); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('requests the signed API after an initial JSON error without waiting for an unnecessary session cookie', async () => { + mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); + mocks.goto.mockImplementation((url) => { + mocks.url.mockReturnValue(url); + return Promise.resolve(url === `${origin}${apiPath}` ? navigationResponse('application/json; charset=utf-8', 403, '{"error":{"code":403}}') : navigationResponse()); + }); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); + + expect(mocks.goto.mock.calls).toEqual([ + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + [`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }], + ]); + expect(mocks.waitForFunction).not.toHaveBeenCalled(); + expect(mocks.browserCookies).toHaveBeenCalledTimes(3); + expect(mocks.evaluate).toHaveBeenCalledOnce(); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, expect.objectContaining({ credentials: 'include' })); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('consumes a successful initial JSON response once without requesting the first API twice', async () => { + mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); + mocks.goto.mockImplementation((url) => { + mocks.url.mockReturnValue(url); + return Promise.resolve(url === `${origin}${apiPath}` ? navigationResponse('application/json', 200, JSON.stringify(feedData)) : navigationResponse()); + }); + const refreshedData = { data: [{ id: 'refreshed-item' }] }; + nativeFetch.mockResolvedValue(Response.json(refreshedData)); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, async (client) => { + expect(await client.get(apiPath)).toEqual(feedData); + expect(nativeFetch).not.toHaveBeenCalled(); + expect(mocks.evaluate).not.toHaveBeenCalled(); + expect(await client.get(apiPath)).toEqual(refreshedData); + }); + + expect(mocks.goto).toHaveBeenCalledTimes(2); + expect(mocks.waitForFunction).not.toHaveBeenCalled(); + expect(nativeFetch).toHaveBeenCalledOnce(); + expect(mocks.evaluate).toHaveBeenCalledOnce(); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('initializes a column on the API origin and restores that origin after reading the column HTML', async () => { + const columnUrl = 'https://zhuanlan.zhihu.com/example'; + const columnApi = '/api/v4/columns/example/items'; + mocks.url.mockReturnValue(columnUrl); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(columnUrl, async (client) => { + expect(await client.get(columnApi)).toEqual(feedData); + expect(await client.getPage()).toBe(pageHtml); + expect(await client.get(columnApi)).toEqual(feedData); + }); + + expect(mocks.goto.mock.calls).toEqual([ + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + [`${origin}${columnApi}`, { waitUntil: 'domcontentloaded' }], + [columnUrl, { waitUntil: 'domcontentloaded' }], + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + ]); + expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(columnUrl, { noGoto: true, closeTimeout: 0 }); + expect(nativeFetch).toHaveBeenCalledTimes(2); + expect(nativeFetch).toHaveBeenCalledWith(`${origin}${columnApi}`, expect.objectContaining({ credentials: 'include' })); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('does not initialize a browser when the callback performs no requests', async () => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(pageUrl, () => Promise.resolve('cached result'))).toBe('cached result'); + + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(nativeFetch).not.toHaveBeenCalled(); + }); + + it('closes the browser when the route callback fails after reading data', async () => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await expect( + withZhihuClient(pageUrl, async (client) => { + await client.get(apiPath); + throw new Error('Route parsing failed'); + }) + ).rejects.toThrow('Route parsing failed'); + + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it.each([ + { status: 403, body: 'test-private-response', error: 'HTTP 403' }, + { status: 200, body: 'test-private-response', error: 'did not return JSON' }, + ])('rejects an unusable API response and closes the browser: $error', async ({ status, body, error }) => { + nativeFetch.mockResolvedValue(new Response(body, { status })); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); + + await expect(result).rejects.toThrow(error); + await expect(result).rejects.not.toThrow('test-private-response'); + expect(mocks.destroy).toHaveBeenCalledOnce(); + expect(mocks.ofetch).not.toHaveBeenCalled(); + }); + + it('closes the browser when its native API fetch fails', async () => { + nativeFetch.mockRejectedValue(new Error('Browser fetch failed')); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await expect(withZhihuClient(pageUrl, (client) => client.get(apiPath))).rejects.toThrow('Browser fetch failed'); + + expect(mocks.destroy).toHaveBeenCalledOnce(); + expect(mocks.ofetch).not.toHaveBeenCalled(); }); - it('closes the browser and gives an actionable error when cookies are unavailable', async () => { + it('closes the browser and gives an actionable phase when session cookies are unavailable', async () => { mocks.waitForFunction.mockRejectedValue(new Error('Page timeout')); - const { getBrowserCredentials } = await import('../lib/routes/zhihu/browser'); - await expect(getBrowserCredentials('https://www.zhihu.com/people/example', '')).rejects.toThrow('Configure ZHIHU_COOKIES'); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); + + await expect(result).rejects.toThrow('session cookie wait'); + await expect(result).rejects.toThrow('Configure ZHIHU_COOKIES'); + expect(nativeFetch).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('stops before the target and closes the browser when guest navigation fails', async () => { + mocks.goto.mockRejectedValueOnce(new Error('Navigation failed with test-private-diagnostic')); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); + + await expect(result).rejects.toThrow('guest cookie initialization'); + await expect(result).rejects.toThrow('Configure ZHIHU_COOKIES'); + await expect(result).rejects.not.toThrow('test-private-diagnostic'); + expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); + expect(mocks.browserCookies).not.toHaveBeenCalled(); + expect(mocks.waitForFunction).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it('stops before the target and closes the browser when the guest page does not set d_c0', async () => { + mocks.browserCookies.mockResolvedValueOnce([{ name: '__zse_ck', value: 'browser-zse' }]); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await expect(withZhihuClient(pageUrl, (client) => client.get(apiPath))).rejects.toThrow('guest cookie initialization'); + + expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); + expect(mocks.browserCookies).toHaveBeenCalledExactlyOnceWith(origin); + expect(mocks.waitForFunction).not.toHaveBeenCalled(); + expect(mocks.evaluate).not.toHaveBeenCalled(); expect(mocks.destroy).toHaveBeenCalledOnce(); }); + + it('rejects non-API paths before initializing a session', async () => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await expect(withZhihuClient(pageUrl, (client) => client.get('//example.invalid/api/'))).rejects.toThrow('expected an API path'); + + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + expect(nativeFetch).not.toHaveBeenCalled(); + expect(mocks.ofetch).not.toHaveBeenCalled(); + }); }); From d3bfcfc77b2e0296f2a030158f229eb9b643228c Mon Sep 17 00:00:00 2001 From: DIYgod Date: Sun, 6 Sep 2026 23:38:20 +0800 Subject: [PATCH 0717/1190] fix(zhihu): initialize page sessions outside column APIs --- lib/routes/zhihu/utils.ts | 3 +- tests/zhihu-browser.test.ts | 55 ++++++++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index 330c5f59c2b9..acbcbb4565fe 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -78,7 +78,8 @@ export const withZhihuClient = async (pageUrl: string, callback: (client: Zhi let browserPromise: ReturnType | undefined; const startBrowser = async (apiPath?: string) => { const { createBrowserClient } = await import('./browser'); - return createBrowserClient(pageUrl, dc0 ? configured : '', apiPath); + // Column APIs can initialize directly; other routes need their page session. + return createBrowserClient(pageUrl, dc0 ? configured : '', apiPath?.startsWith('/api/v4/columns/') ? apiPath : undefined); }; const getBrowser = (apiPath?: string) => (browserPromise ??= startBrowser(apiPath)); diff --git a/tests/zhihu-browser.test.ts b/tests/zhihu-browser.test.ts index 80a199d90e32..2d36fa802ff8 100644 --- a/tests/zhihu-browser.test.ts +++ b/tests/zhihu-browser.test.ts @@ -30,6 +30,8 @@ vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrig const origin = 'https://www.zhihu.com'; const pageUrl = `${origin}/people/example`; const apiPath = '/api/v4/members/example/activities?limit=20'; +const columnPageUrl = 'https://zhuanlan.zhihu.com/example'; +const columnApiPath = '/api/v4/columns/example/items'; const feedData = { data: [{ id: 'api-item' }] }; const pageHtml = 'Profile content'; const nativeFetch = vi.fn(); @@ -101,7 +103,7 @@ describe('Zhihu client session', () => { expect(mocks.addCookies).not.toHaveBeenCalled(); expect(mocks.goto.mock.calls).toEqual([ [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }], + [pageUrl, { waitUntil: 'domcontentloaded' }], ]); expect(mocks.browserCookies).toHaveBeenCalledTimes(3); expect(mocks.browserCookies).toHaveBeenCalledWith(origin); @@ -120,7 +122,7 @@ describe('Zhihu client session', () => { { name: 'd_c0', value: 'seed==', domain: '.zhihu.com', path: '/' }, { name: 'z_c0', value: 'login=value', domain: '.zhihu.com', path: '/' }, ]); - expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }); + expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(pageUrl, { waitUntil: 'domcontentloaded' }); expect(mocks.evaluate).toHaveBeenCalledOnce(); expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, { headers: expect.objectContaining({ 'x-zse-96': expect.stringMatching(/^2\.0_/) }), @@ -178,20 +180,20 @@ describe('Zhihu client session', () => { mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); mocks.goto.mockImplementation((url) => { mocks.url.mockReturnValue(url); - return Promise.resolve(url === `${origin}${apiPath}` ? navigationResponse('application/json; charset=utf-8', 403, '{"error":{"code":403}}') : navigationResponse()); + return Promise.resolve(url === `${origin}${columnApiPath}` ? navigationResponse('application/json; charset=utf-8', 403, '{"error":{"code":403}}') : navigationResponse()); }); const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); + expect(await withZhihuClient(columnPageUrl, (client) => client.get(columnApiPath))).toEqual(feedData); expect(mocks.goto.mock.calls).toEqual([ [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [`${origin}${apiPath}`, { waitUntil: 'domcontentloaded' }], + [`${origin}${columnApiPath}`, { waitUntil: 'domcontentloaded' }], ]); expect(mocks.waitForFunction).not.toHaveBeenCalled(); expect(mocks.browserCookies).toHaveBeenCalledTimes(3); expect(mocks.evaluate).toHaveBeenCalledOnce(); - expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, expect.objectContaining({ credentials: 'include' })); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${columnApiPath}`, expect.objectContaining({ credentials: 'include' })); expect(mocks.ofetch).not.toHaveBeenCalled(); expect(mocks.destroy).toHaveBeenCalledOnce(); }); @@ -200,17 +202,17 @@ describe('Zhihu client session', () => { mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); mocks.goto.mockImplementation((url) => { mocks.url.mockReturnValue(url); - return Promise.resolve(url === `${origin}${apiPath}` ? navigationResponse('application/json', 200, JSON.stringify(feedData)) : navigationResponse()); + return Promise.resolve(url === `${origin}${columnApiPath}` ? navigationResponse('application/json', 200, JSON.stringify(feedData)) : navigationResponse()); }); const refreshedData = { data: [{ id: 'refreshed-item' }] }; nativeFetch.mockResolvedValue(Response.json(refreshedData)); const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - await withZhihuClient(pageUrl, async (client) => { - expect(await client.get(apiPath)).toEqual(feedData); + await withZhihuClient(columnPageUrl, async (client) => { + expect(await client.get(columnApiPath)).toEqual(feedData); expect(nativeFetch).not.toHaveBeenCalled(); expect(mocks.evaluate).not.toHaveBeenCalled(); - expect(await client.get(apiPath)).toEqual(refreshedData); + expect(await client.get(columnApiPath)).toEqual(refreshedData); }); expect(mocks.goto).toHaveBeenCalledTimes(2); @@ -246,6 +248,39 @@ describe('Zhihu client session', () => { expect(mocks.destroy).toHaveBeenCalledOnce(); }); + it('initializes a c_-style column through its API even when the page already uses the API origin', async () => { + const columnUrl = `${origin}/column/c_123`; + const columnApi = '/api/v4/columns/c_123/items'; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(columnUrl, (client) => client.get(columnApi))).toEqual(feedData); + + expect(mocks.goto.mock.calls).toEqual([ + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + [`${origin}${columnApi}`, { waitUntil: 'domcontentloaded' }], + ]); + expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(columnUrl, { noGoto: true, closeTimeout: 0 }); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${columnApi}`, expect.objectContaining({ credentials: 'include' })); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + + it.each([ + [`${origin}/people/example`, '/api/v3/moments/example/activities?limit=5'], + [`${origin}/topic/123/newest`, '/api/v5.1/topics/123/feeds/timeline_activity'], + [`${origin}/question/123`, '/api/v4/questions/123/answers?limit=20'], + ])('initializes the human page for a non-column API: %s', async (targetUrl, targetApi) => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(targetUrl, (client) => client.get(targetApi))).toEqual(feedData); + + expect(mocks.goto.mock.calls).toEqual([ + [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], + [targetUrl, { waitUntil: 'domcontentloaded' }], + ]); + expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${targetApi}`, expect.objectContaining({ credentials: 'include' })); + expect(mocks.destroy).toHaveBeenCalledOnce(); + }); + it('does not initialize a browser when the callback performs no requests', async () => { const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); From 5a2f4127e556b8918fc14e9f720c18c3130ac288 Mon Sep 17 00:00:00 2001 From: LunyaaDev Date: Sun, 6 Sep 2026 22:22:34 +0200 Subject: [PATCH 0718/1190] fix(route/kleinanzeigen): fix parsing search results - #23219 (#23219) --- lib/routes/kleinanzeigen/utils/parse-listing-page.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/routes/kleinanzeigen/utils/parse-listing-page.ts b/lib/routes/kleinanzeigen/utils/parse-listing-page.ts index b8cfc3c13d2f..7f8a31ca8a70 100644 --- a/lib/routes/kleinanzeigen/utils/parse-listing-page.ts +++ b/lib/routes/kleinanzeigen/utils/parse-listing-page.ts @@ -11,12 +11,14 @@ import { getProductPage } from './get-product-page'; */ export const parseListingPage = ($: CheerioAPI): Promise => Promise.all( - $('li.ad-listitem.fully-clickable-card') + $('#srchrslt-results li') .not('.badge-topad') .toArray() .map((item) => { const $item = $(item); const article = $item.find('article'); - return getProductPage(`https://www.kleinanzeigen.de${article.attr('data-href')}`); + return article.attr('data-href'); }) + .filter((href) => href !== undefined) + .map((href) => getProductPage(`https://www.kleinanzeigen.de${href}`)) ); From 79dd07879b8813272b202444664f139dccfface7 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Mon, 7 Sep 2026 04:39:15 +0800 Subject: [PATCH 0719/1190] feat(route): add Minhang Museum exhibition route (#23117) * feat(route): add Minhang Museum exhibiton route * chore: trigger ci --- lib/routes/minhangmuseum/interim.tsx | 125 ++++++++++++++++++++++++++ lib/routes/minhangmuseum/namespace.ts | 10 +++ 2 files changed, 135 insertions(+) create mode 100644 lib/routes/minhangmuseum/interim.tsx create mode 100644 lib/routes/minhangmuseum/namespace.ts diff --git a/lib/routes/minhangmuseum/interim.tsx b/lib/routes/minhangmuseum/interim.tsx new file mode 100644 index 000000000000..50ba3c524893 --- /dev/null +++ b/lib/routes/minhangmuseum/interim.tsx @@ -0,0 +1,125 @@ +import { load } from 'cheerio'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +const parseExhibitionDuration = (duration?: string) => { + const [startDate, endDate] = duration?.match(/\d{4}年\d{1,2}月\d{1,2}日/g)?.map((d) => d.replace(/(\d{4})年(\d{1,2})月(\d{1,2})日/, (_, y, m, d) => `${y}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`)) ?? []; + + return { + startDate, + endDate, + }; +}; + +export const route: Route = { + path: '/interim', + categories: ['travel'], + example: '/minhangmuseum/interim', + name: '临时展览', + maintainers: ['magazian'], + radar: [ + { + source: ['minhangmuseum.shmh.gov.cn/weixin/interim/list.htm'], + target: '/interim', + }, + ], + handler: async () => { + const baseUrl = 'https://minhangmuseum.shmh.gov.cn'; + const listUrl = `${baseUrl}/weixin/interim/list.htm`; + const museumName = namespace.zh?.name || namespace.name; + + const response = await ofetch(listUrl); + const $ = load(response); + + const listItems = $('#courseContent li, .ex-ul li') + .toArray() + .map((el) => { + const $el = $(el); + const href = $el.find('a').attr('href'); + if (!href) { + return null; + } + const itemLink = new URL(href, baseUrl).href; + const title = $el.find('.ex-li-title').text(); + const fullDuration = $el.find('.apply-time').text(); + const imgUrl = $el.find('.ex-li-img img').attr('src'); + + return { + title, + itemLink, + fullDuration, + imgUrl, + }; + }) + .filter((item): item is NonNullable => item !== null); + + const items: DataItem[] = await Promise.all( + listItems.map((item) => + cache.tryGet(item.itemLink, async () => { + const detailResponse = await ofetch(item.itemLink); + const $detail = load(detailResponse); + + let location: string | undefined; + $detail('.activity-detail-time ul li').each((_, el) => { + const label = $detail(el).find('.detail-time-lf').text(); + const val = $detail(el).find('.detail-time-rig').text(); + if (label.includes('地点') && val) { + location = val; + } + }); + + const { startDate, endDate } = parseExhibitionDuration(item.fullDuration); + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    +

    + 原始展期:{item.fullDuration} +

    +
    + ); + + return { + title: item.title, + link: item.itemLink, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }; + }) + ) + ); + + return { + title: `${museumName} - 临时展览`, + link: listUrl, + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/minhangmuseum/namespace.ts b/lib/routes/minhangmuseum/namespace.ts new file mode 100644 index 000000000000..7ffe99144141 --- /dev/null +++ b/lib/routes/minhangmuseum/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Minhang Museum', + url: 'minhangmuseum.shmh.gov.cn', + + zh: { + name: '上海市闵行区博物馆', + }, +}; From 6ca2f66003e4d75f851db4b0ea191a3486046303 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 13:27:18 +0800 Subject: [PATCH 0720/1190] fix(worker): close shared cache and route compatibility gaps --- lib/routes/2048/index.tsx | 14 +- lib/routes/2048/utils.ts | 15 ++ lib/routes/aiea/index.ts | 11 +- lib/routes/airchina/index.ts | 20 +-- lib/routes/brooklynmuseum/exhibitions.ts | 6 +- lib/routes/eastmoney/report/index.tsx | 9 +- lib/routes/gov/zhengce/zhengceku.ts | 12 +- lib/routes/hellogithub/index.ts | 4 +- lib/routes/i-cable/news.tsx | 4 +- lib/routes/iiilab/index.ts | 22 +-- lib/routes/javbus/index.tsx | 14 +- lib/routes/jewishmuseum/exhibitions.ts | 4 +- lib/routes/newmuseum/exhibitions.ts | 6 +- lib/routes/oct0pu5/rss.ts | 16 +- lib/routes/polimi/news.ts | 15 +- lib/routes/queshu/book.ts | 12 +- lib/routes/queshu/sale.ts | 20 ++- lib/routes/readhub/daily.ts | 2 +- lib/routes/readhub/index.ts | 4 +- lib/routes/readhub/templates/description.tsx | 9 +- lib/routes/readhub/util.ts | 111 ++++++++----- lib/routes/smzdm/keyword.ts | 13 +- lib/routes/smzdm/utils.ts | 13 ++ lib/routes/stratechery/index.ts | 12 +- lib/routes/twitter/api/web-api/api.ts | 83 ++++++---- lib/routes/weibo/oasis/user.ts | 20 ++- lib/routes/xiaoyuzhou/podcast.ts | 23 ++- lib/routes/xunhupay/index.ts | 22 +-- lib/shims/xxhash-wasm.ts | 80 --------- lib/utils/cache/http.test.ts | 14 ++ lib/utils/cache/http.ts | 11 +- lib/utils/common-config.test.ts | 157 +++++------------- lib/utils/common-config.ts | 85 ++++------ lib/utils/parse-date-in-timezone.test.ts | 32 ++++ lib/utils/parse-date-in-timezone.ts | 14 ++ tests/2048-utils.test.ts | 21 +++ tests/common-config-routes.test.ts | 141 ++++++++++++++++ tests/common-config.worker.test.ts | 40 +++++ tests/fixtures/readhub/timeline.json | 9 + tests/fixtures/readhub/topic.json | 22 +++ tests/readhub.test.ts | 163 +++++++++++++++++++ tests/readhub.worker.test.ts | 2 + tests/source-date-routes.test.ts | 104 ++++++++++++ tests/twitter-web-user.test.ts | 151 +++++++++++++++++ tests/twitter-web-user.worker.test.ts | 2 + tests/xiaoyuzhou-podcast.test.ts | 40 +++++ tests/xxhash-wasm.test.ts | 48 ++++++ tests/xxhash-wasm.worker.test.ts | 2 + tsdown-worker.config.ts | 3 +- vitest.workers.config.ts | 3 +- wrangler.toml | 2 +- 51 files changed, 1198 insertions(+), 464 deletions(-) create mode 100644 lib/routes/2048/utils.ts delete mode 100644 lib/shims/xxhash-wasm.ts create mode 100644 lib/utils/parse-date-in-timezone.test.ts create mode 100644 lib/utils/parse-date-in-timezone.ts create mode 100644 tests/2048-utils.test.ts create mode 100644 tests/common-config-routes.test.ts create mode 100644 tests/common-config.worker.test.ts create mode 100644 tests/fixtures/readhub/timeline.json create mode 100644 tests/fixtures/readhub/topic.json create mode 100644 tests/readhub.test.ts create mode 100644 tests/readhub.worker.test.ts create mode 100644 tests/source-date-routes.test.ts create mode 100644 tests/twitter-web-user.test.ts create mode 100644 tests/twitter-web-user.worker.test.ts create mode 100644 tests/xiaoyuzhou-podcast.test.ts create mode 100644 tests/xxhash-wasm.test.ts create mode 100644 tests/xxhash-wasm.worker.test.ts diff --git a/lib/routes/2048/index.tsx b/lib/routes/2048/index.tsx index b032a471a7c7..64759ad33966 100644 --- a/lib/routes/2048/index.tsx +++ b/lib/routes/2048/index.tsx @@ -6,6 +6,8 @@ import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { getForumUrl } from './utils'; + export const route: Route = { path: '/:id?', categories: ['multimedia'], @@ -64,14 +66,10 @@ async function handler(ctx) { const id = ctx.req.param('id') ?? '3'; const rootUrl = 'https://hjd2048.com'; - // 获取地址发布页指向的 URL - const domainInfo = await cache.tryGet('2048:domainInfo', async () => { + // Resolve the address page before caching the forum URL. + const domainInfo = await cache.tryGet('2048:domainInfo:v2', async () => { const response = await ofetch('https://2048.info'); - const $ = load(response); - const onclickValue = $('.button').first().attr('onclick'); - const targetUrl = onclickValue?.match(/window\.open\('([^']+)'/)?.[1]; - - return { url: new URL(targetUrl!, 'https://2048.info').href }; + return { url: getForumUrl(response) }; }); // 获取重定向后的url const redirectResponse = await ofetch.raw(domainInfo.url); @@ -91,7 +89,7 @@ async function handler(ctx) { 86400, // fixed cookie duration: 24 hours false ); - const currentUrl = `${redirected.url}thread.php?fid-${id}.html`; + const currentUrl = new URL(`thread.php?fid-${id}.html`, redirected.url).href; const response = await ofetch.raw(currentUrl, { headers: { diff --git a/lib/routes/2048/utils.ts b/lib/routes/2048/utils.ts new file mode 100644 index 000000000000..94f652b48b1d --- /dev/null +++ b/lib/routes/2048/utils.ts @@ -0,0 +1,15 @@ +import { load } from 'cheerio'; + +export function getForumUrl(html: string) { + const $ = load(html); + const button = $('.button').first(); + const target = button.attr('href') || button.attr('onclick')?.match(/window\.open\(\s*(['"])(.*?)\1/)?.[2]; + if (!target) { + throw new Error('The 2048 address page did not contain a forum link.'); + } + const url = new URL(target, 'https://2048.info'); + if (!['https:', 'http:'].includes(url.protocol)) { + throw new Error('The 2048 address page contained an unsupported forum URL.'); + } + return url.href; +} diff --git a/lib/routes/aiea/index.ts b/lib/routes/aiea/index.ts index 630e676f9716..26e1060c2fac 100644 --- a/lib/routes/aiea/index.ts +++ b/lib/routes/aiea/index.ts @@ -49,15 +49,12 @@ async function handler(ctx) { return await buildData({ link, url: link, - title: '%title%', - params: { - title: 'AIEA Seminars', - }, + title: 'AIEA Seminars', item: { item: `.seminar-contents .seminar-partWrap:nth-child(${nth_child}) > .seminar-list`, - title: `$('.seminar-list-title > span').text()`, - link: `$('a[href^="/0504"]').attr('href')`, - description: `$('.seminar-list .txt > .title').text()`, + title: ($) => $('.seminar-list-title > span').text(), + link: ($) => $('a[href^="/0504"]').attr('href'), + description: ($) => $('.seminar-list .txt > .title').text(), }, }); } diff --git a/lib/routes/airchina/index.ts b/lib/routes/airchina/index.ts index f6bbf25da6a2..a1266835594b 100644 --- a/lib/routes/airchina/index.ts +++ b/lib/routes/airchina/index.ts @@ -4,6 +4,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import buildData from '@/utils/common-config'; import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.airchina.com.cn'; @@ -36,18 +37,17 @@ async function handler() { const data = await buildData({ link, url: link, - title: '%title%', - description: '%description%', - params: { - title: '国航服务公告', - description: '中国国际航空公司服务公告', - }, + title: '国航服务公告', + description: '中国国际航空公司服务公告', item: { item: '.serviceMsg li', - title: `$('a').text()`, - link: `$('a').attr('href')`, - pubDate: `parseDate($('span').text(), 'YYYY-MM-DD')`, - guid: Buffer.from(`$('a').attr('href')`).toString('base64'), + title: ($) => $('a').text(), + link: ($) => $('a').attr('href'), + pubDate: ($) => parseDate($('span').text(), 'YYYY-MM-DD'), + guid: ($) => { + const href = $('a').attr('href'); + return href ? Buffer.from(href).toString('base64') : undefined; + }, }, }); diff --git a/lib/routes/brooklynmuseum/exhibitions.ts b/lib/routes/brooklynmuseum/exhibitions.ts index 0c55e9efb1bc..64e0676919f4 100644 --- a/lib/routes/brooklynmuseum/exhibitions.ts +++ b/lib/routes/brooklynmuseum/exhibitions.ts @@ -38,9 +38,9 @@ async function handler(ctx) { title: 'Brooklyn Museum - Exhibitions', item: { item: '.exhibitions .image-card', - title: `$('h2 > a, h3 > a').text()`, - link: `$('h2 > a, h3 > a').attr('href')`, - description: `$('h6').text()`, + title: ($) => $('h2 > a, h3 > a').text(), + link: ($) => $('h2 > a, h3 > a').attr('href'), + description: ($) => $('h6').text(), }, }); } diff --git a/lib/routes/eastmoney/report/index.tsx b/lib/routes/eastmoney/report/index.tsx index a85e63967d24..7ab2d84ef3c9 100644 --- a/lib/routes/eastmoney/report/index.tsx +++ b/lib/routes/eastmoney/report/index.tsx @@ -6,7 +6,7 @@ import type { Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; import { getEpsOrPeStr, getRatingChangeStr } from '../utils'; @@ -81,18 +81,18 @@ async function handler(ctx) { return { title: `[${item.orgSName}]${stockName}${item.title}`, link: `${baseUrl}/report/${linkType[category]}` + (category === 'stock' ? `/${item.infoCode}.html` : `.jshtml?encodeUrl=${item.encodeUrl}`), - pubDate: parseDate(item.publishDate), + pubDate: parseDateInTimezone(item.publishDate, 8), author: item.researcher, originItem: item, // temp use }; }); const items = await Promise.all( - list.map((item) => { + list.map(async (item) => { const tempOriginItem = item.originItem; delete item.originItem; // temp use - return cache.tryGet(item.link, async () => { + const cached = await cache.tryGet(item.link, async () => { try { const { data: response } = await got(item.link); const $ = load(response); @@ -158,6 +158,7 @@ async function handler(ctx) { return item; } }); + return { ...cached, pubDate: item.pubDate }; }) ); diff --git a/lib/routes/gov/zhengce/zhengceku.ts b/lib/routes/gov/zhengce/zhengceku.ts index feacba3be277..caf83404a9ba 100644 --- a/lib/routes/gov/zhengce/zhengceku.ts +++ b/lib/routes/gov/zhengce/zhengceku.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/zhengceku/:department', @@ -26,16 +27,13 @@ async function handler(ctx) { return await buildData({ link, url: link, - title: '%title%', + title: ($) => $('.channel_tab > .noline > a').text().trim() + ' - 政府文件库', description: '政府文件库, 当页的所有列表', - params: { - title: `$('.channel_tab > .noline > a').text().trim() + ' - 政府文件库'`, - }, item: { item: '.news_box > .list > ul > li:not(.line)', - title: `$('h4 > a').text()`, - link: `$('h4 > a').attr('href')`, - pubDate: `parseDate($('h4 > .date').text().trim())`, + title: ($) => $('h4 > a').text(), + link: ($) => $('h4 > a').attr('href'), + pubDate: ($) => parseDate($('h4 > .date').text().trim()), }, }); } diff --git a/lib/routes/hellogithub/index.ts b/lib/routes/hellogithub/index.ts index 52c625d50bcd..c0f17b90ecfe 100644 --- a/lib/routes/hellogithub/index.ts +++ b/lib/routes/hellogithub/index.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; const sorts = { featured: '精选', @@ -64,7 +64,7 @@ async function handler(ctx) { title: `${item.name}: ${item.title}`, author: item.author, link: `${rootUrl}/repository/${item.item_id}`, - pubDate: parseDate(item.updated_at), + pubDate: parseDateInTimezone(item.updated_at, 8), name: `${item.author}/${item.name}`, description: item.summary, language: item.primary_lang, diff --git a/lib/routes/i-cable/news.tsx b/lib/routes/i-cable/news.tsx index 7190694b2a70..64eea03fc8f6 100644 --- a/lib/routes/i-cable/news.tsx +++ b/lib/routes/i-cable/news.tsx @@ -6,6 +6,7 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; export const route: Route = { path: '/news/:category?', @@ -67,7 +68,8 @@ async function handler(ctx) { return { title: item.title.rendered, link: item.link, - pubDate: item.date_gmt, + // WordPress date_gmt is UTC even when its string omits the Z suffix. + pubDate: item.date_gmt ? parseDateInTimezone(item.date_gmt, 0) : undefined, description, category: item._embedded['wp:term'][0].map((term) => term.name) ?? [], }; diff --git a/lib/routes/iiilab/index.ts b/lib/routes/iiilab/index.ts index 0c773ea29497..3b5512f88514 100644 --- a/lib/routes/iiilab/index.ts +++ b/lib/routes/iiilab/index.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.iiilab.com/'; @@ -24,19 +25,18 @@ async function handler() { return await buildData({ link, url: link, - title: '%title%', - description: '%description%', - params: { - title: '发现', - description: '人人都是自媒体-发现', - }, + title: '发现', + description: '人人都是自媒体-发现', item: { item: '.aw-common-list > div', - title: `$('a').first().text()`, - link: `$('a').first().attr('href')`, - description: `$('.markitup-box').text()`, - pubDate: `parseDate($('.text-color-999').first().text(), 'YYYY-MM-DD HH:mm')`, - guid: Buffer.from(`$('a').attr('href')`).toString('base64'), + title: ($) => $('a').first().text(), + link: ($) => $('a').first().attr('href'), + description: ($) => $('.markitup-box').text(), + pubDate: ($) => parseDate($('.text-color-999').first().text(), 'YYYY-MM-DD HH:mm'), + guid: ($) => { + const href = $('a').attr('href'); + return href ? Buffer.from(href).toString('base64') : undefined; + }, }, }); } diff --git a/lib/routes/javbus/index.tsx b/lib/routes/javbus/index.tsx index add522313674..c037c4e187fd 100644 --- a/lib/routes/javbus/index.tsx +++ b/lib/routes/javbus/index.tsx @@ -9,7 +9,7 @@ import { ViewType } from '@/types'; import cache from '@/utils/cache'; import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; const toSize = (raw) => { const matches = raw.match(/(\d+(\.\d+)?)(\D\w*)/); @@ -109,14 +109,15 @@ async function handler(ctx) { return { link: $item.attr('href'), guid: $item.find('date').first().text(), - pubDate: parseDate($item.find('date').last().text()), + // This is a calendar release date, represented at UTC midnight. + pubDate: parseDateInTimezone($item.find('date').last().text(), 0), title: '', }; }); items = await Promise.all( - items.map((item) => - cache.tryGet(item.link!, async () => { + items.map(async (item) => { + const cached = await cache.tryGet(item.link!, async () => { const detailResponse = await got({ method: 'get', url: item.link, @@ -206,8 +207,9 @@ async function handler(ctx) { }); return item; - }) - ) + }); + return { ...cached, pubDate: item.pubDate }; + }) ); const title = $('head title').text(); diff --git a/lib/routes/jewishmuseum/exhibitions.ts b/lib/routes/jewishmuseum/exhibitions.ts index e92b79931ecd..b06a7408d459 100644 --- a/lib/routes/jewishmuseum/exhibitions.ts +++ b/lib/routes/jewishmuseum/exhibitions.ts @@ -28,8 +28,8 @@ async function handler() { title: 'Jewish Museums - Exhibitions', item: { item: '#current article.exhibition, #upcoming article, #past article.exhibition', - title: `$('h3').text()`, - link: `$('h3').parent().attr('href')`, + title: ($) => $('h3').text(), + link: ($) => $('h3').parent().attr('href'), }, }); } diff --git a/lib/routes/newmuseum/exhibitions.ts b/lib/routes/newmuseum/exhibitions.ts index 975dac84a552..769f0c148526 100644 --- a/lib/routes/newmuseum/exhibitions.ts +++ b/lib/routes/newmuseum/exhibitions.ts @@ -38,9 +38,9 @@ async function handler(ctx) { title: 'New Museum - Exhibitions', item: { item: '.exh', - title: `$('.exh .title').text()`, - link: `$('.exh > a').attr('href')`, - description: `$('.exh .body-reveal').text()`, + title: ($) => $('.exh .title').text(), + link: ($) => $('.exh > a').attr('href'), + description: ($) => $('.exh .body-reveal').text(), }, }); } diff --git a/lib/routes/oct0pu5/rss.ts b/lib/routes/oct0pu5/rss.ts index 48bea0bd77a6..7d4d3cbec5cd 100644 --- a/lib/routes/oct0pu5/rss.ts +++ b/lib/routes/oct0pu5/rss.ts @@ -31,18 +31,14 @@ async function handler() { return await buildData({ link, url: link, - title: '%title%', - description: '%description%', - params: { - title: '博客', - description: 'Oct0pu5的博客', - }, + title: '博客', + description: 'Oct0pu5的博客', item: { item: '.recent-posts > .recent-post-item', - title: `$('.recent-post-info > a').text()`, - link: `$('.recent-post-info > a').attr('href')`, - description: `$('.recent-post-info > .content').text()`, - pubDate: `Date.parse($('div.recent-post-info > div.article-meta-wrap > span.post-meta-date > time').text().trim())`, + title: ($) => $('.recent-post-info > a').text(), + link: ($) => $('.recent-post-info > a').attr('href'), + description: ($) => $('.recent-post-info > .content').text(), + pubDate: ($) => Date.parse($('div.recent-post-info > div.article-meta-wrap > span.post-meta-date > time').text().trim()), }, }); } diff --git a/lib/routes/polimi/news.ts b/lib/routes/polimi/news.ts index b27909c824df..568fee717e3d 100644 --- a/lib/routes/polimi/news.ts +++ b/lib/routes/polimi/news.ts @@ -2,6 +2,7 @@ import type { Context } from 'hono'; import type { Route } from '@/types'; import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/news/:language?', @@ -21,15 +22,15 @@ async function handler(ctx: Context) { link, url: link, title: 'Polimi News', - params: { - homeLink: 'https://www.polimi.it', - }, item: { item: '.card--editorial-photo', - title: `$('.card-title').text()`, - link: `'%homeLink%' + $('.card-footer a').attr('href')`, - description: `$('.news-bodytext').html()`, - pubDate: `parseDate($('time').attr('datetime'))`, + title: ($) => $('.card-title').text(), + link: ($) => 'https://www.polimi.it' + $('.card-footer a').attr('href'), + description: ($) => $('.news-bodytext').html(), + pubDate: ($) => { + const datetime = $('time').attr('datetime'); + return datetime ? parseDate(datetime) : undefined; + }, }, }); } diff --git a/lib/routes/queshu/book.ts b/lib/routes/queshu/book.ts index 36dc5137b3a8..c44ddc47b4ac 100644 --- a/lib/routes/queshu/book.ts +++ b/lib/routes/queshu/book.ts @@ -23,17 +23,13 @@ async function handler(ctx: Context) { return await buildData({ link, url: link, - title: '%title%', - description: `$('#detail_intro .detail_body').text().trim()`, + title: ($) => $('#book_left > #h1').text().trim() + ' - 单品活动信息 - 缺书网', + description: ($) => $('#detail_intro .detail_body').text().trim(), allowEmpty: true, - params: { - title: `$('#book_left > #h1').text().trim() + ' - 单品活动信息 - 缺书网'`, - host, - }, item: { item: '.stacked.right_state > a', - title: `$('.right_item .bodycol_258').text()`, - link: `'%host%' + $('.right_item').parent().attr('href')`, + title: ($) => $('.right_item .bodycol_258').text(), + link: ($) => host + $('.right_item').parent().attr('href'), }, }); } diff --git a/lib/routes/queshu/sale.ts b/lib/routes/queshu/sale.ts index 75644d691568..5678f2010dd1 100644 --- a/lib/routes/queshu/sale.ts +++ b/lib/routes/queshu/sale.ts @@ -17,16 +17,20 @@ async function handler() { return await buildData({ link, url: link, - title: '%title%', - params: { - title: '图书促销 - 缺书网', - host, - }, + title: '图书促销 - 缺书网', item: { item: '#tb_sale tr', - title: `$('.news_sale_detail .sale_btn').text() + ':' + $('.news_sale_title a').text()`, - link: `'%host%' + $('.news_sale_title a').attr('href')`, - description: `$('.news_sale_detail .sale_btn').text() + ':' + $('.news_sale_title a').text() + '
    ' + $('.news_sale_detail .sale_time_end').first().text() + '
    ' + '发布时间:' + $('.news_sale_detail .sale_time_end.inline_right').text()`, + title: ($) => $('.news_sale_detail .sale_btn').text() + ':' + $('.news_sale_title a').text(), + link: ($) => host + $('.news_sale_title a').attr('href'), + description: ($) => + $('.news_sale_detail .sale_btn').text() + + ':' + + $('.news_sale_title a').text() + + '
    ' + + $('.news_sale_detail .sale_time_end').first().text() + + '
    ' + + '发布时间:' + + $('.news_sale_detail .sale_time_end.inline_right').text(), }, }); } diff --git a/lib/routes/readhub/daily.ts b/lib/routes/readhub/daily.ts index 00e0f8806c3b..4ef4966e1c9c 100644 --- a/lib/routes/readhub/daily.ts +++ b/lib/routes/readhub/daily.ts @@ -59,7 +59,7 @@ async function handler(ctx) { return { item: items, title: `${author} - ${route.name}`, - link: currentUrl, + link: infoUrl, description: $('meta[name="description"]').prop('content'), language: 'zh' as const satisfies Language, image, diff --git a/lib/routes/readhub/index.ts b/lib/routes/readhub/index.ts index 55836a96652b..9df017ab3fb6 100644 --- a/lib/routes/readhub/index.ts +++ b/lib/routes/readhub/index.ts @@ -56,9 +56,9 @@ async function handler(ctx) { rootUrl, }), author: item.siteNameDisplay, - category: [...(item.entityList.map((c) => c.name) ?? []), ...(item.tagList.map((c) => c.name) ?? [])], + category: [...(item.entityList?.map((c) => c.name) ?? []), ...(item.tagList?.map((c) => c.name) ?? [])], guid: item.uid, - pubDate: parseDate(item.publishDate), + pubDate: item.publishDate ? parseDate(item.publishDate) : undefined, })); items = await processItems(items); diff --git a/lib/routes/readhub/templates/description.tsx b/lib/routes/readhub/templates/description.tsx index 3c716a045169..cddb48dc2ea3 100644 --- a/lib/routes/readhub/templates/description.tsx +++ b/lib/routes/readhub/templates/description.tsx @@ -1,6 +1,9 @@ import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc.js'; import { renderToString } from 'hono/jsx/dom/server'; +dayjs.extend(utc); + type NewsItem = { url?: string; title?: string; @@ -28,7 +31,7 @@ export const renderDescription = ({ description, news, timeline, rootUrl }: Desc renderToString( <> {description ?

    {description}

    : null} - {news ? ( + {news?.length ? ( <>

    媒体报道

    @@ -47,7 +50,7 @@ export const renderDescription = ({ description, news, timeline, rootUrl }: Desc
    ) : null} - {timeline ? ( + {timeline?.topics?.length ? ( <>

    事件追踪

    @@ -55,7 +58,7 @@ export const renderDescription = ({ description, news, timeline, rootUrl }: Desc {timeline.topics?.map((topic, index) => (
    - {topic.publishDate ? dayjs(topic.publishDate).format('YYYY-MM-DD HH:mm:ss') : ''} + {topic.publishDate ? dayjs(topic.publishDate).utcOffset(8).format('YYYY-MM-DD HH:mm:ss') : ''} {topic.title} diff --git a/lib/routes/readhub/util.ts b/lib/routes/readhub/util.ts index 5f0d4a183616..a7b5fd3dd30a 100644 --- a/lib/routes/readhub/util.ts +++ b/lib/routes/readhub/util.ts @@ -1,3 +1,6 @@ +import { z } from 'zod'; + +import type { DataItem } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -8,45 +11,77 @@ const domain = 'readhub.cn'; const rootUrl = `https://${domain}`; const apiRootUrl = `https://api.${domain}`; const apiTopicUrl = new URL('topic/list', apiRootUrl).href; +const apiDetailUrl = new URL('topic/detail', apiRootUrl).href; +const apiTimelineUrl = new URL('topic/timeline/list', apiRootUrl).href; + +const namedEntry = z.object({ name: z.string() }); +const newsSchema = z.object({ url: z.url(), title: z.string(), siteNameDisplay: z.string().nullish() }); +const topicSchema = z.object({ + uid: z.string().min(1), + title: z.string().min(1), + summary: z.string().min(1), + url: z.url().nullish(), + publishDate: z.iso.datetime({ offset: true }), + siteNameDisplay: z.string().nullish(), + newsAggList: z.array(newsSchema), + entityList: z.array(namedEntry).nullish(), + tagList: z.array(namedEntry).nullish(), +}); +const timelineEntrySchema = z.object({ uid: z.string().min(1), title: z.string(), publishDate: z.iso.datetime({ offset: true }).nullish() }); +const timelineDataSchema = z.object({ items: z.array(timelineEntrySchema) }); +const topicDataSchema = z.object({ items: z.array(topicSchema) }); +const timelineSchema = z.object({ data: timelineDataSchema }); +const detailSchema = z.object({ data: topicDataSchema }); + +type ReadhubItem = DataItem & { link: string; guid: string }; + +async function fetchTopic(uid: string): Promise { + const { data: detailResponse } = await got(apiDetailUrl, { searchParams: { uid } }); + const detail = detailSchema.safeParse(detailResponse); + if (!detail.success) { + throw new Error('Readhub topic detail response is invalid or missing publication dates'); + } + const matches = detail.data.data.items.filter((topic) => topic.uid === uid); + if (matches.length !== 1) { + throw new Error('Readhub topic detail response does not contain the requested topic'); + } + const topic = matches[0]; + + const { data: timelineResponse } = await got(apiTimelineUrl, { searchParams: { topic_uid: uid, size: 10 } }); + const timeline = timelineSchema.safeParse(timelineResponse); + if (!timeline.success) { + throw new Error('Readhub topic timeline response is invalid'); + } + + return { + title: topic.title, + link: topic.url ?? new URL(`topic/${topic.uid}`, rootUrl).href, + description: renderDescription({ + description: topic.summary, + news: topic.newsAggList.map((news) => ({ ...news, siteNameDisplay: news.siteNameDisplay ?? undefined })), + timeline: { topics: timeline.data.data.items.map((entry) => ({ ...entry, publishDate: entry.publishDate ?? undefined })) }, + rootUrl, + }), + author: topic.siteNameDisplay ?? undefined, + category: [...(topic.entityList ?? []), ...(topic.tagList ?? [])].map((entry) => entry.name), + guid: `readhub-${topic.uid}`, + pubDate: parseDate(topic.publishDate), + }; +} + +const processItems = (items: ReadhubItem[]): Promise => + Promise.all( + items.map((item) => { + const url = new URL(item.link); + const topicPath = /^\/topic\/([^/]+)\/?$/.exec(url.pathname); + if (url.origin !== rootUrl || !topicPath) { + return { ...item, guid: `readhub-${item.guid}` }; + } -/** - * Process items asynchronously. - * - * @param {Array} items - The array of items to process. - * @returns {Promise>} Returns a Promise that resolves to an array of processed items. - */ -const processItems = async (items) => - await Promise.all( - items.map((item) => - cache.tryGet(item.link, async () => { - try { - if (!item.link.startsWith(rootUrl)) { - throw new Error(`"${item.link}" is an external URL`); - } - - const { data: detailResponse } = await got(item.link); - - const data = JSON.parse(detailResponse.match(/\{\\"topic\\":(.*?)\}\]\\n"\]\)<\/script>/)[1].replaceAll(String.raw`\"`, '"')); - - item.title = data.title; - item.link = data.url ?? new URL(`topic/${data.uid}`, rootUrl).href; - item.description = renderDescription({ - description: data.summary, - news: data.newsAggList, - timeline: data.timeline, - rootUrl, - }); - item.author = data.siteNameDisplay; - item.category = [...(data.entityList.map((c) => c.name) ?? []), ...(data.tagList.map((c) => c.name) ?? [])]; - item.guid = `readhub-${data.uid}`; - item.pubDate = parseDate(data.publishDate.replaceAll(/\s/g, '')); - } catch { - item.guid = `readhub-${item.guid}`; - } - - return item; - }) - ) + // Keep failed enrichment out of the cache, including old summary-only entries. + const uid = decodeURIComponent(topicPath[1]); + return cache.tryGet(`readhub:topic:v2:${uid}`, () => fetchTopic(uid)); + }) ); export { apiRootUrl, apiTopicUrl, processItems, rootUrl }; diff --git a/lib/routes/smzdm/keyword.ts b/lib/routes/smzdm/keyword.ts index acf3a0938091..69e007b76214 100644 --- a/lib/routes/smzdm/keyword.ts +++ b/lib/routes/smzdm/keyword.ts @@ -5,10 +5,8 @@ import ConfigNotFoundError from '@/errors/types/config-not-found'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import got from '@/utils/got'; -import { parseDate } from '@/utils/parse-date'; -import timezone from '@/utils/timezone'; -import { getHeaders } from './utils'; +import { getHeaders, parseSearchDate } from './utils'; export const route: Route = { path: '/keyword/:keyword', @@ -63,14 +61,15 @@ async function handler(ctx) { return { title: `${keyword} - 什么值得买`, link: `https://search.smzdm.com/?c=home&s=${encodeURIComponent(keyword)}&order=time`, - item: - list && - list.toArray().map((item) => { + item: list + .toArray() + .filter((item) => $(item).find('.feed-block-title a').first().attr('href')) + .map((item) => { const $item = $(item); return { title: `${$item.find('.feed-block-title a').eq(0).text().trim()} - ${$item.find('.feed-block-title a').eq(1).text().trim()}`, description: `${$item.find('.feed-block-descripe').contents().eq(2).text().trim()}
    ${$item.find('.feed-block-extras span').text().trim()}
    `, - pubDate: timezone(parseDate($item.find('.feed-block-extras').contents().eq(0).text().trim(), ['MM-DD HH:mm', 'HH:mm']), 8), + pubDate: parseSearchDate($item.find('.feed-block-extras').contents().eq(0).text().trim()), link: $item.find('.feed-block-title a').attr('href'), }; }), diff --git a/lib/routes/smzdm/utils.ts b/lib/routes/smzdm/utils.ts index 005e4fd343fc..3df7a5b6b77e 100644 --- a/lib/routes/smzdm/utils.ts +++ b/lib/routes/smzdm/utils.ts @@ -1,4 +1,17 @@ import { config } from '@/config'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; + +export const parseSearchDate = (value: string, now = new Date()): Date | undefined => { + const today = new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString().slice(0, 10); + if (/^\d{2}:\d{2}$/.test(value)) { + return parseDateInTimezone(`${today}T${value}`, 8); + } + if (/^\d{2}-\d{2} \d{2}:\d{2}$/.test(value)) { + // Search results omit the year; a later month/day belongs to last year. + const year = Number(today.slice(0, 4)) - Number(value.slice(0, 5) > today.slice(5)); + return parseDateInTimezone(`${year}-${value}`, 8); + } +}; export const getHeaders = () => ({ accept: 'application/json, text/javascript, */*; q=0.01', diff --git a/lib/routes/stratechery/index.ts b/lib/routes/stratechery/index.ts index 21cdfba0857d..76498d46d6d6 100644 --- a/lib/routes/stratechery/index.ts +++ b/lib/routes/stratechery/index.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/', @@ -21,10 +22,13 @@ async function handler() { description: 'Stratechery provides analysis of the strategy and business side of technology and media, and the impact of technology on society. ', item: { item: 'article', - title: `$('article > header > h1 > a').text()`, - link: `$('article > header > h1 > a').attr('href')`, - pubDate: `parseDate($('article .entry-date').attr('datetime'))`, - description: `$('article > .entry-content').html().replace(/%/g, '%')`, + title: ($) => $('article > header > h1 > a').text(), + link: ($) => $('article > header > h1 > a').attr('href'), + pubDate: ($) => { + const datetime = $('article .entry-date').attr('datetime'); + return datetime ? parseDate(datetime) : undefined; + }, + description: ($) => $('article > .entry-content').html()!.replaceAll('%', '%'), }, }); } diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts index 7bfe5c75b015..280db0c7e895 100644 --- a/lib/routes/twitter/api/web-api/api.ts +++ b/lib/routes/twitter/api/web-api/api.ts @@ -8,8 +8,30 @@ import { baseUrl, gqlFeatures, gqlMap, initGqlMap } from './constants'; import type { ApiParams } from './utils'; import { gatherLegacyFromData, paginationTweets, twitterGot } from './utils'; -const getUserData = (id) => - cache.tryGet(`twitter-userdata-${id}`, () => { +const getUserResult = (response: any) => { + const errors = response?.errors; + if (errors !== undefined && errors !== null && (!Array.isArray(errors) || errors.length > 0)) { + const codes = Array.isArray(errors) ? [...new Set(errors.map((error) => error?.code).filter((code) => Number.isSafeInteger(code)))] : []; + throw new Error(`Twitter API user lookup failed${codes.length ? ` (codes: ${codes.join(', ')})` : ''}`); + } + + const user = response?.data?.user; + if (user === null || user?.result === null) { + throw new InvalidParameterError("This account doesn't exist"); + } + const result = user?.result; + if (result?.__typename === 'UserUnavailable') { + throw new InvalidParameterError('Twitter user is unavailable'); + } + if (!result || typeof result.rest_id !== 'string' || result.rest_id.length === 0) { + throw new Error('Twitter API returned an incomplete user response'); + } + return result; +}; + +const getUserData = async (id: string) => { + // Other Twitter API adapters cache different schemas under the legacy key. + const data = await cache.tryGet(`twitter:web:userdata:v2:${id}`, async () => { const params = { variables: id.startsWith('+') ? JSON.stringify({ @@ -26,31 +48,28 @@ const getUserData = (id) => }), }; - if (config.twitter.thirdPartyApi) { - const endpoint = id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName; - - return ofetch(`${config.twitter.thirdPartyApi}${endpoint}`, { - method: 'GET', - params, - headers: { - 'accept-encoding': 'gzip', - }, - }); - } - - return twitterGot(`${baseUrl}${id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName}`, params, { - allowNoAuth: !id.startsWith('+'), - }); + const response = config.twitter.thirdPartyApi + ? await ofetch(`${config.twitter.thirdPartyApi}${id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName}`, { + method: 'GET', + params, + headers: { + 'accept-encoding': 'gzip', + }, + }) + : await twitterGot(`${baseUrl}${id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName}`, params, { + allowNoAuth: !id.startsWith('+'), + }); + + // HTTP 200 can still contain GraphQL errors; never cache those responses. + getUserResult(response); + return response; }); + return getUserResult(data); +}; const cacheTryGet = async (_id, params, operationName, func) => { - const userData: any = await getUserData(_id); - const id = userData.data?.user?.result?.rest_id; - if (id === undefined) { - cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire); - throw new InvalidParameterError('User not found'); - } - return cache.tryGet(getTwitterUserCacheKey(id, operationName, params), () => func(id, params), config.cache.routeExpire, false); + const user = await getUserData(_id); + return cache.tryGet(getTwitterUserCacheKey(user.rest_id, operationName, params), () => func(user.rest_id, params), config.cache.routeExpire, false); }; const getUserTweets = (id: string, params?: ApiParams) => @@ -164,19 +183,11 @@ const getList = async (id: string, params?: ApiParams) => ); const getUser = async (id: string) => { - const userData: any = await getUserData(id); - - if (!userData.data.user) { - throw new InvalidParameterError("This account doesn't exist"); - } - if (userData.data.user.result.__typename === 'UserUnavailable') { - throw new InvalidParameterError(userData.data.user.result.message || 'User is unavailable'); - } - + const user = await getUserData(id); return { - profile_image_url: userData.data?.user?.result?.avatar?.image_url, - description: userData.data?.user?.result?.profile_bio?.description, - ...userData.data?.user?.result?.core, + profile_image_url: user.avatar?.image_url, + description: user.profile_bio?.description, + ...user.core, }; }; diff --git a/lib/routes/weibo/oasis/user.ts b/lib/routes/weibo/oasis/user.ts index 078c3346b932..47258066a3c1 100644 --- a/lib/routes/weibo/oasis/user.ts +++ b/lib/routes/weibo/oasis/user.ts @@ -34,13 +34,23 @@ async function handler(ctx) { await buildData({ link, url: link, - title: `$('.name-main').text().trim() + ' - 用户 - 绿洲'`, - description: `$('.desc').text().trim()`, + title: ($) => $('.name-main').text().trim() + ' - 用户 - 绿洲', + description: ($) => $('.desc').text().trim(), item: { item: '.container .status-item', - title: `$('.status-item-title').contents().filter((_, node) => node.type === 'text').text()`, - description: `$('.status-item-title').contents().filter((_, node) => node.type === 'text').text() + '
    ' + $('.status-img').html()`, - link: `'https://oasis.weibo.cn/v1/h5/share?sid=' + $('.status-item-title').parent().data('id')`, + title: ($) => + $('.status-item-title') + .contents() + .filter((_, node) => node.type === 'text') + .text(), + description: ($) => + $('.status-item-title') + .contents() + .filter((_, node) => node.type === 'text') + .text() + + '
    ' + + $('.status-img').html(), + link: ($) => 'https://oasis.weibo.cn/v1/h5/share?sid=' + $('.status-item-title').parent().data('id'), }, }) ); diff --git a/lib/routes/xiaoyuzhou/podcast.ts b/lib/routes/xiaoyuzhou/podcast.ts index 48e86d83fb3b..4300f3383c85 100644 --- a/lib/routes/xiaoyuzhou/podcast.ts +++ b/lib/routes/xiaoyuzhou/podcast.ts @@ -44,14 +44,16 @@ async function handler(ctx) { response = await ofetch(link); $ = load(response); - const nextDataElement = $('#__NEXT_DATA__').get(0); - page_data = JSON.parse(nextDataElement.children[0].data); - - // If no episodes found, we should try episode URL - if (!page_data.props.pageProps.podcast?.episodes) { - throw new Error('No episodes found in podcast data'); + page_data = JSON.parse($('#__NEXT_DATA__').text()); + } catch (error) { + // An episode ID may return 404 at the podcast URL. Preserve access and + // transport errors instead of hiding them behind a second failed request. + if (!(error instanceof Error) || !('status' in error) || error.status !== 404) { + throw error; } - } catch { + } + + if (!page_data?.props?.pageProps?.podcast?.episodes) { // Try as episode instead link = `https://www.xiaoyuzhoufm.com/episode/${id}`; response = await ofetch(link); @@ -65,11 +67,14 @@ async function handler(ctx) { response = await ofetch(link); $ = load(response); - const nextDataElement = $('#__NEXT_DATA__').get(0); - page_data = JSON.parse(nextDataElement.children[0].data); + page_data = JSON.parse($('#__NEXT_DATA__').text()); } } + if (!page_data?.props?.pageProps?.podcast?.episodes) { + throw new Error('Xiaoyuzhou did not return podcast data for this podcast or episode ID.'); + } + let episodes = page_data.props.pageProps.podcast.episodes.map((item) => ({ title: item.title, enclosure_url: item.enclosure.url, diff --git a/lib/routes/xunhupay/index.ts b/lib/routes/xunhupay/index.ts index a9ad383df916..a02c679fae9e 100644 --- a/lib/routes/xunhupay/index.ts +++ b/lib/routes/xunhupay/index.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.xunhupay.com'; @@ -32,19 +33,18 @@ async function handler() { return await buildData({ link, url: link, - title: '%title%', - description: '%description%', - params: { - title: '博客', - description: '虎皮椒-博客', - }, + title: '博客', + description: '虎皮椒-博客', item: { item: '.blog-post > article', - title: `$('h5').text()`, - link: `$('a').attr('href')`, - description: `$('.content').text()`, - pubDate: `parseDate($('.date').text(), 'YYYY-MM-DD')`, - guid: Buffer.from(`$('a').attr('href')`).toString('base64'), + title: ($) => $('h5').text(), + link: ($) => $('a').attr('href'), + description: ($) => $('.content').text(), + pubDate: ($) => parseDate($('.date').text(), 'YYYY-MM-DD'), + guid: ($) => { + const href = $('a').attr('href'); + return href ? Buffer.from(href).toString('base64') : undefined; + }, }, }); } diff --git a/lib/shims/xxhash-wasm.ts b/lib/shims/xxhash-wasm.ts deleted file mode 100644 index e9aefdb49f76..000000000000 --- a/lib/shims/xxhash-wasm.ts +++ /dev/null @@ -1,80 +0,0 @@ -// oxlint-disable unicorn/prefer-math-trunc unicorn-js/no-this-outside-of-class -// xxhash-wasm shim for Cloudflare Workers -// Uses Web Crypto API instead of WebAssembly - -type XXHash = { - update(input: string | Uint8Array): XXHash; - digest(): T; -}; - -type XXHashAPI = { - h32(input: string, seed?: number): number; - h32ToString(input: string, seed?: number): string; - h32Raw(inputBuffer: Uint8Array, seed?: number): number; - create32(seed?: number): XXHash; - h64(input: string, seed?: bigint): bigint; - h64ToString(input: string, seed?: bigint): string; - h64Raw(inputBuffer: Uint8Array, seed?: bigint): bigint; - create64(seed?: bigint): XXHash; -}; - -const encoder = new TextEncoder(); - -// Simple sync hash for h32 methods (fallback) -const simpleHash32 = (input: Uint8Array, seed = 0): number => { - let hash = seed; - for (const byte of input) { - hash = Math.trunc((hash << 5) - hash + byte); - } - return hash >>> 0; -}; - -function xxhash(): XXHashAPI { - return { - h32: (input: string, seed?: number): number => simpleHash32(encoder.encode(input), seed), - h32ToString: (input: string, seed?: number): string => simpleHash32(encoder.encode(input), seed).toString(16).padStart(8, '0'), - h32Raw: (inputBuffer: Uint8Array, seed?: number): number => simpleHash32(inputBuffer, seed), - create32: (seed?: number): XXHash => { - const chunks: Uint8Array[] = []; - return { - update(input: string | Uint8Array) { - chunks.push(input instanceof Uint8Array ? input : encoder.encode(input)); - return this; - }, - digest() { - const totalLength = chunks.reduce((sum, arr) => sum + arr.length, 0); - const combined = new Uint8Array(totalLength); - let offset = 0; - for (const chunk of chunks) { - combined.set(chunk, offset); - offset += chunk.length; - } - return simpleHash32(combined, seed); - }, - }; - }, - // h64 methods use async SHA-256 but return sync - this is a limitation - // In practice, only h64ToString is used and it's called with await xxhash() - h64: (_input: string, _seed?: bigint): bigint => { - throw new Error('h64 is not supported in Worker shim, use h64ToString instead'); - }, - h64ToString: (input: string, _seed?: bigint): string => { - // This needs to be sync to match the API, but we use a simple hash - // The actual usage in cache.ts awaits xxhash() first, so this works - let hash = 0n; - const data = encoder.encode(input); - for (const byte of data) { - hash = ((hash << 5n) - hash + BigInt(byte)) & 0xff_ff_ff_ff_ff_ff_ff_ffn; - } - return hash.toString(16).padStart(16, '0'); - }, - h64Raw: (_inputBuffer: Uint8Array, _seed?: bigint): bigint => { - throw new Error('h64Raw is not supported in Worker shim'); - }, - create64: (_seed?: bigint): XXHash => { - throw new Error('create64 is not supported in Worker shim'); - }, - }; -} - -export default xxhash; diff --git a/lib/utils/cache/http.test.ts b/lib/utils/cache/http.test.ts index ea7637f20a63..acea7ea59cbc 100644 --- a/lib/utils/cache/http.test.ts +++ b/lib/utils/cache/http.test.ts @@ -117,4 +117,18 @@ describe('http cache module', () => { expect(cache.status.available).toBe(true); expect(urls[0]).not.toContain('?refresh=1'); }); + + it('reports Cloudflare routing errors instead of treating them as cache misses', async () => { + setHttpCacheEnv(); + const { errorSpy } = await spyOnLogger(); + vi.stubGlobal( + 'fetch', + vi.fn(() => new Response('error code: 1042', { status: 404 })) + ); + const cache = (await import('@/utils/cache/http')).default; + cache.init(); + + await expect(cache.get('missing')).resolves.toBeNull(); + expect(errorSpy).toHaveBeenCalledWith('HTTP cache get returned HTTP 404 without a cache miss response.'); + }); }); diff --git a/lib/utils/cache/http.ts b/lib/utils/cache/http.ts index 5b876cf7e951..c1bbc9737f2e 100644 --- a/lib/utils/cache/http.ts +++ b/lib/utils/cache/http.ts @@ -106,7 +106,16 @@ export default { } if (response.status === 404) { - return ''; + try { + const data = await response.json(); + if (data && typeof data === 'object' && 'hit' in data && data.hit === false) { + return ''; + } + } catch { + // Cloudflare routing errors can also return 404, without reaching the cache service. + } + logger.error('HTTP cache get returned HTTP 404 without a cache miss response.'); + return null; } if (!response.ok) { diff --git a/lib/utils/common-config.test.ts b/lib/utils/common-config.test.ts index 6cf3b60358f1..3a4a5d08dd87 100644 --- a/lib/utils/common-config.test.ts +++ b/lib/utils/common-config.test.ts @@ -2,128 +2,61 @@ import iconv from 'iconv-lite'; import { http, HttpResponse } from 'msw'; import { describe, expect, it } from 'vitest'; -import configUtils, { getProp, replaceParams, transElemText } from '@/utils/common-config'; - -describe('index', () => { - it('transElemText', () => { - const $ = () => 'RSSHub'; - expect(transElemText($, '$()')).toBe('RSSHub'); - }); - - it('replaceParams', () => { - const $ = () => 'RSSHub'; - const data = { - params: { - title: 'RSSHub', - }, - title: '%title%', - }; - expect(replaceParams(data, data.title, $)).toBe('RSSHub'); - }); - - it('getProp', () => { - const $ = () => 'RSSHub'; - const data = { - title: 'RSSHub', - }; - expect(getProp(data, ['title'], $)).toBe('RSSHub'); - expect(getProp(data, 'title', $)).toBe('RSSHub'); - }); - - it('all', () => { - const $ = () => 'RSSHub'; - const data = { - params: { - title: '$()', - }, - title: '%title%', - }; - expect(getProp(data, ['title'], $)).toBe('RSSHub'); - }); - - it('buildData', async () => { - const data = await configUtils({ - link: 'http://rsshub.test/buildData', - url: 'http://rsshub.test/buildData', - title: '%title%', - params: { - title: 'buildData', - }, +import buildData from '@/utils/common-config'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const url = 'http://rsshub.test/buildData'; + +describe('common-config', () => { + it('extracts page and item values with callbacks and preserves literal text', async () => { + const suffix = 'buildData'; + const literal = `$('script').text() %one% + %two%`; + const data = await buildData({ + link: url, + url, + title: ($) => $('.content li a').first().text() + ' - ' + suffix, + description: literal, item: { item: '.content li', - title: `$('a').text() + ' - %title%'`, - link: `$('a').attr('href')`, - description: `$('.description').html()`, - pubDate: `timezone(parseDate($('.date').text(), 'YYYY-MM-DD'), 0)`, + title: ($) => $('a').text() + ' - ' + suffix, + link: ($) => $('a').attr('href'), + description: ($) => $('.description').html(), + pubDate: ($) => timezone(parseDate($('.date').text(), 'YYYY-MM-DD'), 0), }, }); - - expect(data).toMatchObject({ - link: 'http://rsshub.test/buildData', - title: 'buildData', + expect(data).toEqual({ + link: url, + title: '1 - buildData', + description: literal, + allowEmpty: false, item: [ - { - description: 'RSSHub1', - guid: undefined, - link: '/1', - pubDate: new Date('2025-01-01T00:00:00Z'), - title: '1 - buildData', - }, - { - description: 'RSSHub2', - guid: undefined, - link: '/2', - pubDate: new Date('2025-01-02T00:00:00Z'), - title: '2 - buildData', - }, + { title: '1 - buildData', link: '/1', description: 'RSSHub1', pubDate: new Date('2025-01-01T00:00:00Z'), guid: undefined }, + { title: '2 - buildData', link: '/2', description: 'RSSHub2', pubDate: new Date('2025-01-02T00:00:00Z'), guid: undefined }, ], }); }); -}); - -describe('charset', () => { - const html = `
    -
      -
    • - 1 -
      中文RSSHub1
      -
      2025-01-01
      -
    • -
    -
    `; - it('parses charset from content-type', async () => { - const { default: server } = await import('@/setup.test'); - server.use( - http.get( - 'http://rsshub.test/buildData-gbk', - () => - new HttpResponse(iconv.encode(html, 'gbk'), { - headers: { - 'content-type': 'text/html; charset=gbk', - }, - }) - ) - ); - - const data = await configUtils({ - link: 'http://rsshub.test/buildData-gbk', - url: 'http://rsshub.test/buildData-gbk', - title: '%title%', - params: { - title: 'buildData', - }, - item: { - item: '.content li', - title: `$('a').text() + ' - %title%'`, - link: `$('a').attr('href')`, - description: `$('.description').html()`, - pubDate: `timezone(parseDate($('.date').text(), 'YYYY-MM-DD'), 0)`, - }, + it('preserves constants, numeric dates, missing fields and allowEmpty', async () => { + const date = 1_735_689_600_000; + const data = await buildData({ + url, + title: 'Feed', + allowEmpty: true, + item: { item: '.content li', title: 'Item', description: undefined, pubDate: date }, }); + expect(data.allowEmpty).toBe(true); + expect(data.item).toEqual(Array.from({ length: 2 }, () => ({ title: 'Item', description: undefined, pubDate: date, link: undefined, guid: undefined }))); + const empty = await buildData({ url, title: 'Empty', item: { item: '.missing', title: 'Item' } }); + expect(empty.item).toEqual([]); + expect(empty.allowEmpty).toBe(false); + }); - expect(data.title).toBe('buildData'); - expect(data.item[0].title).toBe('1 - buildData'); - expect(data.item[0].description).toBe('中文RSSHub1'); + it('decodes non-UTF-8 responses before calling selectors', async () => { + const { default: server } = await import('@/setup.test'); + server.use(http.get('http://rsshub.test/buildData-gbk', () => new HttpResponse(iconv.encode('

    中文标题

    ', 'gbk'), { headers: { 'content-type': 'text/html; charset=gbk' } }))); + const data = await buildData({ url: 'http://rsshub.test/buildData-gbk', title: ($) => $('h1').text(), item: { item: 'li', title: ($) => $('a').text(), link: ($) => $('a').attr('href') } }); + expect(data.title).toBe('中文标题'); + expect(data.item[0]).toMatchObject({ title: '中文条目', link: '/1' }); }); }); diff --git a/lib/utils/common-config.ts b/lib/utils/common-config.ts index 3de6f7cc683b..46481d61de69 100644 --- a/lib/utils/common-config.ts +++ b/lib/utils/common-config.ts @@ -1,52 +1,29 @@ -import { load } from 'cheerio'; +import { type Cheerio, load } from 'cheerio'; +import type { AnyNode } from 'domhandler'; import iconv from 'iconv-lite'; +import type { Data, DataItem } from '@/types'; import ofetch from '@/utils/ofetch'; -import { parseDate as _parseDate } from '@/utils/parse-date'; -import _timezone from '@/utils/timezone'; -function transElemText($, prop) { - const regex = /\$\(.*\)/; - let result = prop; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const parseDate = _parseDate; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const timezone = _timezone; - if (regex.test(result)) { - // eslint-disable-next-line no-eval - result = eval(result); - } - return result; -} +type Selector = (selector: string) => Cheerio; +type Value = T | ((select: Selector) => T); +type ItemFields = Pick; -function replaceParams(data, prop, $) { - const regex = /%(.*)%/g; - let result = prop; - let group = regex.exec(prop); - while (group) { - // FIXME Multi vars - result = result.replace(group[0], () => transElemText($, data.params[group![1]])); - group = regex.exec(prop); - } - return result; -} +type BuildDataConfig = Pick & { + url: string; + title: Value; + description?: Value; + item: { item: string } & { [Key in keyof ItemFields]: Value }; +}; -function getProp(data, prop, $) { - let result = data; - if (Array.isArray(prop)) { - for (const e of prop) { - result = transElemText($, result[e]); - } - } else { - result = transElemText($, result[prop]); - } - return replaceParams(data, result, $); +function resolveValue(value: Value, select: Selector): T { + return typeof value === 'function' ? (value as (select: Selector) => T)(select) : value; } -export default async function buildData(data) { +export default async function buildData(data: BuildDataConfig) { const response = await ofetch.raw(data.url); const contentType = response.headers.get('content-type') || ''; - // 若没有指定编码,则默认utf-8 + // Default to UTF-8 when no encoding is specified. let charset = 'utf-8'; for (const attr of contentType.split(';')) { if (attr.includes('charset=')) { @@ -55,24 +32,22 @@ export default async function buildData(data) { } const responseData = charset === 'utf-8' ? response._data : iconv.decode(Buffer.from(await ofetch(data.url, { responseType: 'arrayBuffer' })), charset); const $ = load(responseData); - const $item = $(data.item.item); - // 这里应该是可以通过参数注入一些代码的,不过应该无伤大雅 return { link: data.link, - title: getProp(data, 'title', $), - description: getProp(data, 'description', $), + title: resolveValue(data.title, $), + description: resolveValue(data.description, $), allowEmpty: data.allowEmpty || false, - item: $item.toArray().map((e) => { - const $elem = (selector) => $(e).find(selector); - return { - title: getProp(data, ['item', 'title'], $elem), - description: getProp(data, ['item', 'description'], $elem), - pubDate: getProp(data, ['item', 'pubDate'], $elem), - link: getProp(data, ['item', 'link'], $elem), - guid: getProp(data, ['item', 'guid'], $elem), - }; - }), + item: $(data.item.item) + .toArray() + .map((element) => { + const select: Selector = (selector) => $(element).find(selector); + return { + title: resolveValue(data.item.title, select), + description: resolveValue(data.item.description, select), + pubDate: resolveValue(data.item.pubDate, select), + link: resolveValue(data.item.link, select), + guid: resolveValue(data.item.guid, select), + }; + }), }; } - -export { getProp, replaceParams, transElemText }; diff --git a/lib/utils/parse-date-in-timezone.test.ts b/lib/utils/parse-date-in-timezone.test.ts new file mode 100644 index 000000000000..fec997814f36 --- /dev/null +++ b/lib/utils/parse-date-in-timezone.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; + +import { parseDateInTimezone } from './parse-date-in-timezone'; + +describe('parseDateInTimezone', () => { + it.each([ + ['2026-08-28T08:07:22', 8, '2026-08-28T00:07:22.000Z'], + ['2026-09-07 00:00:00.000', 8, '2026-09-06T16:00:00.000Z'], + ['2026-09-06T15:41:43', 0, '2026-09-06T15:41:43.000Z'], + ['2026-09-18', 0, '2026-09-18T00:00:00.000Z'], + ['2026-09-07T00:00:00', -3.5, '2026-09-07T03:30:00.000Z'], + ['2026-09-07T08:00:00+08:00', 8, '2026-09-07T00:00:00.000Z'], + ['2026-09-07T08:00:00+0800', 0, '2026-09-07T00:00:00.000Z'], + ['2026-09-07T08:00:00-05:00', 8, '2026-09-07T13:00:00.000Z'], + ['2026-09-07T00:00:00Z', 8, '2026-09-07T00:00:00.000Z'], + ['Mon, 07 Sep 2026 08:00:00 GMT+8', 8, '2026-09-07T00:00:00.000Z'], + ])('parses %s in offset %s independently of the host timezone', (input, offset, expected) => { + expect(parseDateInTimezone(input, offset).toISOString()).toBe(expected); + }); + + it('preserves absolute Date and millisecond timestamp inputs without mutating them', () => { + const date = new Date('2026-09-07T00:00:00Z'); + expect(parseDateInTimezone(date, 8).getTime()).toBe(date.getTime()); + expect(parseDateInTimezone(date.getTime(), -5).getTime()).toBe(date.getTime()); + expect(date.toISOString()).toBe('2026-09-07T00:00:00.000Z'); + }); + + it('does not invent a date for an empty or unparseable string', () => { + expect(Number.isNaN(parseDateInTimezone('', 8).getTime())).toBe(true); + expect(Number.isNaN(parseDateInTimezone('unknown', 8).getTime())).toBe(true); + }); +}); diff --git a/lib/utils/parse-date-in-timezone.ts b/lib/utils/parse-date-in-timezone.ts new file mode 100644 index 000000000000..4f77ba81ba61 --- /dev/null +++ b/lib/utils/parse-date-in-timezone.ts @@ -0,0 +1,14 @@ +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; + +dayjs.extend(utc); + +// Interpret a source's wall clock without using the host's timezone. Dates, +// timestamps and ISO strings with an explicit offset already identify an instant. +export const parseDateInTimezone = (date: string | number | Date, offset: number): Date => { + const parsed = dayjs.utc(date); + if (typeof date !== 'string' || /(?:Z|[+-]\d{2}:?\d{2}|(?:GMT|UTC)(?:[+-]\d{1,4})?)$/i.test(date.trim())) { + return parsed.toDate(); + } + return parsed.subtract(offset, 'hour').toDate(); +}; diff --git a/tests/2048-utils.test.ts b/tests/2048-utils.test.ts new file mode 100644 index 000000000000..7891e2a5f8de --- /dev/null +++ b/tests/2048-utils.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; + +import { getForumUrl } from '../lib/routes/2048/utils'; + +describe('2048 forum address discovery', () => { + it('follows the current address page anchor', () => { + expect(getForumUrl('Forum')).toBe('https://2048.info/bbs.php'); + }); + + it('accepts the previous onclick format', () => { + expect(getForumUrl('')).toBe('https://forum.example/'); + }); + + it('rejects missing links before they can be cached as /undefined', () => { + expect(() => getForumUrl('Unavailable')).toThrow('did not contain a forum link'); + }); + + it('rejects script URLs', () => { + expect(() => getForumUrl('Forum')).toThrow('unsupported forum URL'); + }); +}); diff --git a/tests/common-config-routes.test.ts b/tests/common-config-routes.test.ts new file mode 100644 index 000000000000..0d0c0cd54827 --- /dev/null +++ b/tests/common-config-routes.test.ts @@ -0,0 +1,141 @@ +import type { Context } from 'hono'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import type { Data, Route } from '../lib/types'; + +const mocks = vi.hoisted(() => ({ raw: vi.fn(), get: vi.fn() })); +vi.mock('../lib/utils/ofetch', () => ({ default: Object.assign(vi.fn(), { raw: mocks.raw }) })); +vi.mock('../lib/utils/got', () => ({ default: mocks.get })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: (_key: string, load: () => unknown) => load() } })); +vi.mock('../lib/routes/weibo/utils', () => ({ default: { sinaimgTvax: (data: Data) => data } })); + +const fixtures = [ + { + name: 'government policy library', + module: () => import('../lib/routes/gov/zhengce/zhengceku'), + params: { department: 'bmwj' }, + html: '
    ', + title: '部门文件 - 政府文件库', + item: { title: '政策正文', link: '/policy', pubDate: new Date(2025, 0, 1) }, + }, + { + name: 'Polimi', + module: () => import('../lib/routes/polimi/news'), + params: { language: 'en' }, + html: '', + title: 'Polimi News', + item: { title: 'News', link: 'https://www.polimi.it/story', description: 'Body', pubDate: new Date('2025-01-01T00:00:00Z') }, + }, + { + name: 'Jewish Museum', + module: () => import('../lib/routes/jewishmuseum/exhibitions'), + html: '
    ', + title: 'Jewish Museums - Exhibitions', + item: { title: 'Exhibition', link: '/show' }, + }, + { + name: 'Brooklyn Museum', + module: () => import('../lib/routes/brooklynmuseum/exhibitions'), + html: '
    ', + title: 'Brooklyn Museum - Exhibitions', + item: { title: 'Exhibition', link: '/show', description: 'Description' }, + }, + { + name: 'New Museum', + module: () => import('../lib/routes/newmuseum/exhibitions'), + html: '
    Exhibition
    Description
    ', + title: 'New Museum - Exhibitions', + item: { title: 'Exhibition', link: '/show', description: 'Description' }, + }, + { + name: 'Stratechery', + module: () => import('../lib/routes/stratechery/index'), + html: '', + title: 'Stratechery by Ben Thompson', + item: { title: 'Post', link: '/post', description: '

    100% growth

    ', pubDate: new Date('2025-01-01T00:00:00Z') }, + }, + { + name: 'Queshu book', + module: () => import('../lib/routes/queshu/book'), + params: { bookid: '123' }, + html: '

    Book

    Description
    ', + title: 'Book - 单品活动信息 - 缺书网', + item: { title: 'Offer', link: 'http://www.queshu.com/sale' }, + }, + { + name: 'Queshu sale', + module: () => import('../lib/routes/queshu/sale'), + html: '
    BookOfferEnds todayYesterday
    ', + title: '图书促销 - 缺书网', + item: { title: 'Offer:Book', link: 'http://www.queshu.com/book', description: 'Offer:Book
    Ends today
    发布时间:Yesterday' }, + }, + { + name: 'AIEA', + module: () => import('../lib/routes/aiea/index'), + params: { period: 'upcoming' }, + html: '
    Seminar
    Details
    Description
    ', + title: 'AIEA Seminars', + item: { title: 'Seminar', link: '/0504/1', description: 'Description' }, + }, + { + name: 'Oasis', + module: () => import('../lib/routes/weibo/oasis/user'), + params: { userid: '123' }, + html: '
    Author
    Profile
    PostIgnored child
    ', + title: 'Author - 用户 - 绿洲', + item: { title: 'Post', link: 'https://oasis.weibo.cn/v1/h5/share?sid=456', description: 'Post
    ' }, + }, + { + name: 'Oct0pu5', + module: () => import('../lib/routes/oct0pu5/rss'), + html: '
    ', + title: '博客', + item: { title: 'Post', link: '/post', description: 'Description', pubDate: 1_735_689_600_000 }, + }, + { + name: 'Air China', + module: () => import('../lib/routes/airchina/index'), + html: '', + title: '国航服务公告', + item: { title: 'First', link: '/1', pubDate: new Date(2025, 0, 1), description: 'Article detail' }, + uniqueGuid: true, + }, + { + name: 'Xunhupay', + module: () => import('../lib/routes/xunhupay/index'), + html: '
    First
    Description
    2025-01-01
    Second
    2025-01-02
    ', + title: '博客', + item: { title: 'First', link: '/1', pubDate: new Date(2025, 0, 1), description: 'Description' }, + uniqueGuid: true, + }, + { + name: 'iiilab', + module: () => import('../lib/routes/iiilab/index'), + html: '
    First
    Description
    2025-01-01 00:00
    Second
    2025-01-02 00:00
    ', + title: '发现', + item: { title: 'First', link: '/1', pubDate: new Date(2025, 0, 1), description: 'Description' }, + uniqueGuid: true, + }, +]; + +beforeEach(() => { + vi.clearAllMocks(); + mocks.get.mockResolvedValue({ data: '
    Article detail
    ' }); +}); + +describe('common-config route callbacks', () => { + it.each(fixtures)('$name extracts its HTML fixture without evaluating selector strings', async (fixture) => { + mocks.raw.mockResolvedValue({ headers: new Headers({ 'content-type': 'text/html' }), _data: fixture.html }); + const { route } = await fixture.module(); + const params: Record = fixture.params ?? {}; + const ctx = { req: { param: (key?: string) => (key ? params[key] : params), query: () => {} } } as unknown as Context; + const data = (await (route.handler as Exclude)(ctx)) as Data; + expect(mocks.raw).toHaveBeenCalledTimes(1); + expect(data.title).toBe(fixture.title); + expect(data.item).toHaveLength(fixture.uniqueGuid ? 2 : 1); + expect(data.item![0]).toMatchObject(fixture.item); + if (fixture.uniqueGuid) { + expect(data.item!.map((item) => item.guid)).toEqual(['LzE=', 'LzI=']); + } + }); +}); diff --git a/tests/common-config.worker.test.ts b/tests/common-config.worker.test.ts new file mode 100644 index 000000000000..6702b6884d79 --- /dev/null +++ b/tests/common-config.worker.test.ts @@ -0,0 +1,40 @@ +import { afterEach, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/gov/zhengce/zhengceku'; +import type { Data, Route } from '../lib/types'; +import buildData from '../lib/utils/common-config'; + +// These unused network/devtools exports are tree-shaken in the production bundle. +vi.mock('undici', () => ({})); +vi.mock('node-network-devtools', () => ({ register: vi.fn() })); +vi.mock('../lib/config', () => ({ config: { requestRetry: 0, loggerLevel: 'error' } })); + +afterEach(() => vi.unstubAllGlobals()); + +it('extracts the policy library in workerd with page/item callbacks and dates', async () => { + const fetchMock = vi.fn(() => + Promise.resolve( + new Response( + '
    ', + { headers: { 'content-type': 'text/html' } } + ) + ) + ); + vi.stubGlobal('fetch', fetchMock); + const data = (await (route.handler as Exclude)({ req: { param: () => 'bmwj' } } as never)) as Data; + expect(data.title).toBe('部门文件 - 政府文件库'); + expect(data.item).toEqual([{ title: '政策', link: '/policy', pubDate: new Date('2025-01-01T00:00:00Z'), description: undefined, guid: undefined }]); + expect(fetchMock).toHaveBeenCalledTimes(1); +}); + +it('treats JavaScript-looking text and percent signs as literal data in workerd', async () => { + vi.stubGlobal( + 'fetch', + vi.fn(() => Promise.resolve(new Response('
  • 100% growth %remaining%
  • ', { headers: { 'content-type': 'text/html' } }))) + ); + const title = `$('a').text(); globalThis.shouldNotRun = true`; + const result = await buildData({ url: 'https://example.test/', title, item: { item: 'li', title: ($) => $('a').text() } }); + expect(result.title).toBe(title); + expect(result.item[0].title).toBe('100% growth %remaining%'); + expect(Reflect.has(globalThis, 'shouldNotRun')).toBe(false); +}); diff --git a/tests/fixtures/readhub/timeline.json b/tests/fixtures/readhub/timeline.json new file mode 100644 index 000000000000..81837b24db53 --- /dev/null +++ b/tests/fixtures/readhub/timeline.json @@ -0,0 +1,9 @@ +{ + "data": { + "self": { "title": "相关事件", "topicTitle": "Topic" }, + "items": [ + { "uid": "earlier-1", "title": "Earlier event", "publishDate": "2026-09-05T02:03:04.000Z" }, + { "uid": "earlier-2", "title": "Another event", "publishDate": "2026-09-04T15:04:05+08:00" } + ] + } +} diff --git a/tests/fixtures/readhub/topic.json b/tests/fixtures/readhub/topic.json new file mode 100644 index 000000000000..1b24cd3c3398 --- /dev/null +++ b/tests/fixtures/readhub/topic.json @@ -0,0 +1,22 @@ +{ + "data": { + "items": [ + { + "uid": "topic-42", + "title": "Topic \"quoted\" & 中文", + "summary": "First paragraph.\nA quoted \"statement\", path C:\\topic, and .", + "createdAt": "2026-09-06T08:00:00.000Z", + "publishDate": "2026-09-06T09:40:34.084Z", + "siteCount": 2, + "siteNameDisplay": "Source A", + "newsAggList": [ + { "uid": "news-1", "url": "https://example.com/report-1", "title": "Report A", "siteNameDisplay": "Source A" }, + { "uid": "news-2", "url": "https://example.com/report-2", "title": "Report B", "siteNameDisplay": "Source B" } + ], + "entityList": [{ "name": "Entity" }], + "tagList": [{ "name": "Technology" }], + "itemId": 42 + } + ] + } +} diff --git a/tests/readhub.test.ts b/tests/readhub.test.ts new file mode 100644 index 000000000000..0c9bbb153918 --- /dev/null +++ b/tests/readhub.test.ts @@ -0,0 +1,163 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route as daily } from '../lib/routes/readhub/daily'; +import { processItems } from '../lib/routes/readhub/util'; +import cache from '../lib/utils/cache'; +import got from '../lib/utils/got'; +import timelineFixture from './fixtures/readhub/timeline.json'; +import topicFixture from './fixtures/readhub/topic.json'; + +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: vi.fn() } })); +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); + +// Response shapes from public Readhub detail/timeline APIs on 2026-09-07. +// Values are synthetic; escaped text and explicit offsets exercise preservation. +const detailUrl = 'https://api.readhub.cn/topic/detail'; +const timelineUrl = 'https://api.readhub.cn/topic/timeline/list'; +const link = 'https://readhub.cn/topic/topic-42'; +const input = { title: 'Daily summary', link, guid: 'topic-42', description: '

    Incomplete summary

    ' }; +const cached = new Map(); + +beforeEach(() => { + vi.clearAllMocks(); + cached.clear(); + vi.mocked(cache.tryGet).mockImplementation(async (key, fetchValue) => { + if (cached.has(key)) { + return cached.get(key); + } + const value = await fetchValue(); + cached.set(key, value); + return value; + }); + vi.mocked(got).mockImplementation((url) => { + if (url === detailUrl) { + return Promise.resolve({ data: structuredClone(topicFixture) }); + } + if (url === timelineUrl) { + return Promise.resolve({ data: structuredClone(timelineFixture) }); + } + throw new Error('Unexpected upstream request'); + }); +}); + +describe('Readhub API enrichment', () => { + it('preserves the full summary, media, timeline, tags, title, GUID, and upstream publication date', async () => { + const [item] = await processItems([input]); + + expect(item).toMatchObject({ + title: topicFixture.data.items[0].title, + link, + author: 'Source A', + category: ['Entity', 'Technology'], + guid: 'readhub-topic-42', + pubDate: new Date('2026-09-06T09:40:34.084Z'), + }); + expect(item.description).toContain('First paragraph.\nA quoted "statement", path C:\\topic, and <markup>.'); + expect(item.description).toContain('

    媒体报道

    '); + expect(item.description).toContain('Report A'); + expect(item.description).toContain('Source B'); + expect(item.description).toContain('

    事件追踪

    '); + expect(item.description).toContain('Earlier event'); + expect(item.description).toContain('2026-09-05 10:03:04'); + expect(item.description).toContain('2026-09-04 15:04:05'); + expect(got).toHaveBeenNthCalledWith(1, detailUrl, { searchParams: { uid: 'topic-42' } }); + expect(got).toHaveBeenNthCalledWith(2, timelineUrl, { searchParams: { topic_uid: 'topic-42', size: 10 } }); + expect(input.guid).toBe('topic-42'); + }); + + it('does not reuse or extend old cached summary-only items', async () => { + cached.set(link, { ...input, guid: 'readhub-topic-42' }); + const [first] = await processItems([input]); + const [second] = await processItems([{ ...input, title: 'Another route title' }]); + + expect(first).toEqual(second); + expect(first.pubDate).toBeInstanceOf(Date); + expect(cache.tryGet).toHaveBeenCalledWith('readhub:topic:v2:topic-42', expect.any(Function)); + expect(got).toHaveBeenCalledTimes(2); + }); + + it('selects the requested UID instead of another item in the response', async () => { + const response = structuredClone(topicFixture); + response.data.items.unshift({ ...response.data.items[0], uid: 'unrelated', title: 'Unrelated' }); + vi.mocked(got).mockResolvedValueOnce({ data: response }); + expect((await processItems([input]))[0].title).toBe(topicFixture.data.items[0].title); + }); + + it.each(['missing', 'duplicate'])('rejects a %s requested topic without caching the summary', async (kind) => { + const response = structuredClone(topicFixture); + if (kind === 'missing') { + response.data.items[0].uid = 'unrelated'; + } else { + response.data.items.push(response.data.items[0]); + } + vi.mocked(got).mockResolvedValueOnce({ data: response }); + await expect(processItems([input])).rejects.toThrow('does not contain the requested topic'); + expect(cached.size).toBe(0); + }); + + it.each([ + { data: { items: [{ ...topicFixture.data.items[0], publishDate: undefined }] } }, + { data: { items: [{ ...topicFixture.data.items[0], publishDate: 'not a date' }] } }, + { data: { items: [{ ...topicFixture.data.items[0], summary: '' }] } }, + { data: { items: [{ ...topicFixture.data.items[0], newsAggList: undefined }] } }, + 'Unexpected upstream page', + ])('rejects invalid detail data instead of returning an incomplete item', async (response) => { + vi.mocked(got).mockResolvedValueOnce({ data: response }); + await expect(processItems([input])).rejects.toThrow('Readhub topic detail response is invalid'); + expect(cached.size).toBe(0); + }); + + it('retries after a failed enrichment rather than caching the fallback', async () => { + vi.mocked(got).mockRejectedValueOnce(new Error('Upstream unavailable')); + await expect(processItems([input])).rejects.toThrow('Upstream unavailable'); + expect(cached.size).toBe(0); + expect((await processItems([input]))[0].pubDate).toEqual(new Date('2026-09-06T09:40:34.084Z')); + }); + + it('rejects a malformed timeline instead of dropping related events', async () => { + vi.mocked(got) + .mockResolvedValueOnce({ data: topicFixture }) + .mockResolvedValueOnce({ data: { data: {} } }); + await expect(processItems([input])).rejects.toThrow('Readhub topic timeline response is invalid'); + expect(cached.size).toBe(0); + }); + + it('accepts an explicitly empty timeline and optional tags without inventing related items', async () => { + const response = { data: { items: [{ ...topicFixture.data.items[0], entityList: undefined, tagList: null }] } }; + vi.mocked(got) + .mockResolvedValueOnce({ data: response }) + .mockResolvedValueOnce({ data: { data: { items: [] } } }); + const [item] = await processItems([input]); + expect(item.category).toEqual([]); + expect(item.description).toContain('媒体报道'); + expect(item.description).not.toContain('事件追踪'); + expect(item.pubDate).toEqual(new Date('2026-09-06T09:40:34.084Z')); + }); + + it.each(['https://example.com/article', 'https://readhub.cn.example.com/topic/topic-42'])('preserves external category items without fetching their pages: %s', async (externalLink) => { + const external = { ...input, link: externalLink, pubDate: new Date('2026-09-01T00:00:00Z') }; + expect(await processItems([external])).toEqual([{ ...external, guid: 'readhub-topic-42' }]); + expect(got).not.toHaveBeenCalled(); + expect(cache.tryGet).not.toHaveBeenCalled(); + }); + + it('enriches daily summaries that contain no date and keeps the public feed URL', async () => { + const upstreamMock = vi.mocked(got).getMockImplementation()!; + vi.mocked(got).mockImplementation((url, options) => { + if (url === 'https://api.readhub.cn/daily') { + return Promise.resolve({ data: { data: { items: [{ uid: 'topic-42', title: input.title, summary: 'Daily summary' }] } } }); + } + if (url === 'https://readhub.cn/daily') { + return Promise.resolve({ data: '' }); + } + return upstreamMock(url, options); + }); + const feed = await daily.handler({ req: { query: vi.fn() } } as never); + + expect(feed.link).toBe('https://readhub.cn/daily'); + expect(feed.item).toHaveLength(1); + expect(feed.item![0].pubDate).toEqual(new Date('2026-09-06T09:40:34.084Z')); + expect(feed.item![0].description).toContain('Report A'); + expect(feed.item![0].description).toContain('Earlier event'); + }); +}); diff --git a/tests/readhub.worker.test.ts b/tests/readhub.worker.test.ts new file mode 100644 index 000000000000..88139a439388 --- /dev/null +++ b/tests/readhub.worker.test.ts @@ -0,0 +1,2 @@ +// Run the same enrichment and failure cases in workerd. +import './readhub.test'; diff --git a/tests/source-date-routes.test.ts b/tests/source-date-routes.test.ts new file mode 100644 index 000000000000..d26519b659d1 --- /dev/null +++ b/tests/source-date-routes.test.ts @@ -0,0 +1,104 @@ +import type { Context } from 'hono'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route as eastmoney } from '../lib/routes/eastmoney/report'; +import { route as hellogithub } from '../lib/routes/hellogithub'; +import { route as iCable } from '../lib/routes/i-cable/news'; +import { route as javbus } from '../lib/routes/javbus'; +import { route as smzdm } from '../lib/routes/smzdm/keyword'; +import { parseSearchDate } from '../lib/routes/smzdm/utils'; +import type { Data, Route } from '../lib/types'; + +const mocks = vi.hoisted(() => ({ request: vi.fn(), tryGet: vi.fn() })); +vi.mock('../lib/utils/got', () => ({ default: mocks.request })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/config', () => ({ config: { smzdm: { cookie: 'test-only-cookie' }, feature: { allow_user_supply_unsafe_domain: false }, cache: { routeExpire: 900 } } })); + +const invoke = async (route: Route, path: string, params: Record = {}) => + (await route.handler({ req: { path, query: vi.fn(), param: (name?: string) => (name ? params[name] : params) } } as unknown as Context)) as Data; + +beforeEach(() => { + vi.resetAllMocks(); + mocks.request.mockRejectedValue(new Error('Unexpected upstream request')); + mocks.tryGet.mockImplementation(async (_key, callback) => await callback()); + vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('External network access is disabled'))); +}); + +afterEach(() => { + vi.useRealTimers(); + vi.unstubAllGlobals(); +}); + +describe('explicit source dates', () => { + it('interprets HelloGitHub wall clock dates as UTC+8 and preserves explicit offsets', async () => { + mocks.request.mockResolvedValueOnce({ + data: { data: ['2026-08-28T08:07:22', '2026-08-28T00:07:22Z', '2026-08-28T08:07:22+08:00'].map((updated_at, index) => ({ item_id: String(index), name: 'Project', title: 'Summary', author: 'Author', updated_at })) }, + }); + const result = await invoke(hellogithub, '/hellogithub/home'); + expect(result.item.map((item) => (item.pubDate as Date).toISOString())).toEqual(Array.from({ length: 3 }, () => '2026-08-28T00:07:22.000Z')); + }); + + it('uses the WordPress GMT field as UTC and leaves absent dates absent', async () => { + mocks.tryGet.mockResolvedValueOnce({ data: [{ id: 1, name: 'News', link: 'https://example.com/news' }] }); + mocks.request.mockResolvedValueOnce({ + data: ['2026-09-06T15:41:43', '2026-09-06T23:41:43+08:00', undefined].map((date_gmt) => ({ + title: { rendered: 'Title' }, + link: 'https://example.com/article', + date_gmt, + content: { rendered: '

    Article

    ' }, + _embedded: { 'wp:term': [[]] }, + })), + }); + const result = await invoke(iCable, '/i-cable/news'); + expect(result.item.slice(0, 2).map((item) => (item.pubDate as Date).toISOString())).toEqual(Array.from({ length: 2 }, () => '2026-09-06T15:41:43.000Z')); + expect(result.item[2].pubDate).toBeUndefined(); + }); + + it('corrects Eastmoney list dates even when full article entries were cached with the old host timezone', async () => { + const cached = { title: 'Cached report', link: 'https://example.com/report.pdf', description: '

    Cached body

    ', pubDate: '2026-09-07T00:00:00.000Z' }; + mocks.request.mockResolvedValueOnce({ + data: ``, + }); + mocks.tryGet.mockResolvedValueOnce(cached); + const result = await invoke(eastmoney, '/eastmoney/report/industry', { category: 'industry' }); + expect(result.item[0]).toMatchObject({ description: cached.description, link: cached.link, pubDate: new Date('2026-09-06T16:00:00Z') }); + expect(cached.pubDate).toBe('2026-09-07T00:00:00.000Z'); + expect(mocks.request).toHaveBeenCalledTimes(1); + }); + + it('preserves the Javbus calendar release date at UTC midnight on cache hits', async () => { + const cached = { title: 'Cached release', link: 'https://www.javbus.com/release', description: '

    Cached body

    ', pubDate: '2026-09-17T16:00:00.000Z' }; + mocks.request.mockResolvedValueOnce({ data: 'Releasesrelease-id2026-09-18' }); + mocks.tryGet.mockResolvedValueOnce(cached); + const result = await invoke(javbus, '/javbus/star/example'); + expect(result.item[0]).toMatchObject({ description: cached.description, pubDate: new Date('2026-09-18T00:00:00Z') }); + expect(cached.pubDate).toBe('2026-09-17T16:00:00.000Z'); + expect(mocks.request).toHaveBeenCalledTimes(1); + }); + + it('skips SMZDM layout rows without article links and resolves time-only dates in the source calendar day', async () => { + vi.useFakeTimers({ toFake: ['Date'] }); + vi.setSystemTime(new Date('2026-09-06T18:10:00Z')); + mocks.request.mockResolvedValueOnce({ + data: ` +
    00:40
    +
    +
    00:40Source
    `, + }); + const result = await invoke(smzdm, '/smzdm/keyword/example', { keyword: 'example' }); + expect(result.item).toHaveLength(1); + expect(result.item[0]).toMatchObject({ link: 'https://example.com/deal', title: 'Deal - Price', pubDate: new Date('2026-09-06T16:40:00Z') }); + }); + + it('resolves SMZDM month/day values at the source year boundary', () => { + const now = new Date('2025-12-31T18:10:00Z'); + expect(parseSearchDate('00:40', now)?.toISOString()).toBe('2025-12-31T16:40:00.000Z'); + expect(parseSearchDate('12-31 23:40', now)?.toISOString()).toBe('2025-12-31T15:40:00.000Z'); + expect(parseSearchDate('01-01 01:40', now)?.toISOString()).toBe('2025-12-31T17:40:00.000Z'); + }); + + it('leaves unknown SMZDM dates absent instead of inventing the current time', () => { + expect(parseSearchDate('')).toBeUndefined(); + expect(parseSearchDate('unknown')).toBeUndefined(); + }); +}); diff --git a/tests/twitter-web-user.test.ts b/tests/twitter-web-user.test.ts new file mode 100644 index 000000000000..85288f838ec6 --- /dev/null +++ b/tests/twitter-web-user.test.ts @@ -0,0 +1,151 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { config } from '../lib/config'; +import InvalidParameterError from '../lib/errors/types/invalid-parameter'; +import api from '../lib/routes/twitter/api/web-api/api'; +import { gatherLegacyFromData, paginationTweets, twitterGot } from '../lib/routes/twitter/api/web-api/utils'; +import cache from '../lib/utils/cache'; +import ofetch from '../lib/utils/ofetch'; + +vi.mock('../lib/config', () => ({ config: { twitter: {}, cache: { routeExpire: 300 } } })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: vi.fn(), set: vi.fn() } })); +vi.mock('../lib/utils/ofetch', () => ({ default: vi.fn() })); +vi.mock('../lib/routes/twitter/utils', () => ({ getTwitterUserCacheKey: vi.fn(() => 'tweet-cache') })); +vi.mock('../lib/routes/twitter/api/web-api/constants', () => ({ + baseUrl: 'https://x.com/i/api', + gqlMap: { UserByScreenName: '/graphql/user-by-name', UserByRestId: '/graphql/user-by-id' }, + gqlFeatures: { UserByScreenName: {}, UserByRestId: {} }, + initGqlMap: vi.fn(), +})); +vi.mock('../lib/routes/twitter/api/web-api/utils', () => ({ twitterGot: vi.fn(), paginationTweets: vi.fn(), gatherLegacyFromData: vi.fn() })); + +const cached = new Map(); +const user = { + data: { + user: { + result: { + __typename: 'User', + rest_id: '123', + core: { name: 'Example', screen_name: 'example' }, + avatar: { image_url: 'https://example.com/avatar.png' }, + profile_bio: { description: 'Example bio' }, + }, + }, + }, +}; + +beforeEach(() => { + vi.clearAllMocks(); + cached.clear(); + config.twitter.thirdPartyApi = undefined; + vi.mocked(cache.tryGet).mockImplementation(async (key, fetchValue) => { + if (cached.has(key)) { + return cached.get(key); + } + const value = await fetchValue(); + cached.set(key, value); + return value; + }); + vi.mocked(twitterGot).mockResolvedValue(structuredClone(user)); + vi.mocked(ofetch).mockResolvedValue(structuredClone(user)); +}); + +describe('Twitter web user lookup', () => { + it('preserves valid user metadata and caches only the verified web response', async () => { + cached.set('twitter-userdata-example', { errors: [{ code: 88, message: 'Legacy cached failure' }] }); + expect(await api.getUser('example')).toEqual({ + name: 'Example', + screen_name: 'example', + profile_image_url: 'https://example.com/avatar.png', + description: 'Example bio', + }); + await api.getUser('example'); + expect(twitterGot).toHaveBeenCalledTimes(1); + expect(cache.tryGet).toHaveBeenCalledWith('twitter:web:userdata:v2:example', expect.any(Function)); + expect(cached.get('twitter-userdata-example')).toEqual({ errors: [{ code: 88, message: 'Legacy cached failure' }] }); + expect(cache.set).not.toHaveBeenCalled(); + }); + + it.each([ + [{ errors: [{ code: 88, message: 'Private upstream details' }] }, 'Twitter API user lookup failed (codes: 88)'], + [{ ...user, errors: [{ code: 89 }] }, 'Twitter API user lookup failed (codes: 89)'], + [{ errors: [{ message: 'Private upstream details' }] }, 'Twitter API user lookup failed'], + [{ errors: { message: 'Malformed errors' } }, 'Twitter API user lookup failed'], + [{ data: {} }, 'Twitter API returned an incomplete user response'], + [{ data: { user: {} } }, 'Twitter API returned an incomplete user response'], + [{ data: { user: { result: { __typename: 'User' } } } }, 'Twitter API returned an incomplete user response'], + [undefined, 'Twitter API returned an incomplete user response'], + ['Unexpected upstream response', 'Twitter API returned an incomplete user response'], + ])('rejects an upstream failure without caching or reporting a missing account', async (response, message) => { + vi.mocked(twitterGot).mockResolvedValueOnce(response); + let result: unknown; + try { + await api.getUser('example'); + } catch (error) { + result = error; + } + expect(result).toBeInstanceOf(Error); + expect(result).not.toBeInstanceOf(InvalidParameterError); + expect((result as Error).message).toBe(message); + expect(cached.size).toBe(0); + expect(cache.set).not.toHaveBeenCalled(); + }); + + it.each([ + [{ data: { user: null } }, "This account doesn't exist"], + [{ data: { user: { result: null } } }, "This account doesn't exist"], + [{ data: { user: { result: { __typename: 'UserUnavailable', message: 'Upstream details' } } } }, 'Twitter user is unavailable'], + ])('distinguishes an explicitly missing or unavailable user', async (response, message) => { + vi.mocked(twitterGot).mockResolvedValueOnce(response); + await expect(api.getUser('example')).rejects.toThrow(new InvalidParameterError(message)); + expect(cached.size).toBe(0); + }); + + it('retries normally after a GraphQL error and accepts an empty errors array', async () => { + vi.mocked(twitterGot) + .mockResolvedValueOnce({ errors: [{ code: 88 }] }) + .mockResolvedValueOnce({ ...user, errors: [] }); + await expect(api.getUser('example')).rejects.toThrow('Twitter API user lookup failed'); + expect((await api.getUser('example')).name).toBe('Example'); + expect(twitterGot).toHaveBeenCalledTimes(2); + }); + + it('validates cached data before dereferencing it', async () => { + cached.set('twitter:web:userdata:v2:example', { data: {} }); + await expect(api.getUser('example')).rejects.toThrow('Twitter API returned an incomplete user response'); + expect(twitterGot).not.toHaveBeenCalled(); + expect(cache.set).not.toHaveBeenCalled(); + }); + + it('preserves numeric-ID lookup parameters and auth requirements', async () => { + await api.getUser('+123'); + expect(twitterGot).toHaveBeenCalledWith('https://x.com/i/api/graphql/user-by-id', expect.objectContaining({ variables: JSON.stringify({ userId: '123', withSafetyModeUserFields: true }) }), { + allowNoAuth: false, + }); + }); + + it('validates third-party GraphQL errors without calling the direct API', async () => { + config.twitter.thirdPartyApi = 'https://api.example.com'; + vi.mocked(ofetch).mockResolvedValueOnce({ errors: [{ code: 88 }] }); + await expect(api.getUser('example')).rejects.toThrow('Twitter API user lookup failed (codes: 88)'); + expect(cached.size).toBe(0); + expect(twitterGot).not.toHaveBeenCalled(); + expect(ofetch).toHaveBeenCalledWith('https://api.example.com/graphql/user-by-name', expect.objectContaining({ method: 'GET' })); + }); + + it('does not request tweets or write an empty legacy cache entry when user lookup fails', async () => { + vi.mocked(twitterGot).mockResolvedValueOnce({ errors: [{ code: 88 }] }); + await expect(api.getUserTweets('example')).rejects.toThrow('Twitter API user lookup failed'); + expect(paginationTweets).not.toHaveBeenCalled(); + expect(cache.set).not.toHaveBeenCalled(); + expect(cached.size).toBe(0); + }); + + it('passes a verified user ID to the existing tweet cache and mapper', async () => { + vi.mocked(paginationTweets).mockResolvedValueOnce({ tweets: 'upstream result' }); + vi.mocked(gatherLegacyFromData).mockReturnValueOnce([{ id_str: 'tweet-1' }]); + expect(await api.getUserTweets('example')).toEqual([{ id_str: 'tweet-1' }]); + expect(paginationTweets).toHaveBeenCalledWith('UserTweets', '123', expect.objectContaining({ count: 20 })); + expect(cache.tryGet).toHaveBeenCalledWith('tweet-cache', expect.any(Function), 300, false); + }); +}); diff --git a/tests/twitter-web-user.worker.test.ts b/tests/twitter-web-user.worker.test.ts new file mode 100644 index 000000000000..6e41fa8500c2 --- /dev/null +++ b/tests/twitter-web-user.worker.test.ts @@ -0,0 +1,2 @@ +// Run user-response validation and cache behavior in workerd as well. +import './twitter-web-user.test'; diff --git a/tests/xiaoyuzhou-podcast.test.ts b/tests/xiaoyuzhou-podcast.test.ts new file mode 100644 index 000000000000..b6573003622e --- /dev/null +++ b/tests/xiaoyuzhou-podcast.test.ts @@ -0,0 +1,40 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/xiaoyuzhou/podcast'; +import ofetch from '../lib/utils/ofetch'; + +vi.mock('../lib/utils/ofetch', () => ({ default: vi.fn() })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: (_key: string, fetchValue: () => unknown) => fetchValue() } })); + +const context = { req: { param: () => 'source-id' } } as any; +const podcastHtml = ``; + +beforeEach(() => vi.resetAllMocks()); + +describe('Xiaoyuzhou podcast and episode resolution', () => { + it('preserves a podcast access error without requesting the same ID as an episode', async () => { + const blocked = Object.assign(new Error('Forbidden'), { status: 403 }); + vi.mocked(ofetch).mockRejectedValue(blocked); + + await expect(route.handler!(context)).rejects.toBe(blocked); + expect(ofetch).toHaveBeenCalledTimes(1); + }); + + it('resolves episode IDs when the podcast URL returns 404', async () => { + vi.mocked(ofetch) + .mockRejectedValueOnce(Object.assign(new Error('Not Found'), { status: 404 })) + .mockResolvedValueOnce('Podcast') + .mockResolvedValueOnce(podcastHtml); + + await expect(route.handler!(context)).resolves.toMatchObject({ title: 'Podcast', link: 'https://www.xiaoyuzhoufm.com/podcast/podcast-id' }); + expect(ofetch).toHaveBeenNthCalledWith(2, 'https://www.xiaoyuzhoufm.com/episode/source-id'); + expect(ofetch).toHaveBeenCalledTimes(3); + }); + + it('does not hide malformed page data behind an unrelated episode request', async () => { + vi.mocked(ofetch).mockResolvedValue('Unexpected response'); + + await expect(route.handler!(context)).rejects.toBeInstanceOf(SyntaxError); + expect(ofetch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/tests/xxhash-wasm.test.ts b/tests/xxhash-wasm.test.ts new file mode 100644 index 000000000000..c57e50f2e40f --- /dev/null +++ b/tests/xxhash-wasm.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; +import xxhash from 'xxhash-wasm'; + +// Empty/a/abc are standard XXH vectors; the remaining vectors were generated +// with xxhash-wasm 1.1.0's Node entry. This suite also runs via its workerd entry. +const vectors = [ + { input: '', h32: '02cc5d05', h64: 'ef46db3751d8e999', seed32: 'bd209070', seed64: '51e24c0e9077a48c' }, + { input: 'a', h32: '550d7456', h64: 'd24ec4f1a98c6e5b', seed32: '0d5a8e75', seed64: '63a379f532ba23d4' }, + { input: 'abc', h32: '32d153ff', h64: '44bc2cf5ad770999', seed32: '11364062', seed64: '1fc03ef74cebaa7d' }, + { input: '0123456789abcdef'.repeat(5), h32: '3fe8e7af', h64: '0d4cc7d5057880df', seed32: 'e0c25edb', seed64: '3d2aa4692d5507dc' }, + { input: 'RSSHub 中文 🐧', h32: '57c604bf', h64: '24862e5b32f4f03f', seed32: '129c225f', seed64: '163f1ecdcce47f0a' }, + { input: '/gov/zhengce/zhengceku/bmwj:json', h32: 'e008b760', h64: '390f38bd8df8e23e', seed32: 'abc5ecf2', seed64: 'b1183e2c2e5ba95d' }, + { input: '/gov/zhengce/zhengceku/bmwj:json:47', h32: 'f144b479', h64: '24d1ed2f1309ed35', seed32: 'f6461457', seed64: '2675f5df8c4442da' }, +]; + +const hash = await xxhash(); + +describe('official xxhash Node and Worker parity', () => { + it.each(vectors)('matches known 32/64-bit and seeded vectors for $input', (vector) => { + const bytes = new TextEncoder().encode(vector.input); + expect(hash.h32ToString(vector.input)).toBe(vector.h32); + expect(hash.h64ToString(vector.input)).toBe(vector.h64); + expect(hash.h32(vector.input)).toBe(Number.parseInt(vector.h32, 16)); + expect(hash.h64(vector.input)).toBe(BigInt('0x' + vector.h64)); + expect(hash.h32Raw(bytes)).toBe(Number.parseInt(vector.h32, 16)); + expect(hash.h64Raw(bytes)).toBe(BigInt('0x' + vector.h64)); + expect(hash.h32ToString(vector.input, 0x12_34_56_78)).toBe(vector.seed32); + expect(hash.h64ToString(vector.input, 0x01_23_45_67_89_ab_cd_efn)).toBe(vector.seed64); + }); + + it('supports streaming mixed strings and byte subarrays across block boundaries', () => { + const prefix = '0123456789abcdef'.repeat(5); + const bytes = new TextEncoder().encode('skipRSSHub 中文 🐧skip'); + const suffix = bytes.subarray(4, -4); + const h32 = hash.create32(0x12_34_56_78); + const h64 = hash.create64(0x01_23_45_67_89_ab_cd_efn); + for (const chunk of [prefix.slice(0, 7), prefix.slice(7), suffix]) { + h32.update(chunk); + h64.update(chunk); + } + expect(h32.digest()).toBe(hash.h32(prefix + 'RSSHub 中文 🐧', 0x12_34_56_78)); + expect(h64.digest()).toBe(hash.h64(prefix + 'RSSHub 中文 🐧', 0x01_23_45_67_89_ab_cd_efn)); + }); + + it('does not retain the polynomial shim collision', () => { + expect(hash.h64ToString('Aa')).not.toBe(hash.h64ToString('BB')); + }); +}); diff --git a/tests/xxhash-wasm.worker.test.ts b/tests/xxhash-wasm.worker.test.ts new file mode 100644 index 000000000000..1a987605aadf --- /dev/null +++ b/tests/xxhash-wasm.worker.test.ts @@ -0,0 +1,2 @@ +// Run the same reference vectors against the package's static-WASM workerd export. +import './xxhash-wasm.test'; diff --git a/tsdown-worker.config.ts b/tsdown-worker.config.ts index 6ca7321b2cbe..0b7fbb40f634 100644 --- a/tsdown-worker.config.ts +++ b/tsdown-worker.config.ts @@ -84,7 +84,6 @@ export default defineConfig({ 'dotenv/config': path.resolve('./lib/shims/dotenv-config.ts'), '@sentry/node': path.resolve('./lib/shims/sentry-node.ts'), '@honeybadger-io/js': path.resolve('./lib/shims/honeybadger.ts'), - 'xxhash-wasm': path.resolve('./lib/shims/xxhash-wasm.ts'), typescript: path.resolve('./lib/shims/typescript.ts'), // Routes file with Worker-specific build (match relative import from lib/) '../assets/build/routes.js': path.resolve('./assets/build/routes-worker.js'), @@ -94,6 +93,8 @@ export default defineConfig({ deps: { onlyBundle: false, neverBundle: [ + // Let Wrangler select the package's static-WASM workerd export. + 'xxhash-wasm', // Exclude non-code files that might be accidentally imported /\/_README$/, /\.node$/, diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index a4578e0de036..a2c9ab7da198 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -58,6 +58,7 @@ export default defineConfig({ compatibilityDate: '2026-08-22', compatibilityFlags: ['nodejs_compat'], kvNamespaces: ['CACHE'], + modulesRules: [{ type: 'CompiledWasm', include: ['**/*.wasm'] }], }, }), workerAliasPlugin(), @@ -69,6 +70,6 @@ export default defineConfig({ }, }, test: { - include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts'], + include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts', 'lib/utils/parse-date-in-timezone.test.ts', 'tests/source-date-routes.test.ts'], }, }); diff --git a/wrangler.toml b/wrangler.toml index b989c01860cd..7db2ae2535ac 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,7 +1,7 @@ name = "rsshub" main = "dist-worker/worker.mjs" compatibility_date = "2026-09-01" -compatibility_flags = ["nodejs_compat"] +compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"] find_additional_modules = true base_dir = "dist-worker/modules" From 20241b29b6150a4a25ed2513cfb23ad10c04ad4f Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 13:44:53 +0800 Subject: [PATCH 0721/1190] fix(worker): prevent stale cache locks and normalize feed metadata --- lib/middleware/cache.ts | 44 ++++--- lib/routes/gov/zhengce/zhengceku.ts | 5 +- lib/routes/twitter/api/web-api/api.ts | 9 ++ lib/utils/cache/http.test.ts | 1 + lib/utils/cache/index.test.ts | 2 + lib/utils/cache/index.ts | 4 + lib/utils/cache/index.worker.test.ts | 1 + lib/utils/cache/index.worker.ts | 3 + tests/cache-atomic-backends.test.ts | 55 +++++++++ tests/cache-coordination.test.ts | 161 ++++++++++++++++++++++++++ tests/cache-http.worker.test.ts | 1 + tests/common-config-routes.test.ts | 2 +- tests/twitter-web-user.test.ts | 12 ++ vitest.workers.config.ts | 2 +- 14 files changed, 279 insertions(+), 23 deletions(-) create mode 100644 tests/cache-atomic-backends.test.ts create mode 100644 tests/cache-coordination.test.ts diff --git a/lib/middleware/cache.ts b/lib/middleware/cache.ts index 0a9d28cb8869..98c251ec910c 100644 --- a/lib/middleware/cache.ts +++ b/lib/middleware/cache.ts @@ -26,10 +26,13 @@ const middleware: MiddlewareHandler = async (ctx, next) => { let value = await cacheModule.globalCache.get(key); - // Doesn't hit the cache? Try to become the fetcher and let others know! + // Only atomic backends can coordinate fetchers. HTTP/KV may return stale + // control keys after a completed request, while their feed cache stays useful. let isRequesting = false; - if (!value) { - isRequesting = !(await cacheModule.globalCache.claim(controlKey, config.cache.requestTimeout)); + let ownsClaim = false; + if (!value && cacheModule.globalCache.supportsAtomicClaims) { + ownsClaim = await cacheModule.globalCache.claim(controlKey, config.cache.requestTimeout); + isRequesting = !ownsClaim; } if (isRequesting) { @@ -61,31 +64,34 @@ const middleware: MiddlewareHandler = async (ctx, next) => { if (isRequesting) { // waited out a stale claim without finding a cache entry, take over the fetch - await cacheModule.globalCache.set(controlKey, '1', config.cache.requestTimeout); + ownsClaim = await cacheModule.globalCache.claim(controlKey, config.cache.requestTimeout); + if (!ownsClaim) { + throw new RequestInProgressError('This path is currently fetching, please come back later!'); + } } // let routers control cache ctx.set('cacheKey', key); - ctx.set('cacheControlKey', controlKey); + if (ownsClaim) { + ctx.set('cacheControlKey', controlKey); + } try { await next(); - } catch (error) { - await cacheModule.globalCache.set(controlKey, '0', config.cache.requestTimeout); - throw error; - } - const data: Data = ctx.get('data'); - if (ctx.res.headers.get('Cache-Control') !== 'no-cache' && data) { - data.lastBuildDate = new Date().toUTCString(); - ctx.set('data', data); - const body = JSON.stringify(data); - await cacheModule.globalCache.set(key, body, config.cache.routeExpire); + const data: Data = ctx.get('data'); + if (ctx.res.headers.get('Cache-Control') !== 'no-cache' && data) { + data.lastBuildDate = new Date().toUTCString(); + ctx.set('data', data); + const body = JSON.stringify(data); + await cacheModule.globalCache.set(key, body, config.cache.routeExpire); + } + } finally { + // Release after writing the feed, including failures after the route ran. + if (ownsClaim) { + await cacheModule.globalCache.set(controlKey, '0', config.cache.requestTimeout); + } } - - // We need to let it go, even no cache set. - // Wait to set cache so the next request could be handled correctly - await cacheModule.globalCache.set(controlKey, '0', config.cache.requestTimeout); }; export default middleware; diff --git a/lib/routes/gov/zhengce/zhengceku.ts b/lib/routes/gov/zhengce/zhengceku.ts index caf83404a9ba..738f2eb1fd7a 100644 --- a/lib/routes/gov/zhengce/zhengceku.ts +++ b/lib/routes/gov/zhengce/zhengceku.ts @@ -1,6 +1,6 @@ import type { Route } from '@/types'; import buildData from '@/utils/common-config'; -import { parseDate } from '@/utils/parse-date'; +import { parseDateInTimezone } from '@/utils/parse-date-in-timezone'; export const route: Route = { path: '/zhengceku/:department', @@ -33,7 +33,8 @@ async function handler(ctx) { item: '.news_box > .list > ul > li:not(.line)', title: ($) => $('h4 > a').text(), link: ($) => $('h4 > a').attr('href'), - pubDate: ($) => parseDate($('h4 > .date').text().trim()), + // The list provides calendar dates, represented at UTC midnight. + pubDate: ($) => parseDateInTimezone($('h4 > .date').text().trim(), 0), }, }); } diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts index 280db0c7e895..2d85fa37da0e 100644 --- a/lib/routes/twitter/api/web-api/api.ts +++ b/lib/routes/twitter/api/web-api/api.ts @@ -15,6 +15,15 @@ const getUserResult = (response: any) => { throw new Error(`Twitter API user lookup failed${codes.length ? ` (codes: ${codes.join(', ')})` : ''}`); } + // Some third-party providers return errors with HTTP 200 outside GraphQL. + const code = response?.code; + if (Number.isSafeInteger(code) && code >= 400) { + if (code === 404 && response.message === 'User not found') { + throw new InvalidParameterError('Twitter API could not find this user'); + } + throw new Error(`Twitter API user lookup failed (code: ${code})`); + } + const user = response?.data?.user; if (user === null || user?.result === null) { throw new InvalidParameterError("This account doesn't exist"); diff --git a/lib/utils/cache/http.test.ts b/lib/utils/cache/http.test.ts index acea7ea59cbc..d96d01df51bf 100644 --- a/lib/utils/cache/http.test.ts +++ b/lib/utils/cache/http.test.ts @@ -115,6 +115,7 @@ describe('http cache module', () => { await expect(cache.globalCache.get('route/key')).resolves.toBe('route-cache'); expect(cache.status.available).toBe(true); + expect(cache.globalCache.supportsAtomicClaims).toBe(false); expect(urls[0]).not.toContain('?refresh=1'); }); diff --git a/lib/utils/cache/index.test.ts b/lib/utils/cache/index.test.ts index 6ac1ab3b4c99..f072ad9d7cef 100644 --- a/lib/utils/cache/index.test.ts +++ b/lib/utils/cache/index.test.ts @@ -23,6 +23,7 @@ describe('cache', () => { if (!cache.clients.memoryCache || !cache.status.available) { throw new Error('Memory cache client error'); } + expect(cache.globalCache.supportsAtomicClaims).toBe(true); await cache.set('mock', undefined); expect(await cache.get('mock')).toBe(''); expect(await cache.has('mock')).toBe(true); @@ -67,6 +68,7 @@ describe('cache', () => { if (!cache.clients.redisClient || !cache.status.available) { throw new Error('Redis client error'); } + expect(cache.globalCache.supportsAtomicClaims).toBe(true); await cache.set('mock1', undefined); expect(await cache.get('mock1')).toBe(''); await cache.set('mock2', '2'); diff --git a/lib/utils/cache/index.ts b/lib/utils/cache/index.ts index 828c8d418ee4..8d45379efdf2 100644 --- a/lib/utils/cache/index.ts +++ b/lib/utils/cache/index.ts @@ -9,6 +9,7 @@ import memory from './memory'; import redis from './redis'; type GlobalCache = { + supportsAtomicClaims: boolean; get: (key: string) => Promise | string | null | undefined; has: (key: string) => Promise | boolean; set: (key: string, value?: string | T, maxAge?: number) => any; @@ -20,6 +21,7 @@ type GlobalCache = { }; const globalCache: GlobalCache = { + supportsAtomicClaims: false, get: () => null, has: () => false, set: () => null, @@ -45,6 +47,7 @@ if (isWorker) { } else { switch (config.cache.type) { case 'redis': { + globalCache.supportsAtomicClaims = true; cacheModule = redis; cacheModule.init(); const { redisClient } = cacheModule.clients; @@ -104,6 +107,7 @@ if (isWorker) { }; break; case 'memory': { + globalCache.supportsAtomicClaims = true; cacheModule = memory; cacheModule.init(); const { memoryCache } = cacheModule.clients; diff --git a/lib/utils/cache/index.worker.test.ts b/lib/utils/cache/index.worker.test.ts index c2ae95141c82..6890a35b37dc 100644 --- a/lib/utils/cache/index.worker.test.ts +++ b/lib/utils/cache/index.worker.test.ts @@ -25,6 +25,7 @@ describe('worker cache with KV binding', () => { it('becomes available once the KV namespace is bound', () => { setKVNamespace(env.CACHE); expect(cache.status.available).toBe(true); + expect(cache.globalCache.supportsAtomicClaims).toBe(false); }); it('round-trips strings and serializes objects', async () => { diff --git a/lib/utils/cache/index.worker.ts b/lib/utils/cache/index.worker.ts index 1e0dcdca833e..0490d7d151d2 100644 --- a/lib/utils/cache/index.worker.ts +++ b/lib/utils/cache/index.worker.ts @@ -15,6 +15,7 @@ if (cacheModule === http) { } type GlobalCache = { + supportsAtomicClaims: boolean; get: (key: string) => Promise | string | null | undefined; has: (key: string) => Promise | boolean; set: (key: string, value?: string | T, maxAge?: number) => any; @@ -26,6 +27,8 @@ type GlobalCache = { }; const globalCache: GlobalCache = { + // HTTP and KV reads can be stale, so neither can coordinate request locks. + supportsAtomicClaims: false, get: async (key) => { if (cacheModule === http) { if (key && cacheModule.status.available) { diff --git a/tests/cache-atomic-backends.test.ts b/tests/cache-atomic-backends.test.ts new file mode 100644 index 000000000000..c68a91e5d57e --- /dev/null +++ b/tests/cache-atomic-backends.test.ts @@ -0,0 +1,55 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const state = vi.hoisted(() => ({ type: 'memory', values: new Map(), eval: vi.fn() })); +vi.mock('../lib/config', () => ({ + config: { + cache: { + get type() { + return state.type; + }, + routeExpire: 300, + contentExpire: 600, + }, + memory: { max: 100 }, + }, +})); +vi.mock('../lib/utils/logger', () => ({ default: { info: vi.fn(), error: vi.fn() } })); +vi.mock('../lib/utils/cache/redis', () => ({ + default: { + init: vi.fn(), + status: { available: true }, + get: vi.fn(), + has: vi.fn(), + set: (key: string, value: string) => state.values.set(key, value), + clients: { redisClient: { get: (key: string) => state.values.get(key), exists: (key: string) => Number(state.values.has(key)), eval: state.eval } }, + }, +})); + +beforeEach(() => { + vi.resetModules(); + state.values.clear(); + state.eval.mockReset().mockImplementation((_script, _numberOfKeys, key: string) => { + if (state.values.get(key) === '1') { + return 0; + } + state.values.set(key, '1'); + return 1; + }); +}); + +describe('atomic cache backends', () => { + it.each(['memory', 'redis'])('retains atomic claims and release for %s', async (backend) => { + state.type = backend; + const cache = (await import('../lib/utils/cache/index')).default; + + expect(cache.globalCache.supportsAtomicClaims).toBe(true); + expect(await Promise.all([cache.globalCache.claim('same-request', 60), cache.globalCache.claim('same-request', 60)])).toEqual([true, false]); + await cache.globalCache.set('same-request', '0', 60); + expect(await cache.globalCache.claim('same-request', 60)).toBe(true); + + if (backend === 'redis') { + expect(state.eval).toHaveBeenCalledTimes(3); + expect(state.eval).toHaveBeenCalledWith(expect.stringContaining("redis.call('GET', KEYS[1])"), 1, 'same-request', 60); + } + }); +}); diff --git a/tests/cache-coordination.test.ts b/tests/cache-coordination.test.ts new file mode 100644 index 000000000000..5fa4b0282f89 --- /dev/null +++ b/tests/cache-coordination.test.ts @@ -0,0 +1,161 @@ +import { Context } from 'hono'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import middleware from '../lib/middleware/cache'; + +const cache = vi.hoisted(() => ({ + supportsAtomicClaims: false, + get: vi.fn(), + set: vi.fn(), + claim: vi.fn(), +})); +vi.mock('../lib/utils/cache/index', () => ({ default: { status: { available: true }, globalCache: cache } })); +vi.mock('../lib/config', () => ({ config: { format: 'rss', cache: { requestTimeout: 60, routeExpire: 300 } } })); + +const context = () => new Context(new Request('https://rsshub.example/test/cache'), { env: {}, path: '/test/cache' }); +const feed = { title: 'Feed', link: 'https://example.com', item: [{ title: 'Entry', link: 'https://example.com/entry' }] }; +const isControl = (key: string) => key.startsWith('rsshub:path-requested:'); + +beforeEach(() => { + vi.resetAllMocks(); + vi.useFakeTimers(); + cache.supportsAtomicClaims = false; + // Model a stale remote control key, including after the prior writer finished. + cache.get.mockImplementation((key: string) => (isControl(key) ? '1' : null)); + cache.claim.mockResolvedValue(false); +}); + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('cache coordination capabilities', () => { + it('serves an HTTP/KV feed hit without touching a stale remote lock', async () => { + cache.get.mockImplementation((key: string) => (isControl(key) ? '1' : JSON.stringify(feed))); + const ctx = context(); + + await middleware(ctx, vi.fn()); + + expect(ctx.get('data')).toEqual(feed); + expect(ctx.res.headers.get('RSSHub-Cache-Status')).toBe('HIT'); + expect(cache.get).toHaveBeenCalledTimes(1); + expect(cache.claim).not.toHaveBeenCalled(); + expect(cache.set).not.toHaveBeenCalled(); + expect(vi.getTimerCount()).toBe(0); + }); + + it('fetches and caches HTTP/KV misses without waiting on or writing control keys', async () => { + const ctx = context(); + const next = vi.fn(() => { + ctx.set('data', structuredClone(feed)); + return Promise.resolve(); + }); + + await middleware(ctx, next); + + expect(next).toHaveBeenCalledOnce(); + expect(cache.get.mock.calls.every(([key]) => !isControl(key))).toBe(true); + expect(cache.claim).not.toHaveBeenCalled(); + expect(cache.set).toHaveBeenCalledExactlyOnceWith(expect.stringMatching(/^rsshub:koa-redis-cache:/), expect.any(String), 300); + expect(JSON.parse(cache.set.mock.calls[0][1])).toMatchObject(feed); + expect(ctx.get('cacheControlKey')).toBeUndefined(); + expect(vi.getTimerCount()).toBe(0); + }); + + it('allows concurrent HTTP/KV cold misses even when remote reads remain stale', async () => { + const contexts = [context(), context()]; + const fetcher = vi.fn((ctx: Context) => { + ctx.set('data', structuredClone(feed)); + return Promise.resolve(); + }); + + await Promise.all(contexts.map((ctx) => middleware(ctx, () => fetcher(ctx)))); + + expect(fetcher).toHaveBeenCalledTimes(2); + expect(cache.claim).not.toHaveBeenCalled(); + expect(cache.get.mock.calls.every(([key]) => !isControl(key))).toBe(true); + expect(cache.set.mock.calls.every(([key]) => !isControl(key))).toBe(true); + expect(vi.getTimerCount()).toBe(0); + }); + + it('does not release a remote control key when an HTTP/KV route fails', async () => { + await expect(middleware(context(), () => Promise.reject(new Error('route failed')))).rejects.toThrow('route failed'); + expect(cache.set).not.toHaveBeenCalled(); + expect(cache.claim).not.toHaveBeenCalled(); + }); + + it('keeps atomic backends single-flight and serves the waiting request from the completed feed', async () => { + cache.supportsAtomicClaims = true; + const entries = new Map(); + cache.get.mockImplementation((key: string) => entries.get(key)); + cache.set.mockImplementation((key: string, value: string) => entries.set(key, value)); + cache.claim.mockImplementation((key: string) => { + if (entries.get(key) === '1') { + return false; + } + entries.set(key, '1'); + return true; + }); + const { promise: producerReady, resolve: markReady } = Promise.withResolvers(); + const { promise: releaseProducer, resolve: release } = Promise.withResolvers(); + const first = context(); + const second = context(); + const fetcher = vi.fn(async (ctx: Context) => { + if (ctx.get('data')) { + return; + } + markReady(); + await releaseProducer; + ctx.set('data', structuredClone(feed)); + }); + const producer = middleware(first, () => fetcher(first)); + await producerReady; + const waiter = middleware(second, () => fetcher(second)); + await vi.advanceTimersByTimeAsync(0); + expect(cache.claim).toHaveBeenCalledTimes(2); + release(); + await producer; + await vi.advanceTimersByTimeAsync(6000); + await waiter; + + expect(second.res.headers.get('RSSHub-Cache-Status')).toBe('HIT'); + expect(second.get('data')).toMatchObject(feed); + expect(cache.set.mock.calls.filter(([key]) => !isControl(key))).toHaveLength(1); + expect(cache.set.mock.calls.filter(([key, value]) => isControl(key) && value === '0')).toHaveLength(1); + }); + + it.each(['route', 'cache write'])('releases an owned atomic claim when the %s fails', async (failure) => { + cache.supportsAtomicClaims = true; + cache.claim.mockResolvedValue(true); + cache.set.mockImplementation((key: string) => { + if (!isControl(key)) { + throw new Error('cache write failed'); + } + }); + const ctx = context(); + const next = () => { + if (failure === 'route') { + return Promise.reject(new Error('route failed')); + } + ctx.set('data', structuredClone(feed)); + return Promise.resolve(); + }; + + await expect(middleware(ctx, next)).rejects.toThrow(`${failure} failed`); + expect(cache.set).toHaveBeenCalledWith(expect.stringMatching(/^rsshub:path-requested:/), '0', 60); + }); + + it('does not overwrite or release a competing atomic claim during takeover', async () => { + cache.supportsAtomicClaims = true; + cache.get.mockResolvedValue(null); + cache.claim.mockResolvedValue(false); + const next = vi.fn(); + const result = expect(middleware(context(), next)).rejects.toThrow('This path is currently fetching'); + await vi.advanceTimersByTimeAsync(6000); + await result; + + expect(cache.claim).toHaveBeenCalledTimes(2); + expect(cache.set).not.toHaveBeenCalled(); + expect(next).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/cache-http.worker.test.ts b/tests/cache-http.worker.test.ts index 851c530eac18..ba0878925bbb 100644 --- a/tests/cache-http.worker.test.ts +++ b/tests/cache-http.worker.test.ts @@ -62,6 +62,7 @@ afterEach(() => { describe('worker HTTP cache', () => { it('initializes without I/O and keeps using HTTP when a KV binding is present', async () => { expect(cache.status.available).toBe(true); + expect(cache.globalCache.supportsAtomicClaims).toBe(false); expect(fetchMock).not.toHaveBeenCalled(); setKVNamespace(env.CACHE); diff --git a/tests/common-config-routes.test.ts b/tests/common-config-routes.test.ts index 0d0c0cd54827..8f5d1b52b226 100644 --- a/tests/common-config-routes.test.ts +++ b/tests/common-config-routes.test.ts @@ -16,7 +16,7 @@ const fixtures = [ params: { department: 'bmwj' }, html: '
    ', title: '部门文件 - 政府文件库', - item: { title: '政策正文', link: '/policy', pubDate: new Date(2025, 0, 1) }, + item: { title: '政策正文', link: '/policy', pubDate: new Date('2025-01-01T00:00:00Z') }, }, { name: 'Polimi', diff --git a/tests/twitter-web-user.test.ts b/tests/twitter-web-user.test.ts index 85288f838ec6..b54f1481814e 100644 --- a/tests/twitter-web-user.test.ts +++ b/tests/twitter-web-user.test.ts @@ -71,6 +71,9 @@ describe('Twitter web user lookup', () => { [{ ...user, errors: [{ code: 89 }] }, 'Twitter API user lookup failed (codes: 89)'], [{ errors: [{ message: 'Private upstream details' }] }, 'Twitter API user lookup failed'], [{ errors: { message: 'Malformed errors' } }, 'Twitter API user lookup failed'], + [{ code: 500, message: 'Private provider error details' }, 'Twitter API user lookup failed (code: 500)'], + [{ code: 404, message: 'Unrecognized provider error' }, 'Twitter API user lookup failed (code: 404)'], + [{ code: 429, message: 'Private rate-limit details' }, 'Twitter API user lookup failed (code: 429)'], [{ data: {} }, 'Twitter API returned an incomplete user response'], [{ data: { user: {} } }, 'Twitter API returned an incomplete user response'], [{ data: { user: { result: { __typename: 'User' } } } }, 'Twitter API returned an incomplete user response'], @@ -101,6 +104,15 @@ describe('Twitter web user lookup', () => { expect(cached.size).toBe(0); }); + it('classifies the observed HTTP-200 provider not-found envelope without caching it', async () => { + config.twitter.thirdPartyApi = 'https://api.example.com'; + vi.mocked(ofetch).mockResolvedValueOnce({ code: 404, message: 'User not found' }); + await expect(api.getUser('example')).rejects.toThrow(new InvalidParameterError('Twitter API could not find this user')); + expect(cached.size).toBe(0); + expect(cache.set).not.toHaveBeenCalled(); + expect(twitterGot).not.toHaveBeenCalled(); + }); + it('retries normally after a GraphQL error and accepts an empty errors array', async () => { vi.mocked(twitterGot) .mockResolvedValueOnce({ errors: [{ code: 88 }] }) diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index a2c9ab7da198..08d312e0a188 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -70,6 +70,6 @@ export default defineConfig({ }, }, test: { - include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts', 'lib/utils/parse-date-in-timezone.test.ts', 'tests/source-date-routes.test.ts'], + include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts', 'lib/utils/parse-date-in-timezone.test.ts', 'tests/source-date-routes.test.ts', 'tests/cache-coordination.test.ts'], }, }); From 3fe2dda9adf51c8c73ab5ed131ebfc9444d4d6cf Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 14:02:15 +0800 Subject: [PATCH 0722/1190] fix(sciencenet): bound concurrent full-article parsing --- lib/routes/sciencenet/blog.ts | 11 +- tests/sciencenet-concurrency.test.ts | 130 ++++++++++++++++++++ tests/sciencenet-concurrency.worker.test.ts | 2 + 3 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/sciencenet-concurrency.test.ts create mode 100644 tests/sciencenet-concurrency.worker.test.ts diff --git a/lib/routes/sciencenet/blog.ts b/lib/routes/sciencenet/blog.ts index 13bb46c9dcee..3bbbd58dc6d4 100644 --- a/lib/routes/sciencenet/blog.ts +++ b/lib/routes/sciencenet/blog.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; import iconv from 'iconv-lite'; +import pMap from 'p-map'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; @@ -71,8 +72,10 @@ async function handler(ctx) { }; }); - items = await Promise.all( - items.map((item) => + // Bound simultaneous downloads and decoding of potentially large GBK pages. + items = await pMap( + items, + (item) => cache.tryGet(item.link!, async () => { const detailResponse = await got({ method: 'get', @@ -87,8 +90,8 @@ async function handler(ctx) { item.pubDate = timezone(parseDate(content('.xg1').eq(5).text()), 8); return item; - }) - ) + }), + { concurrency: 3 } ); return { diff --git a/tests/sciencenet-concurrency.test.ts b/tests/sciencenet-concurrency.test.ts new file mode 100644 index 000000000000..9df192eb261d --- /dev/null +++ b/tests/sciencenet-concurrency.test.ts @@ -0,0 +1,130 @@ +import iconv from 'iconv-lite'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/sciencenet/blog'; +import type { Data } from '../lib/types'; +import cache from '../lib/utils/cache'; +import got from '../lib/utils/got'; + +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: vi.fn() } })); +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); + +const rootUrl = 'http://blog.sciencenet.cn'; +const indexUrl = `${rootUrl}/blog.php?mod=recommend&type=list&op=5&ord=1`; +const cached = new Map(); + +// Synthetic GBK pages preserve the selectors used by the ScienceNet route. +const indexPage = (count: number) => + iconv.encode(`${Array.from({ length: count }, (_, index) => ``).join('')}
    研究 ${index}2026-09-07 10:30
    `, 'gbk'); +const articlePage = (index: number) => + iconv.encode( + `作者 ${index}${'Metadata'.repeat(5)}2026-09-07 10:30

    完整正文 ${index} & 科学

    x
    `, + 'gbk' + ); +const context = (limit?: number) => ({ req: { param: vi.fn(), query: () => limit?.toString() } }); + +beforeEach(() => { + vi.clearAllMocks(); + cached.clear(); + vi.mocked(cache.tryGet).mockImplementation(async (key, fetchValue) => { + if (cached.has(key)) { + return cached.get(key); + } + const value = await fetchValue(); + cached.set(key, value); + return value; + }); +}); + +describe('ScienceNet bounded detail enrichment', () => { + it('caps actual outstanding detail requests at three and preserves every item, order, content, date, and cache hit', async () => { + const cachedIndexes = new Set([1, 6]); + for (const index of cachedIndexes) { + cached.set(`${rootUrl}/blog-${index}.html`, { + title: `研究 ${index}`, + link: `${rootUrl}/blog-${index}.html`, + author: `Cached author ${index}`, + description: `

    Cached full article ${index}

    `, + pubDate: new Date('2026-09-07T02:30:00Z'), + }); + } + const started: number[] = []; + const pending = new Map void>(); + let active = 0; + let peak = 0; + vi.mocked(got).mockImplementation(({ url }) => { + if (url === indexUrl) { + return Promise.resolve({ data: indexPage(8) }); + } + const index = Number(/blog-(\d+)\.html$/.exec(url)![1]); + started.push(index); + active++; + peak = Math.max(peak, active); + return new Promise((resolve) => { + pending.set(index, () => { + active--; + pending.delete(index); + resolve({ data: articlePage(index) }); + }); + }); + }); + + const feedPromise = route.handler(context(8) as never); + await vi.waitFor(() => expect(started).toEqual([0, 2, 3])); + for (let completed = 1; completed <= 6; completed++) { + // Finish later items first; the emitted feed must retain source order. + const index = Math.max(...pending.keys()); + pending.get(index)!(); + // oxlint-disable-next-line no-await-in-loop -- Advance one completion at a time to observe the concurrency bound. + await vi.waitFor(() => expect(started.length).toBe(Math.min(completed + 3, 6))); + } + const feed = (await feedPromise) as Data; + + expect(peak).toBe(3); + expect(active).toBe(0); + expect(pending.size).toBe(0); + expect(feed.item).toHaveLength(8); + expect(feed.item!.map((item) => item.link)).toEqual(Array.from({ length: 8 }, (_, index) => `${rootUrl}/blog-${index}.html`)); + for (const [index, item] of feed.item!.entries()) { + expect(item.title).toBe(`研究 ${index}`); + expect(item.pubDate).toEqual(new Date('2026-09-07T02:30:00Z')); + if (cachedIndexes.has(index)) { + expect(item.description).toBe(`

    Cached full article ${index}

    `); + } else { + expect(item.description).toContain(`

    完整正文 ${index} & 科学

    `); + expect(item.description).toContain(`https://example.com/figure-${index}.png`); + expect(item.description).toContain('x'); + expect(item.author).toBe(`作者 ${index}`); + } + } + expect(started).toHaveLength(6); + expect(cache.tryGet).toHaveBeenCalledTimes(8); + const warmFeed = (await route.handler(context(8) as never)) as Data; + expect(warmFeed.item).toEqual(feed.item); + expect(started).toHaveLength(6); + }); + + it('preserves the documented default of up to fifty full articles', async () => { + vi.mocked(got).mockImplementation(({ url }) => { + if (url === indexUrl) { + return Promise.resolve({ data: indexPage(55) }); + } + const index = Number(/blog-(\d+)\.html$/.exec(url)![1]); + return Promise.resolve({ data: articlePage(index) }); + }); + const feed = (await route.handler(context() as never)) as Data; + + expect(feed.item).toHaveLength(50); + expect(feed.item![49].description).toContain('完整正文 49'); + expect(got).toHaveBeenCalledTimes(51); + expect(cache.tryGet).toHaveBeenCalledTimes(50); + }); + + it('propagates failed detail requests without caching incomplete items', async () => { + vi.mocked(got) + .mockResolvedValueOnce({ data: indexPage(1) }) + .mockRejectedValueOnce(new Error('Upstream unavailable')); + await expect(route.handler(context(1) as never)).rejects.toThrow('Upstream unavailable'); + expect(cached.size).toBe(0); + }); +}); diff --git a/tests/sciencenet-concurrency.worker.test.ts b/tests/sciencenet-concurrency.worker.test.ts new file mode 100644 index 000000000000..28c73e275ae8 --- /dev/null +++ b/tests/sciencenet-concurrency.worker.test.ts @@ -0,0 +1,2 @@ +// Check the same GBK decoding, bounded concurrency, and complete feeds in workerd. +import './sciencenet-concurrency.test'; From e39a2886c623ea78e44e68a6e2a0afbfc3d7972d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:26:54 +0800 Subject: [PATCH 0723/1190] chore(deps): bump the nix group with 2 updates (#23236) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `97135e8` to `07666e4` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/97135e80b6e432f41f84f72383e1b8147f33ef0c...07666e44528d349ae352c33441fbf625df115370) Updates `nixpkgs` from `34ab990` to `3ed67ec` - [Commits](https://github.com/NixOS/nixpkgs/compare/34ab99075ac4f7e40cf037eef32cb1c360bb85e9...3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2) --- updated-dependencies: - dependency-name: devenv dependency-version: 07666e44528d349ae352c33441fbf625df115370 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: 3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 55 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/flake.lock b/flake.lock index aefdf8237bed..dec4021b6426 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ ] }, "locked": { - "lastModified": 1777487137, - "narHash": "sha256-TuvKVBX60mqyMT6OB5JqVEh1YIWtFMR/igLCaCdC9tw=", + "lastModified": 1788181969, + "narHash": "sha256-OUB6hPlFBB9FRdZgZXSye4lDOg+fbrqKe8ePv2IM7NY=", "owner": "cachix", "repo": "cachix", - "rev": "a66a440c321d35f7193472c317f42a55ccd1cb93", + "rev": "98093e8eea6c1635ef1337a576fa76f3e8bd4f39", "type": "github" }, "original": { @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1788213407, - "narHash": "sha256-zxEb+L6moGAWY+HXqAOTKIBb7tNYYWc+Km1+6byphb0=", + "lastModified": 1788494439, + "narHash": "sha256-vYpGge1n4Jd44/qopYhuJeAEIr+iCrx4KuAIoyw6VlE=", "owner": "cachix", "repo": "devenv", - "rev": "97135e80b6e432f41f84f72383e1b8147f33ef0c", + "rev": "07666e44528d349ae352c33441fbf625df115370", "type": "github" }, "original": { @@ -101,11 +101,11 @@ ] }, "locked": { - "lastModified": 1778716662, - "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", + "lastModified": 1788306937, + "narHash": "sha256-lbrI1UYVpFgKSsyrU9oeF7FHfnu8nLD9ja0ou9f2rxk=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", + "rev": "f16b25b8c3d2809b87925d0b76652d7821a75c68", "type": "github" }, "original": { @@ -135,16 +135,17 @@ "ghostty": { "flake": false, "locked": { - "lastModified": 1784602798, - "narHash": "sha256-298x90knBUWX5GHGXh2SKsAKvStjU2ri9UgOGoF79/8=", + "lastModified": 1786026742, + "narHash": "sha256-tRtgTg/i3w1nFdRHU2IGekfJN8EyP+HARW4MtoUZyqk=", "owner": "ghostty-org", "repo": "ghostty", - "rev": "88b4cd047fa627cdca6781bc7e7dc8b75a2cecb9", + "rev": "22d13172cde98a0a4dda05d3d6a3fcb0dd8ed018", "type": "github" }, "original": { "owner": "ghostty-org", "repo": "ghostty", + "rev": "22d13172cde98a0a4dda05d3d6a3fcb0dd8ed018", "type": "github" } }, @@ -160,11 +161,11 @@ ] }, "locked": { - "lastModified": 1782908218, - "narHash": "sha256-wLMOrPgVyeF3XmP+qfYcLqnVdTxikdcSvbIY7rA9jTA=", + "lastModified": 1788267358, + "narHash": "sha256-nt+lUqYVpc9Y6JeMd2WmXzCDojasdadKo0mWcluvY2Y=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "9f7e99119ece7705299595299f3b031f39356de1", + "rev": "27555e2624241fb116b49095df4caaee85a25691", "type": "github" }, "original": { @@ -226,11 +227,11 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1783935112, - "narHash": "sha256-IAQ14nteIKXAz4cd75UcZrsHGEVJ7QNrkUwX9rmeZ/Y=", + "lastModified": 1788165251, + "narHash": "sha256-ITHt6eU6DuwQqlNV1/wehvKYPE6J8sZIlv75CpDjcHI=", "owner": "nix-community", "repo": "nixd", - "rev": "a64cd33e53b316b6b092ea0a966640cd2309bf3d", + "rev": "e2ca72c353cfbc3d63e942fe9aeae4ec497863bf", "type": "github" }, "original": { @@ -277,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1788179007, - "narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=", + "lastModified": 1788316716, + "narHash": "sha256-bc7rSpXIdn9QWGNqfWcPZWOhEVF8NoeAZkWq0XWnf/k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9", + "rev": "3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2", "type": "github" }, "original": { @@ -306,11 +307,11 @@ ] }, "locked": { - "lastModified": 1782875958, - "narHash": "sha256-5eqDcnBjb1424HRQdnhuhNOBZguq1Z2tqSa2OMF/m2c=", + "lastModified": 1788332415, + "narHash": "sha256-BTFrmyh0oaVDsvA9NNw0YpbbeppTwU0t70iVx672Ew8=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "13139aefa973f3d96c60c0fbab801de058ae25ca", + "rev": "860d7c835ab91bfc8972b67092f5f2db8e9390a0", "type": "github" }, "original": { @@ -343,11 +344,11 @@ ] }, "locked": { - "lastModified": 1780220602, - "narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=", + "lastModified": 1786901030, + "narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "db947814a175b7ca6ded66e21383d938df01c227", + "rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa", "type": "github" }, "original": { From f67fbf0e041923b37180a7e165382bdd958a02c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:32:21 +0800 Subject: [PATCH 0724/1190] chore(deps-dev): bump eslint-plugin-regexp from 3.2.0 to 3.3.0 (#23237) Bumps [eslint-plugin-regexp](https://github.com/ota-meshi/eslint-plugin-regexp) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/ota-meshi/eslint-plugin-regexp/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-regexp/compare/v3.2.0...v3.3.0) --- updated-dependencies: - dependency-name: eslint-plugin-regexp dependency-version: 3.3.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 71 +++++++++++++++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 7d533ac4c094..b21dc922f664 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "eslint": "10.9.1", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.3.0", - "eslint-plugin-regexp": "3.2.0", + "eslint-plugin-regexp": "3.3.0", "eslint-plugin-simple-import-sort": "14.0.0", "eslint-plugin-unicorn": "74.0.0", "eslint-plugin-yml": "3.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 401346481d78..519abe1a4aba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -378,8 +378,8 @@ importers: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) eslint-plugin-regexp: - specifier: 3.2.0 - version: 3.2.0(eslint@10.9.1(jiti@2.7.0)) + specifier: 3.3.0 + version: 3.3.0(eslint@10.9.1(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: 14.0.0 version: 14.0.0(eslint@10.9.1(jiti@2.7.0)) @@ -491,12 +491,12 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@asamuzakjp/css-color@6.0.5': - resolution: {integrity: sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==} + '@asamuzakjp/css-color@6.0.7': + resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} engines: {node: ^22.13.0 || >=24.0.0} - '@asamuzakjp/dom-selector@8.3.0': - resolution: {integrity: sha512-UJLfKXBhrc8i1vH2eJXuYQMwlsLKWFw3O+CPqXSuVEiikeAim3UgrfWX0k4tA/X8cRFM8iZ7OaqBokFGbYusdg==} + '@asamuzakjp/dom-selector@8.3.2': + resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} engines: {node: ^22.13.0 || >=24.0.0} '@asteasolutions/zod-to-openapi@9.1.0': @@ -663,8 +663,8 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@csstools/color-helpers@6.1.0': - resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} + '@csstools/color-helpers@6.1.1': + resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} engines: {node: '>=20.19.0'} '@csstools/css-calc@3.3.0': @@ -674,8 +674,8 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.10': - resolution: {integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==} + '@csstools/css-color-parser@4.2.2': + resolution: {integrity: sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -687,8 +687,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.7': - resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==} + '@csstools/css-syntax-patches-for-csstree@1.1.12': + resolution: {integrity: sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -3941,8 +3941,8 @@ packages: typescript: optional: true - eslint-plugin-regexp@3.2.0: - resolution: {integrity: sha512-4yq47CnLxyfHFKJEo5kvNaiJ0aDtBxSJWk4M2LiamplUXdWxdlzDYqImb/ZVCp+2BqkQjBAmw4Xc07Q8SXVDZg==} + eslint-plugin-regexp@3.3.0: + resolution: {integrity: sha512-TT0JTQbW7CRKohntpGAsMdQ1K3MsmJK4gdAhKJtIwlJ0JvuYxauymFgJdvqxIY4lqNj0EuRZWVrymQtEdJYEFg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: '>=9.38.0' @@ -4580,8 +4580,8 @@ packages: jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdoc-type-pratt-parser@9.1.1: - resolution: {integrity: sha512-kojSbQb9iQM2bk2PmHiKa3reG0U4v2gN9U8D8+eCQq+4KM5ZXBUyBVeF8KmR0RiwqyrW4+uVWYWTOLnpsavnkw==} + jsdoc-type-pratt-parser@9.2.1: + resolution: {integrity: sha512-V4Ww4EHnTcTLSOMoB0FsF72JhQvcAsriCm/LWnxJeGWoxIjEL2l9na11abQok5SYShq8m0Gl02el/xAbTCulvQ==} engines: {node: ^22.22.2 || >=24.15.0} jsdom@30.0.1: @@ -5369,6 +5369,10 @@ packages: resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + postman-request@2.88.1-postman.49: resolution: {integrity: sha512-Vcjtjyy1KQQ7u08PQg6i1Yd6GyNhyToGriudVruouXwLvAenyouHZaABYfJ2niJF3ajpMsvfHjyIYNei1zdekw==} engines: {node: '>= 16'} @@ -6533,16 +6537,16 @@ snapshots: '@actions/io@3.0.2': {} - '@asamuzakjp/css-color@6.0.5': + '@asamuzakjp/css-color@6.0.7': dependencies: '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.2 optional: true - '@asamuzakjp/dom-selector@8.3.0': + '@asamuzakjp/dom-selector@8.3.2': dependencies: bidi-js: 1.0.3 css-tree: 3.2.1 @@ -6692,7 +6696,7 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@6.1.0': + '@csstools/color-helpers@6.1.1': optional: true '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -6701,9 +6705,9 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 optional: true - '@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 6.1.0 + '@csstools/color-helpers': 6.1.1 '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -6714,7 +6718,7 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 optional: true - '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 optional: true @@ -9286,13 +9290,13 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.2.0(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-regexp@3.3.0(eslint@10.9.1(jiti@2.7.0)): dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.8 eslint: 10.9.1(jiti@2.7.0) - jsdoc-type-pratt-parser: 9.1.1 + jsdoc-type-pratt-parser: 9.2.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 @@ -10033,16 +10037,17 @@ snapshots: jsbn@0.1.1: {} - jsdoc-type-pratt-parser@9.1.1: + jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 + '@types/node': 26.4.0 jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: - '@asamuzakjp/css-color': 6.0.5 - '@asamuzakjp/dom-selector': 8.3.0 + '@asamuzakjp/css-color': 6.0.7 + '@asamuzakjp/dom-selector': 8.3.2 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.7(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.12(css-tree@3.2.1) '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.3.0) @@ -11079,6 +11084,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postman-request@2.88.1-postman.49: dependencies: '@postman/form-data': 3.1.1 @@ -11962,7 +11973,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - postcss: 8.5.26 + postcss: 8.5.28 rollup: 4.63.1 tinyglobby: 0.2.17 optionalDependencies: From e4a0abc012faca5ee8216704ad7a3f7f58b21948 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 20:33:53 +0800 Subject: [PATCH 0725/1190] fix(worker): support remote Playwright WebSocket browsers --- lib/app.worker.tsx | 9 +- lib/playwright-worker.d.ts | 17 ++ lib/utils/playwright-fetch.test.ts | 270 ++++++++++++++++++++ lib/utils/playwright-fetch.ts | 246 ++++++++++++++++++ lib/utils/playwright-remote.worker.ts | 32 +++ lib/utils/playwright-transport.worker.ts | 200 +++++++++++++++ lib/utils/playwright.ts | 15 +- lib/utils/playwright.worker.ts | 101 +++++--- lib/utils/request-rewriter/fetch.worker.ts | 6 +- package.json | 4 +- pnpm-lock.yaml | 3 + scripts/workflow/build-playwright-worker.ts | 115 +++++++++ tests/playwright-fetch.worker.test.ts | 1 + tests/playwright-lifecycle.worker.test.ts | 55 +++- tests/playwright-transport.test.ts | 191 ++++++++++++++ tests/playwright-worker-build.test.ts | 66 +++++ wrangler.toml | 8 + 17 files changed, 1287 insertions(+), 52 deletions(-) create mode 100644 lib/playwright-worker.d.ts create mode 100644 lib/utils/playwright-fetch.test.ts create mode 100644 lib/utils/playwright-fetch.ts create mode 100644 lib/utils/playwright-remote.worker.ts create mode 100644 lib/utils/playwright-transport.worker.ts create mode 100644 scripts/workflow/build-playwright-worker.ts create mode 100644 tests/playwright-fetch.worker.test.ts create mode 100644 tests/playwright-transport.test.ts create mode 100644 tests/playwright-worker-build.test.ts diff --git a/lib/app.worker.tsx b/lib/app.worker.tsx index f40a0cf99464..28c73177f173 100644 --- a/lib/app.worker.tsx +++ b/lib/app.worker.tsx @@ -19,10 +19,12 @@ import template from '@/middleware/template'; import trace from '@/middleware/trace'; import registry from '@/registry'; import { setKVNamespace } from '@/utils/cache/index.worker'; -import { setBrowserBinding } from '@/utils/playwright'; +import { setBrowserBinding, setPlaywrightServiceBinding } from '@/utils/playwright'; // Define Worker environment bindings type Bindings = { + PLAYWRIGHT_SERVICE?: any; // Optional remote Playwright service binding + PLAYWRIGHT_SERVICE_ORIGIN?: string; BROWSER?: any; // Browser Rendering API binding CACHE?: KVNamespace; // KV namespace for caching }; @@ -31,9 +33,8 @@ const app = new Hono<{ Bindings: Bindings }>(); // Set browser and KV bindings app.use(async (c, next) => { - if (c.env?.BROWSER) { - setBrowserBinding(c.env.BROWSER); - } + setBrowserBinding(c.env?.BROWSER); + setPlaywrightServiceBinding(c.env?.PLAYWRIGHT_SERVICE, c.env?.PLAYWRIGHT_SERVICE_ORIGIN); if (c.env?.CACHE) { setKVNamespace(c.env.CACHE); } diff --git a/lib/playwright-worker.d.ts b/lib/playwright-worker.d.ts new file mode 100644 index 000000000000..39ebf6d1f553 --- /dev/null +++ b/lib/playwright-worker.d.ts @@ -0,0 +1,17 @@ +declare module '*assets/build/playwright-worker.mjs' { + import type { chromium } from 'patchright'; + + interface PlaywrightCore { + getPlaywrightVersion: () => string; + inprocess: { + playwright: { chromium: typeof chromium }; + }; + server: { + WebSocketTransport: { + connect: (...args: any[]) => Promise; + }; + }; + } + + export function getPlaywrightCore(): Promise; +} diff --git a/lib/utils/playwright-fetch.test.ts b/lib/utils/playwright-fetch.test.ts new file mode 100644 index 000000000000..8ab284c1b651 --- /dev/null +++ b/lib/utils/playwright-fetch.test.ts @@ -0,0 +1,270 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import fetchWithPlaywrightRetry from './playwright-fetch'; + +const { settings, getPage, warn } = vi.hoisted(() => ({ + settings: { playwrightWSEndpoint: 'wss://browser.test/playwright?token=endpoint-fixture', requestTimeout: 30000 }, + getPage: vi.fn(), + warn: vi.fn(), +})); + +vi.mock('@/config', () => ({ config: settings })); +vi.mock('@/utils/playwright', () => ({ getPlaywrightPage: getPage })); +vi.mock('@/utils/logger', () => ({ default: { warn } })); + +const biliUrl = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1'; +const rankingUrl = 'https://app-api.pixiv.net/v1/illust/ranking?mode=week_r18&filter=for_ios'; +const oauthUrl = 'https://oauth.secure.pixiv.net/auth/token'; +const form = 'grant_type=refresh_token&refresh_token=refresh-fixture%2Bvalue&client_secret=client-fixture'; + +function makeBrowser(url: string, body = '{"items":[{"id":1}]}', status = 200) { + let paused: (event: any) => void; + const session = { + on: vi.fn((_event, listener) => { + paused = listener; + }), + send: vi.fn((method: string, _params?: any) => Promise.resolve(method === 'Page.getFrameTree' ? { frameTree: { frame: { id: 'main-frame' } } } : {})), + }; + const emit = (overrides: Record = {}) => paused({ requestId: 'original', request: { url }, frameId: 'main-frame', resourceType: 'Document', ...overrides }); + const response = { + url: () => url, + body: vi.fn(() => Promise.resolve(Buffer.from(body))), + headersArray: vi.fn(() => + Promise.resolve([ + { name: 'Content-Type', value: 'application/json; charset=utf-8' }, + { name: 'Content-Encoding', value: 'gzip' }, + { name: 'Content-Length', value: '1234' }, + { name: 'Connection', value: 'keep-alive, x-private-hop' }, + { name: 'Keep-Alive', value: 'timeout=30' }, + { name: 'X-Private-Hop', value: 'upstream-only' }, + { name: 'Transfer-Encoding', value: 'chunked' }, + { name: 'Cache-Control', value: 'no-store' }, + ]) + ), + status: () => status, + statusText: () => (status === 200 ? 'OK' : 'Forbidden'), + }; + const page = { + goto: vi.fn(() => { + emit(); + return Promise.resolve(response); + }), + }; + const instance = { page, context: { newCDPSession: vi.fn(() => Promise.resolve(session)) }, destroy: vi.fn(() => Promise.resolve()) }; + getPage.mockResolvedValue(instance); + return { instance, session, page, response, emit }; +} + +function oauthRequest(body = form) { + return new Request(oauthUrl, { method: 'POST', body, headers: { 'content-type': 'application/x-www-form-urlencoded', 'user-agent': 'PixivIOSApp/fixture', authorization: 'Bearer authorization-fixture' } }); +} + +beforeEach(() => { + settings.playwrightWSEndpoint = 'wss://browser.test/playwright?token=endpoint-fixture'; + settings.requestTimeout = 30000; + getPage.mockReset(); + warn.mockReset(); +}); + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('targeted Playwright fetch retry', () => { + it.each([ + [biliUrl, 'GET', 200], + [biliUrl, 'GET', 403], + [biliUrl, 'POST', 412], + ['https://api.bilibili.com/x/other', 'GET', 412], + ['https://api.bilibili.com.evil.test/x/web-interface/popular', 'GET', 412], + ['http://api.bilibili.com/x/web-interface/popular', 'GET', 412], + ['https://api.bilibili.com:8443/x/web-interface/popular', 'GET', 412], + [rankingUrl, 'GET', 401], + [rankingUrl, 'GET', 412], + [oauthUrl, 'GET', 403], + [oauthUrl, 'POST', 403], + ['https://oauth.secure.pixiv.net/auth/other', 'POST', 403], + ])('does not replay %s %s status %s', async (url, method, status) => { + const response = new Response('original', { status: status as number }); + const native = vi.fn(() => Promise.resolve(response)); + const request = new Request(url as string, { method: method as string }); + + expect(await fetchWithPlaywrightRetry(request, native)).toBe(response); + expect(getPage).not.toHaveBeenCalled(); + expect(native).toHaveBeenCalledTimes(1); + expect(await response.text()).toBe('original'); + }); + + it('preserves native behavior when no WS endpoint is configured', async () => { + settings.playwrightWSEndpoint = ''; + const request = oauthRequest(); + const response = new Response('original', { status: 403 }); + const native = vi.fn(() => Promise.resolve(response)); + + expect(await fetchWithPlaywrightRetry(request, native)).toBe(response); + expect(native).toHaveBeenCalledWith(request); + expect(request.redirect).toBe('follow'); + expect(getPage).not.toHaveBeenCalled(); + }); + + it.each([ + [biliUrl, 412], + [rankingUrl, 403], + ])('replays one allowlisted GET and preserves response data for %s', async (url, status) => { + const { instance, session } = makeBrowser(url as string); + const request = new Request(url as string, { headers: { authorization: 'Bearer fixture', 'user-agent': 'upstream-client', referer: 'https://source.test/' } }); + const native = vi.fn(() => Promise.resolve(new Response('denied', { status: status as number }))); + + const response = await fetchWithPlaywrightRetry(request, native); + + expect(await response.json()).toEqual({ items: [{ id: 1 }] }); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('application/json; charset=utf-8'); + expect(response.headers.get('cache-control')).toBe('no-store'); + for (const name of ['content-encoding', 'content-length', 'connection', 'keep-alive', 'transfer-encoding', 'x-private-hop']) { + expect(response.headers.has(name)).toBe(false); + } + expect(getPage).toHaveBeenCalledExactlyOnceWith(url, { useConfiguredEndpoint: true, javaScriptEnabled: false, noGoto: true, closeTimeout: 0 }); + const replay = session.send.mock.calls.filter(([method]) => method === 'Fetch.continueRequest'); + expect(replay).toHaveLength(1); + expect(replay[0][1]).toEqual({ requestId: 'original', method: 'GET', headers: Array.from(request.headers, ([name, value]) => ({ name, value })) }); + expect(native).toHaveBeenCalledTimes(1); + expect(instance.destroy).toHaveBeenCalledTimes(1); + }); + + it('captures a small OAuth form before native fetch consumes it', async () => { + const { session, instance } = makeBrowser(oauthUrl, '{"access_token":"fixture"}'); + const request = oauthRequest(); + const native = vi.fn(async (outgoing: Request) => { + expect(outgoing.redirect).toBe('manual'); + expect(new TextDecoder().decode(await outgoing.arrayBuffer())).toBe(form); + return new Response('denied', { status: 403 }); + }); + + expect((await fetchWithPlaywrightRetry(request, native)).status).toBe(200); + + const replay = session.send.mock.calls.find(([method]) => method === 'Fetch.continueRequest')![1]; + expect(replay.method).toBe('POST'); + expect(Buffer.from(replay.postData, 'base64').toString()).toBe(form); + expect(replay.headers).toEqual(Array.from(request.headers, ([name, value]) => ({ name, value }))); + expect(native).toHaveBeenCalledTimes(1); + expect(instance.destroy).toHaveBeenCalledTimes(1); + }); + + it('does not replay a large form even without a Content-Length header', async () => { + const request = oauthRequest('refresh_token=' + 'x'.repeat(20 * 1024)); + const response = new Response('denied', { status: 403 }); + const native = vi.fn(async (outgoing: Request) => { + await outgoing.arrayBuffer(); + return response; + }); + + expect(await fetchWithPlaywrightRetry(request, native)).toBe(response); + expect(getPage).not.toHaveBeenCalled(); + }); + + it.each>([{ 'content-type': 'application/json' }, { 'content-type': 'application/x-www-form-urlencoded', 'content-length': '20000' }])( + 'does not replay an ineligible OAuth request body: %o', + async (headers) => { + const request = new Request(oauthUrl, { method: 'POST', body: form, headers }); + const response = new Response('denied', { status: 403 }); + const native = vi.fn(() => Promise.resolve(response)); + expect(await fetchWithPlaywrightRetry(request, native)).toBe(response); + expect(getPage).not.toHaveBeenCalled(); + } + ); + + it('blocks redirects, scripts, frames and subsequent documents without forwarding POST credentials', async () => { + const { page, emit, session, instance } = makeBrowser(oauthUrl); + page.goto.mockImplementation(() => { + emit(); + emit({ requestId: 'script', resourceType: 'Script' }); + emit({ requestId: 'frame', frameId: 'child-frame' }); + emit({ requestId: 'another-document' }); + emit({ requestId: 'redirect', redirectedRequestId: 'original', request: { url: 'https://other.test/collect' } }); + return Promise.reject(new Error('Navigation blocked: refresh-fixture endpoint-fixture')); + }); + const response = new Response('original denial', { status: 403 }); + const result = await fetchWithPlaywrightRetry(oauthRequest(), () => Promise.resolve(response)); + + expect(result).toBe(response); + expect(await result.text()).toBe('original denial'); + expect(session.send.mock.calls.filter(([method]) => method === 'Fetch.continueRequest')).toHaveLength(1); + expect(session.send.mock.calls.filter(([method]) => method === 'Fetch.failRequest').map(([, params]) => params.requestId)).toEqual(['script', 'frame', 'another-document', 'redirect']); + expect(instance.destroy).toHaveBeenCalledTimes(1); + expect(JSON.stringify(warn.mock.calls)).not.toMatch(/refresh-fixture|endpoint-fixture|other\.test/); + }); + + it('returns a repeated browser denial without another replay', async () => { + const { instance } = makeBrowser(rankingUrl, 'browser denial', 403); + const native = vi.fn(() => Promise.resolve(new Response('original denial', { status: 403 }))); + + const response = await fetchWithPlaywrightRetry(new Request(rankingUrl), native); + + expect(response.status).toBe(403); + expect(await response.text()).toBe('browser denial'); + expect(native).toHaveBeenCalledTimes(1); + expect(getPage).toHaveBeenCalledTimes(1); + expect(instance.destroy).toHaveBeenCalledTimes(1); + }); + + it('preserves native network errors without opening a browser', async () => { + const failure = new Error('native connection failed'); + await expect(fetchWithPlaywrightRetry(new Request(biliUrl), () => Promise.reject(failure))).rejects.toBe(failure); + expect(getPage).not.toHaveBeenCalled(); + }); + + it('keeps the original response when browser setup fails without logging endpoint details', async () => { + getPage.mockRejectedValue(new Error('wss://browser.test/playwright?token=endpoint-fixture')); + const response = new Response('original denial', { status: 412 }); + + expect(await fetchWithPlaywrightRetry(new Request(biliUrl), () => Promise.resolve(response))).toBe(response); + expect(await response.text()).toBe('original denial'); + expect(JSON.stringify(warn.mock.calls)).not.toContain('endpoint-fixture'); + }); + + it('propagates cancellation during navigation and awaits cleanup', async () => { + const { page, emit, instance } = makeBrowser(biliUrl); + page.goto.mockImplementation(() => { + emit(); + return new Promise(() => {}); + }); + const controller = new AbortController(); + const result = fetchWithPlaywrightRetry(new Request(biliUrl, { signal: controller.signal }), () => Promise.resolve(new Response('denied', { status: 412 }))); + const assertion = expect(result).rejects.toMatchObject({ name: 'AbortError' }); + await vi.waitFor(() => expect(page.goto).toHaveBeenCalled()); + controller.abort(); + + await assertion; + expect(instance.destroy).toHaveBeenCalledTimes(1); + expect(warn).not.toHaveBeenCalled(); + }); + + it('cleans up a browser that finishes opening after cancellation', async () => { + const { instance } = makeBrowser(biliUrl); + const opening = Promise.withResolvers(); + getPage.mockReturnValue(opening.promise); + const controller = new AbortController(); + const result = fetchWithPlaywrightRetry(new Request(biliUrl, { signal: controller.signal }), () => Promise.resolve(new Response('denied', { status: 412 }))); + const assertion = expect(result).rejects.toMatchObject({ name: 'AbortError' }); + await vi.waitFor(() => expect(getPage).toHaveBeenCalled()); + controller.abort(); + await assertion; + opening.resolve(instance); + await vi.waitFor(() => expect(instance.destroy).toHaveBeenCalledTimes(1)); + }); + + it('bounds a stalled browser response body and closes the browser', async () => { + vi.useFakeTimers(); + const { response: browserResponse, instance } = makeBrowser(biliUrl); + browserResponse.body.mockImplementation(() => new Promise(() => {})); + const original = new Response('native denial', { status: 412 }); + const result = fetchWithPlaywrightRetry(new Request(biliUrl), () => Promise.resolve(original)); + await vi.waitFor(() => expect(browserResponse.body).toHaveBeenCalled()); + await vi.advanceTimersByTimeAsync(30000); + + expect(await result).toBe(original); + expect(await original.text()).toBe('native denial'); + expect(instance.destroy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/lib/utils/playwright-fetch.ts b/lib/utils/playwright-fetch.ts new file mode 100644 index 000000000000..a9889e6681c1 --- /dev/null +++ b/lib/utils/playwright-fetch.ts @@ -0,0 +1,246 @@ +import { config } from '@/config'; +import logger from '@/utils/logger'; +import { getPlaywrightPage } from '@/utils/playwright'; + +const maxFormBytes = 16 * 1024; +const responseHeadersToRemove = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade', 'content-encoding', 'content-length']); + +type PlaywrightPage = Awaited>; +type PausedRequest = { + requestId: string; + request: { url: string }; + frameId: string; + resourceType: string; + redirectedRequestId?: string; +}; + +const abortError = () => new DOMException('The request was aborted', 'AbortError'); + +async function abortable(promise: Promise, signal: AbortSignal): Promise { + const aborted = Promise.withResolvers(); + const onAbort = () => aborted.reject(abortError()); + signal.addEventListener('abort', onAbort, { once: true }); + if (signal.aborted) { + onAbort(); + } + try { + return await Promise.race([promise, aborted.promise]); + } finally { + signal.removeEventListener('abort', onAbort); + } +} + +async function discardBody(body: { cancel(): Promise }) { + try { + await body.cancel(); + } catch { + // Cancellation can race with a stream that already failed or was closed. + } +} + +function retryStatus(request: Request): number | undefined { + const url = new URL(request.url); + if (request.method === 'GET') { + if (url.origin === 'https://api.bilibili.com' && url.pathname === '/x/web-interface/popular') { + return 412; + } + if (url.origin === 'https://app-api.pixiv.net' && url.pathname === '/v1/illust/ranking') { + return 403; + } + } else if ( + request.method === 'POST' && + url.origin === 'https://oauth.secure.pixiv.net' && + url.pathname === '/auth/token' && + request.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase() === 'application/x-www-form-urlencoded' && + Number(request.headers.get('content-length') || 0) <= maxFormBytes + ) { + return 403; + } +} + +async function readSmallForm(request: Request, signal: AbortSignal): Promise { + let reader: ReadableStreamDefaultReader | undefined; + const chunks: Uint8Array[] = []; + let length = 0; + try { + reader = request.body?.getReader(); + if (!reader) { + return; + } + while (true) { + // Read serially so the size limit also bounds buffered form data. + // eslint-disable-next-line no-await-in-loop + const { done, value } = await abortable(reader.read(), signal); + if (done) { + break; + } + length += value.byteLength; + if (length > maxFormBytes) { + return; + } + chunks.push(value); + } + const body = new Uint8Array(length); + let offset = 0; + for (const chunk of chunks) { + body.set(chunk, offset); + offset += chunk.byteLength; + } + return body; + } catch { + return; + } finally { + if (reader) { + // A tee branch's cancel promise can wait for the native fetch branch. + void discardBody(reader); + try { + reader.releaseLock(); + } catch { + // An aborted pending read may still own the stream lock. + } + } + } +} + +async function destroyPage(instance: PlaywrightPage) { + const controller = new AbortController(); + // The helper first bounds context.close at 5s, then disconnects the WebSocket. + const timer = setTimeout(() => controller.abort(), 7000); + try { + await abortable(instance.destroy(), controller.signal); + } catch { + logger.warn('Playwright request replay cleanup failed.'); + } finally { + clearTimeout(timer); + } +} + +async function replayRequest(request: Request, body?: Uint8Array): Promise { + const controller = new AbortController(); + const onAbort = () => controller.abort(); + request.signal.addEventListener('abort', onAbort, { once: true }); + const timeout = Math.max(1, Math.min(config.requestTimeout || 30000, 30000)); + const timer = setTimeout(onAbort, timeout); + const wait = (promise: Promise) => abortable(promise, controller.signal); + let instance: PlaywrightPage | undefined; + let closing = false; + try { + if (request.signal.aborted) { + throw abortError(); + } + const opening = getPlaywrightPage(request.url, { useConfiguredEndpoint: true, javaScriptEnabled: false, noGoto: true, closeTimeout: 0 }); + const cleanLateOpening = async () => { + try { + const lateInstance = await opening; + if (closing) { + await destroyPage(lateInstance); + } else { + instance = lateInstance; + } + } catch { + // The awaited setup reports its failure; only late success needs cleanup. + } + }; + void cleanLateOpening(); + instance = await wait(opening); + const { context, page } = instance; + const session = await wait(context.newCDPSession(page)); + const { frameTree } = await wait(session.send('Page.getFrameTree')); + let replayed = false; + session.on('Fetch.requestPaused', (event: PausedRequest) => { + const handleRequest = async () => { + // CDP pauses every redirect hop; page.route only handles the first. + if (closing || controller.signal.aborted || replayed || event.redirectedRequestId || event.resourceType !== 'Document' || event.frameId !== frameTree.frame.id || event.request.url !== request.url) { + await session.send('Fetch.failRequest', { requestId: event.requestId, errorReason: 'BlockedByClient' }); + return; + } + replayed = true; + await session.send('Fetch.continueRequest', { + requestId: event.requestId, + method: request.method, + headers: Array.from(request.headers, ([name, value]) => ({ name, value })), + ...(body && { postData: Buffer.from(body).toString('base64') }), + }); + }; + void handleRequest().catch(() => controller.abort()); + }); + await wait(session.send('Fetch.enable', { patterns: [{ urlPattern: '*', requestStage: 'Request' }], handleAuthRequests: false })); + const response = await wait(page.goto(request.url, { waitUntil: 'domcontentloaded', timeout })); + if (!response || response.url() !== request.url || !replayed) { + throw new Error('Playwright request replay did not return the expected response'); + } + const bytes = await wait(response.body()); + const rawHeaders = await wait(response.headersArray()); + const excludedHeaders = new Set(responseHeadersToRemove); + for (const { name, value } of rawHeaders) { + if (name.toLowerCase() === 'connection') { + for (const header of value.split(',')) { + excludedHeaders.add(header.trim().toLowerCase()); + } + } + } + const headers = new Headers(); + for (const { name, value } of rawHeaders) { + if (!excludedHeaders.has(name.toLowerCase())) { + headers.append(name, value); + } + } + const status = response.status(); + return new Response([204, 205, 304].includes(status) ? null : Uint8Array.from(bytes), { status, statusText: response.statusText(), headers }); + } finally { + closing = true; + clearTimeout(timer); + request.signal.removeEventListener('abort', onAbort); + if (instance) { + await destroyPage(instance); + } + } +} + +export default async function fetchWithPlaywrightRetry(request: Request, nativeFetch: (request: Request) => Promise): Promise { + const status = config.playwrightWSEndpoint ? retryStatus(request) : undefined; + if (status === undefined) { + return nativeFetch(request); + } + // Capture only these small OAuth forms before native fetch consumes the body. + const replay = request.clone(); + const bodyController = new AbortController(); + const bodyTimer = setTimeout(() => bodyController.abort(), 5000); + const stopBody = () => bodyController.abort(); + request.signal.addEventListener('abort', stopBody, { once: true }); + const bodyPromise = request.method === 'POST' ? readSmallForm(replay, bodyController.signal) : Promise.resolve(undefined); + try { + // These API endpoints must not forward credentials or form bodies to a redirect. + const response = await nativeFetch(new Request(request, { redirect: 'manual' })); + if (request.signal.aborted) { + throw abortError(); + } + if (response.status !== status || response.redirected) { + return response; + } + const body = await bodyPromise; + if (request.method === 'POST' && !body) { + return response; + } + try { + const replayedResponse = await replayRequest(replay, body); + if (request.signal.aborted) { + throw abortError(); + } + if (response.body) { + void discardBody(response.body); + } + return replayedResponse; + } catch { + if (request.signal.aborted) { + throw abortError(); + } + logger.warn('Playwright request replay failed; returning the original upstream response.'); + return response; + } + } finally { + clearTimeout(bodyTimer); + stopBody(); + request.signal.removeEventListener('abort', stopBody); + } +} diff --git a/lib/utils/playwright-remote.worker.ts b/lib/utils/playwright-remote.worker.ts new file mode 100644 index 000000000000..6c8460edb31b --- /dev/null +++ b/lib/utils/playwright-remote.worker.ts @@ -0,0 +1,32 @@ +import type { BrowserType } from 'patchright'; + +import { getPlaywrightCore } from '../../assets/build/playwright-worker.mjs'; +import { installNativeTransport, type TransportFetch, type TransportServer, withTransportFetch } from './playwright-transport.worker'; + +export type PlaywrightService = { fetch: TransportFetch }; +let playwrightService: PlaywrightService | undefined; +let serviceOrigin: string | undefined; +let clientPromise: Promise | undefined; + +export const setPlaywrightServiceBinding = (binding?: PlaywrightService, origin?: string) => { + playwrightService = binding; + serviceOrigin = origin; +}; + +const getClient = () => + (clientPromise ??= (async () => { + const core = await getPlaywrightCore(); + installNativeTransport(core.server as unknown as TransportServer); + return core.inprocess.playwright.chromium; + })()); + +export const connectRemotePlaywright = async (endpoint: string) => { + const url = new URL(endpoint); + url.protocol = url.protocol === 'wss:' ? 'https:' : url.protocol === 'ws:' ? 'http:' : url.protocol; + // Only route credentials through a service binding when its configured origin matches. + const binding = playwrightService; + const useBinding = binding && serviceOrigin && url.origin === new URL(serviceOrigin).origin; + const client = await getClient(); + const connect = () => client.connect(endpoint, { timeout: 20000 }); + return useBinding ? withTransportFetch(binding.fetch.bind(binding), connect) : connect(); +}; diff --git a/lib/utils/playwright-transport.worker.ts b/lib/utils/playwright-transport.worker.ts new file mode 100644 index 000000000000..8a82d720b62f --- /dev/null +++ b/lib/utils/playwright-transport.worker.ts @@ -0,0 +1,200 @@ +import { AsyncLocalStorage } from 'node:async_hooks'; + +export type TransportFetch = (url: string, options: RequestInit) => Promise; +type WorkerSocket = { + accept(): void; + send(data: string): void; + close(code?: number, reason?: string): void; + addEventListener(type: string, listener: (event: { data?: string | ArrayBuffer }) => void): void; +}; +type Message = Record; +type Progress = { race(promise: Promise): Promise }; +type ConnectOptions = { headers?: Record }; +export type TransportServer = { WebSocketTransport: { connect: (progress: Progress, endpoint: string, options?: ConnectOptions) => Promise } }; +const requestFetch = new AsyncLocalStorage(); +export const withTransportFetch = (fetchImpl: TransportFetch, callback: () => T) => requestFetch.run(fetchImpl, callback); +// Keep the native Upgrade fetch separate from the feed request rewriter. +const nativeFetch: TransportFetch = fetch.bind(globalThis); + +async function upgradeSocket(fetchImpl: TransportFetch, url: string, headers: Headers, signal: AbortSignal) { + const response = await fetchImpl(url, { headers, redirect: 'manual', signal }); + // progress.race can reject before a concurrent successful upgrade is delivered. + if (signal.aborted) { + try { + response.webSocket?.accept(); + response.webSocket?.close(1000, 'Connection setup cancelled'); + } catch { + // The abandoned socket may already have closed during cancellation. + } + throw new Error('Remote Playwright WebSocket connection was cancelled'); + } + return response; +} + +async function cancelFailedResponse(response: Response) { + try { + await response.body?.cancel(); + } catch { + // Preserve the failed upgrade status when its body is already unavailable. + } +} + +export function installNativeTransport(server: TransportServer, { handshakeTimeoutMs = 20000, fetchImpl = nativeFetch }: { handshakeTimeoutMs?: number; fetchImpl?: TransportFetch } = {}) { + server.WebSocketTransport.connect = async (progress, endpoint, options = {}) => { + const url = new URL(endpoint); + if (!['ws:', 'wss:'].includes(url.protocol) || url.username || url.password) { + throw new Error('Remote Playwright requires a WS(S) endpoint without URL userinfo'); + } + if (new Headers(options.headers).get('x-playwright-proxy')) { + throw new Error('Remote Playwright exposeNetwork is unavailable in this Worker transport'); + } + url.protocol = url.protocol === 'wss:' ? 'https:' : 'http:'; + const headers = new Headers(options.headers); + headers.set('Upgrade', 'websocket'); + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), handshakeTimeoutMs); + let socket: WorkerSocket | undefined; + try { + const pending = upgradeSocket(requestFetch.getStore() ?? fetchImpl, url.href, headers, controller.signal); + const response = await (progress ? progress.race(pending) : pending); + socket = response.webSocket; + if (response.status !== 101 || !socket) { + void cancelFailedResponse(response); + throw new Error(`Remote Playwright WebSocket upgrade failed with HTTP ${response.status}`); + } + const transport = new NativeTransport(socket, response.headers); + socket.accept(); + return transport; + } catch (error) { + controller.abort(); + try { + socket?.close(1000, 'Connection setup failed'); + } catch { + // Setup may fail after the peer has already closed the socket. + } + if (error instanceof Error && error.message.startsWith('Remote Playwright ')) { + throw error; + } + // oxlint-disable-next-line eslint/preserve-caught-error -- Native errors may contain endpoint credentials; do not expose them through cause. + throw new Error('Remote Playwright WebSocket connection failed or timed out'); + } finally { + clearTimeout(timer); + } + }; +} + +export class NativeTransport { + private pendingMessages: Message[] = []; + private closed = false; + private closing = false; + private closeNotified = false; + private closedPromise: Promise; + private resolveClosed!: () => void; + private messageHandler?: (message: Message) => void; + private closeHandler?: (reason?: string) => void; + private closeTimer?: ReturnType; + readonly headers: Array<{ name: string; value: string }>; + + constructor( + private socket: WorkerSocket, + headers: Headers + ) { + this.headers = Array.from(headers, ([name, value]) => ({ name, value })); + this.closedPromise = new Promise((resolve) => { + this.resolveClosed = resolve; + }); + socket.addEventListener('message', (event) => { + try { + const text = typeof event.data === 'string' ? event.data : new TextDecoder().decode(event.data as ArrayBuffer); + const message = JSON.parse(text); + if (this.messageHandler) { + this.deliver(message); + } else { + this.pendingMessages.push(message); + } + } catch { + this.close(); + } + }); + socket.addEventListener('close', () => this.finishClose()); + socket.addEventListener('error', () => this.close()); + } + + set onmessage(handler: ((message: Message) => void) | undefined) { + this.messageHandler = handler; + if (handler) { + for (const message of this.pendingMessages.splice(0)) { + this.deliver(message); + } + } + } + + get onmessage() { + return this.messageHandler; + } + + set onclose(handler: ((reason?: string) => void) | undefined) { + this.closeHandler = handler; + this.notifyClose(); + } + + get onclose() { + return this.closeHandler; + } + + deliver(message: Message) { + queueMicrotask(() => { + if (this.closed) { + return; + } + try { + this.messageHandler?.(message); + } catch { + this.close(); + } + }); + } + + notifyClose() { + if (!this.closed || !this.closeHandler || this.closeNotified) { + return; + } + this.closeNotified = true; + queueMicrotask(() => this.closeHandler?.('Remote Playwright connection closed')); + } + + finishClose() { + clearTimeout(this.closeTimer); + this.closed = true; + this.pendingMessages = []; + this.resolveClosed(); + this.notifyClose(); + } + + send(message: Message) { + if (this.closed || this.closing) { + throw new Error('Remote Playwright connection is closed'); + } + this.socket.send(JSON.stringify(message)); + } + + close() { + if (this.closed || this.closing) { + return; + } + this.closing = true; + try { + this.socket.close(1000, 'Client closed'); + } catch { + this.finishClose(); + } + if (!this.closed) { + this.closeTimer = setTimeout(() => this.finishClose(), 1000); + } + } + + async closeAndWait() { + this.close(); + await this.closedPromise; + } +} diff --git a/lib/utils/playwright.ts b/lib/utils/playwright.ts index 8c6c4d7d8ca5..890cdb1e47ed 100644 --- a/lib/utils/playwright.ts +++ b/lib/utils/playwright.ts @@ -83,13 +83,14 @@ const toBrowserlessCDPLaunchOptions = (currentProxy?: ProxyState | null): Browse }; }; -const getContextOptions = (): BrowserContextOptions => ({ +const getContextOptions = (javaScriptEnabled?: boolean): BrowserContextOptions => ({ + ...(javaScriptEnabled !== undefined && { javaScriptEnabled }), ignoreHTTPSErrors: true, userAgent: config.ua, }); // CDP > WS > local -const launchBrowser = async (currentProxy?: ProxyState | null) => { +const launchBrowser = async (currentProxy?: ProxyState | null, javaScriptEnabled?: boolean) => { let browser: Browser; if (config.playwrightCDPEndpoint) { browser = await chromium.connectOverCDP(getBrowserlessEndpoint(config.playwrightCDPEndpoint, toBrowserlessCDPLaunchOptions(currentProxy))); @@ -99,7 +100,7 @@ const launchBrowser = async (currentProxy?: ProxyState | null) => { browser = await chromium.launch(getLaunchOptions(currentProxy)); } try { - const context = await browser.newContext(getContextOptions()); + const context = await browser.newContext(getContextOptions(javaScriptEnabled)); return { browser, context }; } catch (error) { await browser.close(); @@ -132,6 +133,7 @@ export default async function outPlaywright() { // No-op in Node.js environment (used by Worker build via alias) export const setBrowserBinding = (_binding: any) => {}; +export const setPlaywrightServiceBinding = (_binding?: unknown, _origin?: string) => {}; /** * @returns Playwright page @@ -143,9 +145,14 @@ export const getPlaywrightPage = async ( closeTimeout?: number; gotoConfig?: GotoOptions; noGoto?: boolean; + javaScriptEnabled?: boolean; + useConfiguredEndpoint?: boolean; onBeforeLoad?: (page: Page, context?: BrowserContext) => Promise | void; } = {} ) => { + if (instanceOptions.useConfiguredEndpoint && !config.playwrightWSEndpoint) { + throw new Error('Configure PLAYWRIGHT_WS_ENDPOINT to use the remote Playwright browser.'); + } let allowProxy = false; const proxyRegex = new RegExp(proxy.proxyObj.url_regex); let urlHandler: URL | undefined; @@ -162,7 +169,7 @@ export const getPlaywrightPage = async ( const currentProxy = proxy.getCurrentProxy(); const currentProxyState = currentProxy && allowProxy ? currentProxy : null; const hasProxy = Boolean(getProxyOptions(currentProxyState).proxy); - const { browser, context } = await launchBrowser(currentProxyState); + const { browser, context } = await launchBrowser(currentProxyState, instanceOptions.javaScriptEnabled); const closeTimer = scheduleClose(browser, instanceOptions.closeTimeout); const destroy = async () => { clearTimeout(closeTimer); diff --git a/lib/utils/playwright.worker.ts b/lib/utils/playwright.worker.ts index a4aa2f885c73..86c2cd4a549b 100644 --- a/lib/utils/playwright.worker.ts +++ b/lib/utils/playwright.worker.ts @@ -1,28 +1,39 @@ -// Worker-compatible Playwright using Cloudflare Browser Run. -import type { Browser, Page } from '@cloudflare/playwright'; +// Worker-compatible Playwright with ordinary remote WebSocket and Browser Run support. import { launch } from '@cloudflare/playwright'; +import type { Browser, Page } from 'patchright'; + +import { config } from '@/config'; import logger from './logger'; +import { connectRemotePlaywright } from './playwright-remote.worker'; -type GotoOptions = Parameters[1]; +export { setPlaywrightServiceBinding } from './playwright-remote.worker'; -let browserBinding: any = null; +type GotoOptions = Parameters[1]; +let browserBinding: any; export const setBrowserBinding = (binding: any) => { browserBinding = binding; }; -const getBrowserBinding = () => { - if (!browserBinding) { - throw new Error('Browser Run API not available. This route requires Cloudflare Browser Run which is only available in remote mode. Use `wrangler dev --remote` or deploy to Cloudflare Workers.'); +const launchBrowser = async (options: { javaScriptEnabled?: boolean; useConfiguredEndpoint?: boolean } = {}) => { + let browser: Browser; + if (config.playwrightWSEndpoint) { + browser = await connectRemotePlaywright(config.playwrightWSEndpoint); + } else { + if (options.useConfiguredEndpoint) { + throw new Error('Configure PLAYWRIGHT_WS_ENDPOINT to use the remote Playwright browser.'); + } + if (!browserBinding) { + throw new Error('Configure PLAYWRIGHT_WS_ENDPOINT or a Cloudflare BROWSER binding. Browser Run requires remote mode or a deployed Worker.'); + } + browser = (await launch(browserBinding, { keep_alive: 60000 })) as unknown as Browser; } - return browserBinding; -}; - -const launchBrowser = async () => { - const browser = await launch(getBrowserBinding(), { keep_alive: 60000 }); try { - const context = await browser.newContext({ ignoreHTTPSErrors: true }); + const context = await browser.newContext({ + ignoreHTTPSErrors: true, + ...(options.javaScriptEnabled !== undefined && { javaScriptEnabled: options.javaScriptEnabled }), + }); return { browser, context }; } catch (error) { await browser.close(); @@ -30,43 +41,58 @@ const launchBrowser = async () => { } }; -const scheduleClose = (browser: Browser, timeout = 30000) => - timeout === 0 - ? undefined - : setTimeout(() => { - void browser.close(); - }, timeout); +const cleanup = ({ browser, context }: Awaited>, timeout = 30000) => { + let closing: Promise | undefined; + let timer: ReturnType | undefined; + const destroy = () => { + clearTimeout(timer); + return (closing ??= (async () => { + let contextTimer: ReturnType | undefined; + try { + await Promise.race([ + context.close(), + new Promise((_resolve, reject) => { + contextTimer = setTimeout(() => reject(new Error('Playwright context cleanup timed out')), 5000); + }), + ]); + } finally { + clearTimeout(contextTimer); + // Remote Browser.close() disconnects this client; it does not stop the shared server. + await browser.close(); + } + })()); + }; + if (timeout !== 0) { + timer = setTimeout(() => { + void destroy().catch(() => logger.warn('Playwright browser cleanup failed')); + }, timeout); + } + return destroy; +}; -/** - * @returns Playwright browser context (native `newPage()` shares state across calls) - */ +/** @returns Playwright browser context (native newPage() shares state across calls) */ export default async function outPlaywright() { - const { browser, context } = await launchBrowser(); - scheduleClose(browser); - return context; + const session = await launchBrowser(); + cleanup(session); + return session.context; } -/** - * @returns Playwright page - */ +/** @returns Playwright page with explicit, idempotent cleanup */ export const getPlaywrightPage = async ( url: string, instanceOptions: { // Set to zero only when the caller always awaits destroy() in finally. closeTimeout?: number; gotoConfig?: GotoOptions; + javaScriptEnabled?: boolean; noGoto?: boolean; + useConfiguredEndpoint?: boolean; onBeforeLoad?: (page: Page, context?: Awaited>['context']) => Promise | void; } = {} ) => { - logger.debug(`Launching Cloudflare Browser for: ${url}`); - - const { browser, context } = await launchBrowser(); - const closeTimer = scheduleClose(browser, instanceOptions.closeTimeout); - const destroy = async () => { - clearTimeout(closeTimer); - await browser.close(); - }; + const session = await launchBrowser(instanceOptions); + const { context } = session; + const destroy = cleanup(session, instanceOptions.closeTimeout); try { const page = await context.newPage(); if (instanceOptions.onBeforeLoad) { @@ -77,10 +103,9 @@ export const getPlaywrightPage = async ( } return { context, destroy, page }; } catch (error) { - logger.error(`Playwright page setup failed: ${error}`); await destroy(); throw error; } }; -export { type Page } from '@cloudflare/playwright'; +export { type Page } from 'patchright'; diff --git a/lib/utils/request-rewriter/fetch.worker.ts b/lib/utils/request-rewriter/fetch.worker.ts index 4b467becde77..86df90b8ee4b 100644 --- a/lib/utils/request-rewriter/fetch.worker.ts +++ b/lib/utils/request-rewriter/fetch.worker.ts @@ -2,6 +2,7 @@ // Simplified version without proxy, rate limiting, or header-generator import { config } from '@/config'; import logger from '@/utils/logger'; +import fetchWithPlaywrightRetry from '@/utils/playwright-fetch'; // Static browser headers (Chrome-like fingerprint) const STATIC_BROWSER_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_6_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'; @@ -23,7 +24,8 @@ const originalFetch = fetch; const wrappedFetch = (input: RequestInfo | URL, init?: RequestInit): Promise => { const request = new Request(input, init); - logger.debug(`Outgoing request: ${request.method} ${request.url}`); + const requestUrl = new URL(request.url); + logger.debug(`Outgoing request: ${request.method} ${requestUrl.origin}${requestUrl.pathname}`); // Set User-Agent if not provided if (config.isDefaultUA) { @@ -56,7 +58,7 @@ const wrappedFetch = (input: RequestInfo | URL, init?: RequestInit): Promise name.replace(/^node:/, ''))); + +type Metadata = Record<'package.json' | 'browsers.json', unknown>; + +export function inlinePackageMetadata(source: string, metadata: Metadata) { + const expressions = { + 'package.json': /require\(import_path\d*\.default\.join\(packageRoot, "package\.json"\)\)/g, + 'browsers.json': /require\(import_path\d*\.default\.join\(packageRoot, "browsers\.json"\)\)/g, + }; + for (const filename of Object.keys(expressions) as Array) { + const expression = expressions[filename]; + if (source.matchAll(expression).toArray().length !== 1) { + throw new Error(`Patchright Worker build expected exactly one metadata require: ${filename}`); + } + source = source.replace(expression, () => JSON.stringify(metadata[filename])); + } + return source; +} + +export function addBuiltinRequireMap(source: string) { + const usedBuiltins = [ + ...new Set( + source + .matchAll(/(?:__require|require)\(["']([^"'\n]+)["']\)/g) + .toArray() + .map((match) => match[1].replace(/^node:/, '')) + .filter((name) => builtinNames.has(name)) + ), + ].sort((left, right) => left.localeCompare(right)); + // Avoid the project's node:module shim here: its namespace export creates a Rolldown runtime cycle. + const imports = usedBuiltins.map((name, index) => `import * as builtin${index} from ${JSON.stringify(name === 'module' ? 'module' : `node:${name}`)};`); + const entries = usedBuiltins.flatMap((name, index) => [name, `node:${name}`].map((alias) => `${JSON.stringify(alias)}: (builtin${index}.default ?? builtin${index})`)); + // Workers cannot dynamically require every supported Node builtin. Static imports + // also keep the optional Node-only paths from escaping this module's require map. + const requireMap = `const builtinMap = Object.freeze({${entries.join(',')}}); +const require = (id) => { + if (Object.hasOwn(builtinMap, id)) return builtinMap[id]; + throw new Error('Playwright module is unavailable in Workers: ' + id); +};`; + return { source: `${imports.join('\n')}\n${requireMap}\n${source}`, usedBuiltins }; +} + +export async function buildPlaywrightWorker(outputDir = path.join(repoRoot, 'assets/build')) { + const [packageJson, browsersJson, coreSource] = await Promise.all([readFile(path.join(packageDir, 'package.json'), 'utf8'), readFile(path.join(packageDir, 'browsers.json'), 'utf8'), readFile(corePath, 'utf8')]); + const packageMetadata = JSON.parse(packageJson); + if (packageMetadata.name !== 'patchright-core') { + throw new Error('Playwright Worker build must use the installed patchright-core package'); + } + const source = inlinePackageMetadata(coreSource, { + 'package.json': packageMetadata, + 'browsers.json': JSON.parse(browsersJson), + }); + const result = await build({ + stdin: { + // Patchright initializes AbortController and other request-scoped state. + // Keep its first import inside the caller's Worker request handler. + contents: `let corePromise; +export function getPlaywrightCore() { + return corePromise ??= import('rsshub-playwright-core').then((module) => module.default ?? module); +}`, + loader: 'js', + resolveDir: repoRoot, + sourcefile: 'playwright-worker.mjs', + }, + bundle: true, + format: 'esm', + platform: 'node', + target: 'esnext', + // Remote Chromium needs neither the local BiDi mapper nor macOS file watching. + // A runtime call to an unbundled optional module fails through the require map. + external: ['chromium-bidi/*', 'fsevents'], + write: false, + define: { + __dirname: JSON.stringify('/bundle/lib'), + __filename: JSON.stringify('/bundle/lib/coreBundle.js'), + }, + plugins: [ + { + name: 'playwright-worker-metadata', + setup(builder) { + builder.onResolve({ filter: /^rsshub-playwright-core$/ }, () => ({ path: corePath })); + builder.onLoad({ filter: /\/lib\/coreBundle\.js$/ }, (args) => { + if (args.path === corePath) { + return { contents: source, loader: 'js', resolveDir: path.dirname(corePath) }; + } + }); + }, + }, + ], + }); + const generated = addBuiltinRequireMap(result.outputFiles[0].text); + await mkdir(outputDir, { recursive: true }); + await Promise.all([ + writeFile(path.join(outputDir, 'playwright-worker.mjs'), generated.source), + writeFile(path.join(outputDir, 'playwright-worker.json'), JSON.stringify({ version: packageMetadata.version, builtins: generated.usedBuiltins }, null, 2) + '\n'), + ]); + return { version: packageMetadata.version, bytes: Buffer.byteLength(generated.source), builtins: generated.usedBuiltins }; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === import.meta.filename) { + const result = await buildPlaywrightWorker(); + process.stdout.write(`Built Playwright ${result.version} Worker client (${result.bytes} bytes)\n`); +} diff --git a/tests/playwright-fetch.worker.test.ts b/tests/playwright-fetch.worker.test.ts new file mode 100644 index 000000000000..d2b2642f4a49 --- /dev/null +++ b/tests/playwright-fetch.worker.test.ts @@ -0,0 +1 @@ +import '../lib/utils/playwright-fetch.test'; diff --git a/tests/playwright-lifecycle.worker.test.ts b/tests/playwright-lifecycle.worker.test.ts index 4b7599eba80b..0fc45a53c6b6 100644 --- a/tests/playwright-lifecycle.worker.test.ts +++ b/tests/playwright-lifecycle.worker.test.ts @@ -2,17 +2,27 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { getPlaywrightPage, setBrowserBinding } from '../lib/utils/playwright.worker'; -const mocks = vi.hoisted(() => ({ launch: vi.fn(), newContext: vi.fn(), newPage: vi.fn(), goto: vi.fn(), close: vi.fn() })); +const mocks = vi.hoisted(() => ({ endpoint: undefined as string | undefined, launch: vi.fn(), newContext: vi.fn(), newPage: vi.fn(), goto: vi.fn(), close: vi.fn(), contextClose: vi.fn(), connect: vi.fn() })); vi.mock('@cloudflare/playwright', () => ({ launch: mocks.launch })); -vi.mock('../lib/utils/logger', () => ({ default: { debug: vi.fn(), error: vi.fn() } })); +vi.mock('../lib/utils/playwright-remote.worker', () => ({ connectRemotePlaywright: mocks.connect, setPlaywrightServiceBinding: vi.fn() })); +vi.mock('../lib/config', () => ({ + config: { + get playwrightWSEndpoint() { + return mocks.endpoint; + }, + }, +})); +vi.mock('../lib/utils/logger', () => ({ default: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() } })); beforeEach(() => { vi.useFakeTimers(); vi.clearAllMocks(); + mocks.endpoint = undefined; mocks.goto.mockResolvedValue(undefined); mocks.newPage.mockResolvedValue({ goto: mocks.goto }); - mocks.newContext.mockResolvedValue({ newPage: mocks.newPage }); + mocks.newContext.mockResolvedValue({ newPage: mocks.newPage, close: mocks.contextClose }); mocks.launch.mockResolvedValue({ newContext: mocks.newContext, close: mocks.close }); + mocks.connect.mockResolvedValue({ newContext: mocks.newContext, close: mocks.close }); setBrowserBinding({}); }); @@ -21,6 +31,45 @@ afterEach(() => { }); describe('Worker browser lifecycle', () => { + it('prefers the configured ordinary WebSocket server over BROWSER', async () => { + mocks.endpoint = 'wss://browser.example/playwright?token=test'; + const { destroy } = await getPlaywrightPage('about:blank', { noGoto: true, javaScriptEnabled: false }); + expect(mocks.connect).toHaveBeenCalledWith(mocks.endpoint); + expect(mocks.launch).not.toHaveBeenCalled(); + expect(mocks.newContext).toHaveBeenCalledWith({ ignoreHTTPSErrors: true, javaScriptEnabled: false }); + await Promise.all([destroy(), destroy()]); + expect(mocks.contextClose).toHaveBeenCalledOnce(); + expect(mocks.close).toHaveBeenCalledOnce(); + }); + + it('requires a configured endpoint when explicitly requested', async () => { + await expect(getPlaywrightPage('about:blank', { useConfiguredEndpoint: true })).rejects.toThrow('PLAYWRIGHT_WS_ENDPOINT'); + expect(mocks.launch).not.toHaveBeenCalled(); + }); + + it('does not silently switch to BROWSER when the configured endpoint fails', async () => { + mocks.endpoint = 'wss://browser.example/playwright'; + mocks.connect.mockRejectedValueOnce(new Error('Connection refused')); + await expect(getPlaywrightPage('about:blank')).rejects.toThrow('Connection refused'); + expect(mocks.launch).not.toHaveBeenCalled(); + }); + + it('disconnects even when closing the context fails', async () => { + mocks.contextClose.mockRejectedValueOnce(new Error('Context close failed')); + const { destroy } = await getPlaywrightPage('about:blank', { noGoto: true }); + await expect(destroy()).rejects.toThrow('Context close failed'); + expect(mocks.close).toHaveBeenCalledOnce(); + }); + + it('disconnects if the context close RPC never settles', async () => { + mocks.contextClose.mockReturnValueOnce(new Promise(() => {})); + const { destroy } = await getPlaywrightPage('about:blank', { noGoto: true }); + const closing = expect(destroy()).rejects.toThrow('cleanup timed out'); + await vi.advanceTimersByTimeAsync(5000); + await closing; + expect(mocks.close).toHaveBeenCalledOnce(); + }); + it('keeps a shared browser alive for the requested lifetime and closes it immediately on cleanup', async () => { const { destroy } = await getPlaywrightPage('https://example.com', { noGoto: true, closeTimeout: 120000 }); await vi.advanceTimersByTimeAsync(30000); diff --git a/tests/playwright-transport.test.ts b/tests/playwright-transport.test.ts new file mode 100644 index 000000000000..aeb0ad7f43df --- /dev/null +++ b/tests/playwright-transport.test.ts @@ -0,0 +1,191 @@ +// oxlint-disable unicorn/prefer-add-event-listener -- Playwright transports expose protocol callbacks, not EventTarget methods. +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { installNativeTransport, type TransportFetch, type TransportServer, withTransportFetch } from '../lib/utils/playwright-transport.worker'; + +type SocketEvent = { data?: string | ArrayBuffer }; + +class FakeSocket { + listeners = new Map void>>(); + accept = vi.fn(); + send = vi.fn(); + close = vi.fn(); + + addEventListener(type: string, listener: (event: SocketEvent) => void) { + const listeners = this.listeners.get(type) ?? []; + listeners.push(listener); + this.listeners.set(type, listeners); + } + + emit(type: string, event: SocketEvent = {}) { + const listeners = this.listeners.get(type) ?? []; + for (const listener of listeners) { + listener(event); + } + } +} + +const progress = { race: (promise: Promise) => promise }; +const responseFor = (socket: FakeSocket) => ({ status: 101, headers: new Headers({ 'x-playwright-test': 'ready' }), webSocket: socket }) as Awaited>; + +function createServer(fetchImpl: TransportFetch, handshakeTimeoutMs = 20000) { + const server: TransportServer = { WebSocketTransport: { connect: vi.fn() } }; + installNativeTransport(server, { fetchImpl, handshakeTimeoutMs }); + return server; +} + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('Worker Playwright WebSocket transport', () => { + it('preserves the configured endpoint and protocol headers in a manual-redirect upgrade', async () => { + const socket = new FakeSocket(); + const fetchImpl = vi.fn().mockResolvedValue(responseFor(socket)); + const server = createServer(fetchImpl); + const transport = await server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright?token=test-token', { + headers: { 'User-Agent': 'Playwright/1.62.2', 'x-playwright-browser': 'chromium', Authorization: 'test-authorization' }, + }); + const [url, options] = fetchImpl.mock.calls[0]; + expect(url).toBe('https://browser.example/playwright?token=test-token'); + expect(options.redirect).toBe('manual'); + expect(new Headers(options.headers).get('upgrade')).toBe('websocket'); + expect(new Headers(options.headers).get('user-agent')).toBe('Playwright/1.62.2'); + expect(new Headers(options.headers).get('x-playwright-browser')).toBe('chromium'); + expect(new Headers(options.headers).get('authorization')).toBe('test-authorization'); + expect(socket.accept).toHaveBeenCalledOnce(); + expect(transport.headers).toContainEqual({ name: 'x-playwright-test', value: 'ready' }); + transport.send({ id: 1, method: 'initialize' }); + expect(socket.send).toHaveBeenCalledWith('{"id":1,"method":"initialize"}'); + socket.emit('close'); + }); + + it.each([302, 428])('rejects HTTP %i without forwarding or exposing the response body', async (status) => { + const cancel = vi.fn().mockResolvedValue(undefined); + const fetchImpl = vi.fn().mockResolvedValue({ status, headers: new Headers(), body: { cancel } } as unknown as Awaited>); + const server = createServer(fetchImpl); + await expect(server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright?token=test-token')).rejects.toThrow(`Remote Playwright WebSocket upgrade failed with HTTP ${status}`); + expect(fetchImpl).toHaveBeenCalledOnce(); + expect(cancel).toHaveBeenCalledOnce(); + }); + + it('rejects unsupported endpoint credentials and network exposure before fetching', async () => { + const fetchImpl = vi.fn(); + const server = createServer(fetchImpl); + await expect(server.WebSocketTransport.connect(progress, 'https://browser.example/playwright')).rejects.toThrow('requires a WS(S) endpoint'); + await expect(server.WebSocketTransport.connect(progress, 'wss://user:password@browser.example/playwright')).rejects.toThrow('without URL userinfo'); + await expect(server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright', { headers: { 'X-Playwright-Proxy': '*' } })).rejects.toThrow('exposeNetwork is unavailable'); + expect(fetchImpl).not.toHaveBeenCalled(); + }); + + it('buffers protocol messages received before the client installs its handler', async () => { + const socket = new FakeSocket(); + socket.accept.mockImplementation(() => socket.emit('message', { data: '{"id":1,"result":{}}' })); + const server = createServer(vi.fn().mockResolvedValue(responseFor(socket))); + const transport = await server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright'); + socket.emit('message', { data: new TextEncoder().encode('{"id":2,"result":{}}').buffer }); + const onMessage = vi.fn(); + transport.onmessage = onMessage; + await Promise.resolve(); + expect(onMessage.mock.calls.map(([message]) => message.id)).toEqual([1, 2]); + socket.emit('close'); + }); + + it('aborts a timed-out handshake and sanitizes transport errors', async () => { + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }); + const fetchImpl = vi.fn( + (_url, options) => + new Promise((_resolve, reject) => { + options.signal?.addEventListener('abort', () => reject(new Error('private endpoint and credential details')), { once: true }); + }) + ); + const server = createServer(fetchImpl, 100); + const assertion = expect(server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright')).rejects.toThrow('Remote Playwright WebSocket connection failed or timed out'); + await vi.advanceTimersByTimeAsync(100); + await assertion; + expect(fetchImpl.mock.calls[0][1].signal?.aborted).toBe(true); + expect(vi.getTimerCount()).toBe(0); + }); + + it('closes an upgrade delivered after Playwright cancels the connection', async () => { + const pending = Promise.withResolvers>>(); + const fetchImpl = vi.fn().mockReturnValue(pending.promise); + const server = createServer(fetchImpl); + const cancelledProgress = { race: (promise: Promise): Promise => Promise.race([promise, Promise.reject(new Error('Connect cancelled'))]) }; + await expect(server.WebSocketTransport.connect(cancelledProgress, 'wss://browser.example/playwright')).rejects.toThrow('failed or timed out'); + expect(fetchImpl.mock.calls[0][1].signal?.aborted).toBe(true); + const socket = new FakeSocket(); + pending.resolve(responseFor(socket)); + await Promise.resolve(); + await Promise.resolve(); + expect(socket.accept).toHaveBeenCalledOnce(); + expect(socket.close).toHaveBeenCalledWith(1000, 'Connection setup cancelled'); + }); + + it('closes exactly once and waits for the peer close event', async () => { + const socket = new FakeSocket(); + const server = createServer(vi.fn().mockResolvedValue(responseFor(socket))); + const transport = await server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright'); + const onClose = vi.fn(); + transport.onclose = onClose; + const closed = vi.fn(); + const closing = (async () => { + await transport.closeAndWait(); + closed(); + })(); + transport.close(); + await Promise.resolve(); + expect(closed).not.toHaveBeenCalled(); + expect(socket.close).toHaveBeenCalledOnce(); + expect(() => transport.send({ id: 1 })).toThrow('connection is closed'); + socket.emit('close'); + await closing; + socket.emit('close'); + await Promise.resolve(); + expect(closed).toHaveBeenCalledOnce(); + expect(onClose).toHaveBeenCalledOnce(); + }); + + it('bounds close when the peer is silent and closes malformed protocol messages', async () => { + vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }); + const socket = new FakeSocket(); + const server = createServer(vi.fn().mockResolvedValue(responseFor(socket))); + const transport = await server.WebSocketTransport.connect(progress, 'wss://browser.example/playwright'); + socket.emit('message', { data: 'invalid JSON' }); + expect(socket.close).toHaveBeenCalledOnce(); + const closing = transport.closeAndWait(); + await vi.advanceTimersByTimeAsync(1000); + await closing; + const lateOnClose = vi.fn(); + transport.onclose = lateOnClose; + await Promise.resolve(); + expect(lateOnClose).toHaveBeenCalledOnce(); + expect(vi.getTimerCount()).toBe(0); + }); + + it('keeps concurrent service-binding overrides isolated and restores native fetch', async () => { + const sockets = [new FakeSocket(), new FakeSocket(), new FakeSocket()]; + const native = vi.fn().mockResolvedValue(responseFor(sockets[0])); + const first = vi.fn().mockResolvedValue(responseFor(sockets[1])); + const second = vi.fn().mockResolvedValue(responseFor(sockets[2])); + const server = createServer(native); + await Promise.all([ + withTransportFetch(first, async () => { + await Promise.resolve(); + await server.WebSocketTransport.connect(progress, 'wss://first.example/playwright'); + }), + withTransportFetch(second, async () => { + await Promise.resolve(); + await server.WebSocketTransport.connect(progress, 'wss://second.example/playwright'); + }), + ]); + expect(first.mock.calls[0][0]).toBe('https://first.example/playwright'); + expect(second.mock.calls[0][0]).toBe('https://second.example/playwright'); + expect(native).not.toHaveBeenCalled(); + await server.WebSocketTransport.connect(progress, 'wss://native.example/playwright'); + expect(native).toHaveBeenCalledOnce(); + for (const socket of sockets) { + socket.emit('close'); + } + }); +}); diff --git a/tests/playwright-worker-build.test.ts b/tests/playwright-worker-build.test.ts new file mode 100644 index 000000000000..131dce73d1ed --- /dev/null +++ b/tests/playwright-worker-build.test.ts @@ -0,0 +1,66 @@ +import { execFile } from 'node:child_process'; +import { mkdtemp, readFile, rm } from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { promisify } from 'node:util'; + +import { afterAll, describe, expect, it } from 'vitest'; + +import { addBuiltinRequireMap, buildPlaywrightWorker, inlinePackageMetadata } from '../scripts/workflow/build-playwright-worker'; + +const exec = promisify(execFile); +let outputDir: string | undefined; + +afterAll(async () => { + if (outputDir) { + await rm(outputDir, { recursive: true, force: true }); + } +}); + +describe('Playwright Worker client build', () => { + it('keeps the native module namespace out of the Worker shim dependency cycle', () => { + const result = addBuiltinRequireMap('require("node:module"); require("process");'); + expect(result.source).toContain('import * as builtin0 from "module";'); + expect(result.source).not.toContain('from "node:module"'); + expect(result.source).toContain('"node:module": (builtin0.default ?? builtin0)'); + }); + + it('fails visibly when a dependency update changes or duplicates a metadata require', () => { + const packageSource = 'require(import_path8.default.join(packageRoot, "package.json"))'; + const browsersSource = 'require(import_path19.default.join(packageRoot, "browsers.json"))'; + const metadata = { 'package.json': { version: '1.62.2', description: '$&' }, 'browsers.json': { browsers: [] } }; + + expect(inlinePackageMetadata(`${packageSource};${browsersSource}`, metadata)).toBe(`${JSON.stringify(metadata['package.json'])};${JSON.stringify(metadata['browsers.json'])}`); + expect(() => inlinePackageMetadata(packageSource, metadata)).toThrow('exactly one metadata require: browsers.json'); + expect(() => inlinePackageMetadata(`${packageSource};${packageSource};${browsersSource}`, metadata)).toThrow('exactly one metadata require: package.json'); + }); + + it('builds the installed protocol client without initializing it before the first request', async () => { + outputDir = await mkdtemp(path.join(os.tmpdir(), 'rsshub-playwright-build-')); + const result = await buildPlaywrightWorker(outputDir); + const moduleUrl = pathToFileURL(path.join(outputDir, 'playwright-worker.mjs')).href; + const script = ` + import assert from 'node:assert/strict'; + const NativeAbortController = globalThis.AbortController; + globalThis.AbortController = class { + constructor() { throw new Error('Protocol initialized outside the request'); } + }; + globalThis.fetch = () => { throw new Error('Build smoke must not access the network'); }; + const { getPlaywrightCore } = await import(${JSON.stringify(moduleUrl)}); + globalThis.AbortController = NativeAbortController; + const first = await getPlaywrightCore(); + const second = await getPlaywrightCore(); + assert.equal(first, second); + assert.equal(first.getPlaywrightVersion(), ${JSON.stringify(result.version)}); + assert.equal(typeof first.inprocess.playwright.chromium.connect, 'function'); + assert.equal(typeof first.server.WebSocketTransport.connect, 'function'); + process.stdout.write('lazy-client-ready'); + `; + const { stdout } = await exec(process.execPath, ['--input-type=module', '-e', script], { timeout: 20000 }); + expect(stdout).toBe('lazy-client-ready'); + expect(result.builtins).toContain('process'); + const manifest = JSON.parse(await readFile(path.join(outputDir, 'playwright-worker.json'), 'utf8')); + expect(manifest.version).toBe(result.version); + }, 30000); +}); diff --git a/wrangler.toml b/wrangler.toml index 7db2ae2535ac..7c2c9109fd1b 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -29,7 +29,15 @@ binding = "BROWSER" [[kv_namespaces]] binding = "CACHE" +# PLAYWRIGHT_WS_ENDPOINT takes priority over BROWSER; use a matching Playwright version. +# Optional service binding when the WebSocket server is another Worker on this account. +# [[services]] +# binding = "PLAYWRIGHT_SERVICE" +# service = "your-playwright-service" + [vars] +# Only use the service binding for this endpoint origin. +# PLAYWRIGHT_SERVICE_ORIGIN = "https://your-playwright-service.example.com" # Environment variables can be set here or via wrangler secret # DEBUG_INFO = "false" From 437659812d29ff47d308588cec1c3a19f50cab41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:39:37 +0800 Subject: [PATCH 0726/1190] chore(deps): bump imapflow from 1.7.7 to 1.7.8 (#23233) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.7.7 to 1.7.8. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.7.7...v1.7.8) --- updated-dependencies: - dependency-name: imapflow dependency-version: 1.7.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 853fc61d403e..09f603b32e46 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.7.7", + "imapflow": "1.7.8", "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 084bb53a76d9..f1888cd3e3d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.7.7 - version: 1.7.7 + specifier: 1.7.8 + version: 1.7.8 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4397,8 +4397,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.7.7: - resolution: {integrity: sha512-YEjC2Lk10NjkeQXRHs+/dsXZcWlwmhWaCu0NKTQjJN8U1NLeHzIkkFQ+ol0habOhs/ofdp5CBSYwLgaVtBbB+g==} + imapflow@1.7.8: + resolution: {integrity: sha512-dJoCIdZOJh26Rn2PdwEzwj0bRDgGBxxX38pio534FagIHVuR2l0SAfLr6sJo32YfuJHiSs/U2ntNDHnMg3/Hlg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -5690,6 +5690,10 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} + socks@2.8.10: + resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.9: resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} @@ -9856,7 +9860,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.7.7: + imapflow@1.7.8: dependencies: '@zone-eu/mailsplit': 5.4.16 encoding-japanese: 2.3.0 @@ -11507,10 +11511,15 @@ snapshots: dependencies: agent-base: 7.1.4 debug: 4.4.3 - socks: 2.8.9 + socks: 2.8.10 transitivePeerDependencies: - supports-color + socks@2.8.10: + dependencies: + ip-address: 10.7.0 + smart-buffer: 4.2.0 + socks@2.8.9: dependencies: ip-address: 10.7.0 From 07e38e9e38c37b110ec75ee169cd2a22e63c49dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:41:07 +0800 Subject: [PATCH 0727/1190] chore(deps): bump @hono/zod-openapi in the hono group (#23225) Bumps the hono group with 1 update: [@hono/zod-openapi](https://github.com/honojs/middleware/tree/HEAD/packages/zod-openapi). Updates `@hono/zod-openapi` from 1.6.1 to 1.6.2 - [Release notes](https://github.com/honojs/middleware/releases) - [Changelog](https://github.com/honojs/middleware/blob/main/packages/zod-openapi/CHANGELOG.md) - [Commits](https://github.com/honojs/middleware/commits/@hono/zod-openapi@1.6.2/packages/zod-openapi) --- updated-dependencies: - dependency-name: "@hono/zod-openapi" dependency-version: 1.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 09f603b32e46..139155efeae9 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@googleapis/youtube": "35.0.0", "@honeybadger-io/js": "6.16.1", "@hono/node-server": "2.1.1", - "@hono/zod-openapi": "1.6.1", + "@hono/zod-openapi": "1.6.2", "@jocmp/mercury-parser": "3.1.0", "@notionhq/client": "5.26.0", "@opentelemetry/api": "1.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1888cd3e3d9..5a6d28a45b35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,8 +53,8 @@ importers: specifier: 2.1.1 version: 2.1.1(hono@4.13.5) '@hono/zod-openapi': - specifier: 1.6.1 - version: 1.6.1(hono@4.13.5)(zod@4.5.4) + specifier: 1.6.2 + version: 1.6.2(hono@4.13.5)(zod@4.5.4) '@jocmp/mercury-parser': specifier: 3.1.0 version: 3.1.0 @@ -1271,15 +1271,15 @@ packages: peerDependencies: hono: ^4 - '@hono/zod-openapi@1.6.1': - resolution: {integrity: sha512-z1xS3FZxl4bBkU3kMIsQsLtsqxPTha5XZCsDEPMSZ6+3RRBQI7KED1GeoDpq2WueXCacsNIjcB0MtCnEHY7ahQ==} + '@hono/zod-openapi@1.6.2': + resolution: {integrity: sha512-ktnBqVgvIlopiiB9ab6hqoYefhcb0DUZU5QJ1F5UtDu8w6+cIORENz93sTZT6G0mQNes0/TbbadfrOpdehioPA==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.10.0' zod: ^4.0.0 - '@hono/zod-validator@0.9.0': - resolution: {integrity: sha512-n0ZSXmCiHVIp4Y5wlOOyZCeTd/rsawA/qW1cipB8QOYKZ9N8Tk0nZUZCXho9cu374AN4JpDNKioNKBJ/W+LBug==} + '@hono/zod-validator@0.9.1': + resolution: {integrity: sha512-iiv6w0qrIc0arfvCtUqBWsvl4fXjzaTcQcCJTTtCnkawF9HHGE+KjEl0ox3gQJ6rKZEgE3mLExlQCF72M+mNuw==} peerDependencies: hono: '>=4.11.2' zod: ^3.25.0 || ^4.0.0 @@ -3738,7 +3738,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.54: @@ -7075,15 +7075,15 @@ snapshots: dependencies: hono: 4.13.5 - '@hono/zod-openapi@1.6.1(hono@4.13.5)(zod@4.5.4)': + '@hono/zod-openapi@1.6.2(hono@4.13.5)(zod@4.5.4)': dependencies: '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.5.4) - '@hono/zod-validator': 0.9.0(hono@4.13.5)(zod@4.5.4) + '@hono/zod-validator': 0.9.1(hono@4.13.5)(zod@4.5.4) hono: 4.13.5 openapi3-ts: 4.6.1 zod: 4.5.4 - '@hono/zod-validator@0.9.0(hono@4.13.5)(zod@4.5.4)': + '@hono/zod-validator@0.9.1(hono@4.13.5)(zod@4.5.4)': dependencies: hono: 4.13.5 zod: 4.5.4 From 2d49e8ed2d7c669cfbefb984195d197ff1ff6063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:43:43 +0800 Subject: [PATCH 0728/1190] chore(deps-dev): bump globals from 17.11.0 to 17.12.0 (#23230) Bumps [globals](https://github.com/sindresorhus/globals) from 17.11.0 to 17.12.0. - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.11.0...v17.12.0) --- updated-dependencies: - dependency-name: globals dependency-version: 17.12.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 139155efeae9..ca5f840c0110 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "eslint-plugin-yml": "3.8.1", "fast-string-width": "3.0.2", "fs-extra": "11.4.0", - "globals": "17.11.0", + "globals": "17.12.0", "husky": "9.1.7", "js-beautify": "2.0.3", "lint-staged": "17.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a6d28a45b35..7a8f38a46e19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,8 +399,8 @@ importers: specifier: 11.4.0 version: 11.4.0 globals: - specifier: 17.11.0 - version: 17.11.0 + specifier: 17.12.0 + version: 17.12.0 husky: specifier: 9.1.7 version: 9.1.7 @@ -4230,8 +4230,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@17.11.0: - resolution: {integrity: sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==} + globals@17.12.0: + resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} engines: {node: '>=18'} globrex@0.1.2: @@ -9324,7 +9324,7 @@ snapshots: entities: 8.0.0 eslint: 10.9.1(jiti@2.7.0) find-up-simple: 1.0.1 - globals: 17.11.0 + globals: 17.12.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 is-identifier: 1.1.0 @@ -9655,7 +9655,7 @@ snapshots: globals@15.15.0: {} - globals@17.11.0: {} + globals@17.12.0: {} globrex@0.1.2: {} From 7cc1e211bd864908276ff8958d44fcd940186dcf Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 20:42:41 +0800 Subject: [PATCH 0729/1190] chore(worker): configure production Playwright service binding --- wrangler.production.toml | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 wrangler.production.toml diff --git a/wrangler.production.toml b/wrangler.production.toml new file mode 100644 index 000000000000..14502a5c4193 --- /dev/null +++ b/wrangler.production.toml @@ -0,0 +1,48 @@ +# Cloudflare Workers Builds must use: wrangler deploy --config wrangler.production.toml +name = "rsshub" +main = "dist-worker/worker.mjs" +compatibility_date = "2026-09-01" +compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"] + +find_additional_modules = true +base_dir = "dist-worker/modules" +rules = [{ type = "Data", globs = ["data_files/**"], fallthrough = true }] + +# Serve static assets from lib/assets +assets = { directory = "lib/assets" } + +[build] +command = "pnpm run worker-build" + +# Workers Paid plan is recommended for better performance +# Free plan has 10ms CPU time limit per request +# Paid plan has 30s CPU time limit per request + +[observability] +enabled = true + +# Browser Run API for Playwright support +# Requires Workers Paid plan +[browser] +binding = "BROWSER" + +# KV namespace for caching (auto-provisioned on first deploy) +[[kv_namespaces]] +binding = "CACHE" + +# PLAYWRIGHT_WS_ENDPOINT takes priority over BROWSER; use a matching Playwright version. +[[services]] +binding = "PLAYWRIGHT_SERVICE" +service = "cloudflare-patchright" + +[vars] +# Only use the service binding for this endpoint origin. +PLAYWRIGHT_SERVICE_ORIGIN = "https://cloudflare-patchright.rss3.workers.dev" +# Environment variables can be set here or via wrangler secret +# DEBUG_INFO = "false" + +# For production, use wrangler secret put to set sensitive values +# Example secrets: +# - ACCESS_KEY +# - GITHUB_ACCESS_TOKEN +# - etc. From 0d706e6978b7a53fe56fa76ab5556e2d2c863d11 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 20:46:12 +0800 Subject: [PATCH 0730/1190] fix(worker): load Playwright only for browser retries --- lib/utils/playwright-fetch.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/playwright-fetch.ts b/lib/utils/playwright-fetch.ts index a9889e6681c1..d7e115af378e 100644 --- a/lib/utils/playwright-fetch.ts +++ b/lib/utils/playwright-fetch.ts @@ -1,6 +1,6 @@ import { config } from '@/config'; import logger from '@/utils/logger'; -import { getPlaywrightPage } from '@/utils/playwright'; +import type { getPlaywrightPage } from '@/utils/playwright'; const maxFormBytes = 16 * 1024; const responseHeadersToRemove = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade', 'content-encoding', 'content-length']); @@ -128,6 +128,7 @@ async function replayRequest(request: Request, body?: Uint8Array): Promise { try { From 0bce68259bdd348e20ecb81299cf711a3f5dd68b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 20:50:35 +0800 Subject: [PATCH 0731/1190] chore(deps): bump query-string from 9.5.0 to 9.5.1 (#23234) Bumps [query-string](https://github.com/sindresorhus/query-string) from 9.5.0 to 9.5.1. - [Release notes](https://github.com/sindresorhus/query-string/releases) - [Commits](https://github.com/sindresorhus/query-string/compare/v9.5.0...v9.5.1) --- updated-dependencies: - dependency-name: query-string dependency-version: 9.5.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ca5f840c0110..c55d39fbf396 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "pac-proxy-agent": "9.1.0", "patchright": "1.62.2", "postal-mime": "3.0.0", - "query-string": "9.5.0", + "query-string": "9.5.1", "rate-limiter-flexible": "11.2.0", "re2js": "2.8.6", "rfc4648": "1.5.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a8f38a46e19..33aec7dd1251 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -206,8 +206,8 @@ importers: specifier: 3.0.0 version: 3.0.0 query-string: - specifier: 9.5.0 - version: 9.5.0 + specifier: 9.5.1 + version: 9.5.1 rate-limiter-flexible: specifier: 11.2.0 version: 11.2.0 @@ -5429,8 +5429,8 @@ packages: quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} - query-string@9.5.0: - resolution: {integrity: sha512-YlJmwNyi0RGYjlxYcuDncMsxFU7YyutbuI7gTm8ySxIGBlwx5yiBCOD5ig9ZNoHkawk/1Dey0N5mEfcUybMVAA==} + query-string@9.5.1: + resolution: {integrity: sha512-/zO3RwuRCMTIcEgq6YMv4OrtEE1XzBG7w5N6zc6ydYnkWYOWsnLI/5894hYEzfESfMOT4cHCsRTIdxsSl1KjGg==} engines: {node: '>=18'} querystringify@2.2.0: @@ -9474,7 +9474,7 @@ snapshots: he: 1.2.0 hmacsha1: 1.0.0 oauth-1.0a: 2.2.6 - query-string: 9.5.0 + query-string: 9.5.1 fast-deep-equal@3.1.3: {} @@ -11165,7 +11165,7 @@ snapshots: quansync@1.0.0: {} - query-string@9.5.0: + query-string@9.5.1: dependencies: decode-uri-component: 0.5.0 filter-obj: 5.1.0 From 54895f7e3cc227110a51be9edddf142704b01174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:02:37 +0000 Subject: [PATCH 0732/1190] chore(deps-dev): bump discord-api-types from 0.38.54 to 0.38.55 (#23229) Bumps [discord-api-types](https://github.com/discordjs/discord-api-types) from 0.38.54 to 0.38.55. - [Release notes](https://github.com/discordjs/discord-api-types/releases) - [Changelog](https://github.com/discordjs/discord-api-types/blob/main/CHANGELOG.md) - [Commits](https://github.com/discordjs/discord-api-types/compare/0.38.54...0.38.55) --- updated-dependencies: - dependency-name: discord-api-types dependency-version: 0.38.55 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c55d39fbf396..42ab4bbdbd23 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,7 @@ "@typescript-eslint/parser": "8.69.0", "@vercel/nft": "1.11.0", "@vitest/coverage-v8": "4.1.11", - "discord-api-types": "0.38.54", + "discord-api-types": "0.38.55", "domhandler": "6.0.1", "esbuild": "0.28.1", "eslint": "10.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33aec7dd1251..1eb0c737d9d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -363,8 +363,8 @@ importers: specifier: 4.1.11 version: 4.1.11(vitest@4.1.11) discord-api-types: - specifier: 0.38.54 - version: 0.38.54 + specifier: 0.38.55 + version: 0.38.55 domhandler: specifier: 6.0.1 version: 6.0.1 @@ -3738,11 +3738,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 - discord-api-types@0.38.54: - resolution: {integrity: sha512-3704EKdPtVl0Mozoe6uBJQ8GNmUkH8c81nfXkdSBx+Um8GN3FI/003uTY0Pg0A4KjL8g2Q+4v5cHR247kv6vwA==} + discord-api-types@0.38.55: + resolution: {integrity: sha512-ytuaRTzdnHUCXJ6KjL9MrItQX0xKncBKEeYI1Bst4+ud47eejH3cG6gaesYakjpPcUhh68XYS2YwGq3mmujFsA==} dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -9020,7 +9020,7 @@ snapshots: dependencies: heap: 0.2.7 - discord-api-types@0.38.54: {} + discord-api-types@0.38.55: {} dom-serializer@2.0.0: dependencies: From e4cfa0519a2508c047233c474e46161304b4aa8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:07:17 +0000 Subject: [PATCH 0733/1190] chore(deps): bump patchright from 1.62.2 to 1.62.3 (#23232) Bumps [patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) from 1.62.2 to 1.62.3. - [Release notes](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/releases) - [Commits](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/commits) --- updated-dependencies: - dependency-name: patchright dependency-version: 1.62.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 42ab4bbdbd23..3d9cd03d9277 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "otplib": "13.5.0", "p-map": "7.0.7", "pac-proxy-agent": "9.1.0", - "patchright": "1.62.2", + "patchright": "1.62.3", "postal-mime": "3.0.0", "query-string": "9.5.1", "rate-limiter-flexible": "11.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1eb0c737d9d3..b4de6cc263f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,8 +200,8 @@ importers: specifier: 9.1.0 version: 9.1.0 patchright: - specifier: 1.62.2 - version: 1.62.2 + specifier: 1.62.3 + version: 1.62.3 postal-mime: specifier: 3.0.0 version: 3.0.0 @@ -5298,13 +5298,13 @@ packages: parseley@0.13.1: resolution: {integrity: sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==} - patchright-core@1.62.2: - resolution: {integrity: sha512-XDl3HB/Kjm9ZBFn4ygD5mxdsqK4aIWNGXdea46XAGd0hvHuJJ84w1M83JqofpXIbD2aJfQ2Rtz4YcqPVrxjSzw==} + patchright-core@1.62.3: + resolution: {integrity: sha512-RQf0M2THMf4TL9HNNUxdYbd4Oe3DOVPni6G/bJYsEpD9F1cgqEpWiTJIh+pfuK9JwGRo4Du6lzagJosMUXbh/Q==} engines: {node: '>=20'} hasBin: true - patchright@1.62.2: - resolution: {integrity: sha512-IAj57p3NhXWJH3SlOCooDa/OoRveulUW1n/H6SY79S0j0sRd/0LLfBH9VMYDkDw5LUOlF0tCp3vkWMfOTiHsXQ==} + patchright@1.62.3: + resolution: {integrity: sha512-TMpWzcZVWUmOe1251PHWpk0gTP2d7+mvS00h1CLL2IQAUKX7UexKfPqEjdKkH8F2TT5AlyrSbwZKr5lu1dThAQ==} engines: {node: '>=20'} hasBin: true @@ -11023,11 +11023,11 @@ snapshots: leac: 0.7.0 peberminta: 0.10.0 - patchright-core@1.62.2: {} + patchright-core@1.62.3: {} - patchright@1.62.2: + patchright@1.62.3: dependencies: - patchright-core: 1.62.2 + patchright-core: 1.62.3 optionalDependencies: fsevents: 2.3.2 From d42aa26b864d217bdf02e80453d109a23e85fbfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 21:34:47 +0800 Subject: [PATCH 0734/1190] chore(deps): bump vm2 from 3.11.8 to 3.12.1 (#23228) Bumps [vm2](https://github.com/patriksimek/vm2) from 3.11.8 to 3.12.1. - [Release notes](https://github.com/patriksimek/vm2/releases) - [Changelog](https://github.com/patriksimek/vm2/blob/main/CHANGELOG.md) - [Commits](https://github.com/patriksimek/vm2/compare/v3.11.8...v3.12.1) --- updated-dependencies: - dependency-name: vm2 dependency-version: 3.12.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3d9cd03d9277..c29f876cf5a8 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "ufo": "1.6.4", "undici": "8.10.1", "uuid": "14.0.2", - "vm2": "3.11.8", + "vm2": "3.12.1", "winston": "3.19.0", "xxhash-wasm": "1.1.0", "youtube-caption-extractor": "1.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4de6cc263f1..ae14abfce9aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -266,8 +266,8 @@ importers: specifier: 14.0.2 version: 14.0.2 vm2: - specifier: 3.11.8 - version: 3.11.8 + specifier: 3.12.1 + version: 3.12.1 winston: specifier: 3.19.0 version: 3.19.0 @@ -6277,8 +6277,8 @@ packages: jsdom: optional: true - vm2@3.11.8: - resolution: {integrity: sha512-fzppkgSU+8m/3A0WfExYzBVLvFapQ31sXvX/WhP7gMbLJalMkHkJO7NPq3IRmYa0UhntY1f1DKpEr06sDFqBEw==} + vm2@3.12.1: + resolution: {integrity: sha512-VMqg/4swWz7lkse95rxPlgtnb7WkHL2wfDtrgcNIaIjvpskjRmEmS+MKoItl4npwtW+u2Kalle4zarf3aEWjGQ==} engines: {node: '>=6.0'} hasBin: true @@ -12026,7 +12026,7 @@ snapshots: transitivePeerDependencies: - msw - vm2@3.11.8: + vm2@3.12.1: dependencies: acorn: 8.18.0 acorn-walk: 8.3.5 From f2039a9386256174a10580a20ff627ec09e1a388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:46:33 +0000 Subject: [PATCH 0735/1190] chore(deps-dev): bump @types/node from 26.4.0 to 26.4.1 (#23235) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.4.0 to 26.4.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.4.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 132 ++++++++++++++++++++++++------------------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index c29f876cf5a8..ba2f0a3890a8 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,7 @@ "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", - "@types/node": "26.4.0", + "@types/node": "26.4.1", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.69.0", "@typescript-eslint/parser": "8.69.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae14abfce9aa..da5e8f76f7d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -345,8 +345,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.4.0 - version: 26.4.0 + specifier: 26.4.1 + version: 26.4.1 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -376,7 +376,7 @@ importers: version: 10.9.1(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.4.0)(eslint@10.9.1(jiti@2.7.0)) + version: 9.1.1(@types/node@26.4.1)(eslint@10.9.1(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) @@ -415,7 +415,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.1)(utf-8-validate@5.0.10) @@ -463,10 +463,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: specifier: 4.127.1 version: 4.127.1(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -2833,8 +2833,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.4.0': - resolution: {integrity: sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==} + '@types/node@26.4.1': + resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -6657,7 +6657,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260815.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: 4.124.0(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -7214,76 +7214,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.4.0)': + '@inquirer/checkbox@4.3.2(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/core': 10.3.2(@types/node@26.4.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/confirm@5.1.21(@types/node@26.4.0)': + '@inquirer/confirm@5.1.21(@types/node@26.4.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.4.0) - '@inquirer/type': 3.0.10(@types/node@26.4.0) + '@inquirer/core': 10.3.2(@types/node@26.4.1) + '@inquirer/type': 3.0.10(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/confirm@6.1.1(@types/node@26.4.0)': + '@inquirer/confirm@6.1.1(@types/node@26.4.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.0) - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/core': 11.2.1(@types/node@26.4.1) + '@inquirer/type': 4.0.7(@types/node@26.4.1) optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/core@10.3.2(@types/node@26.4.0)': + '@inquirer/core@10.3.2(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/core@11.2.1(@types/node@26.4.0)': + '@inquirer/core@11.2.1(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.0) + '@inquirer/type': 4.0.7(@types/node@26.4.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.4.0)': + '@inquirer/select@4.4.2(@types/node@26.4.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.4.0) + '@inquirer/core': 10.3.2(@types/node@26.4.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.0) + '@inquirer/type': 3.0.10(@types/node@26.4.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/type@3.0.10(@types/node@26.4.0)': + '@inquirer/type@3.0.10(@types/node@26.4.1)': optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 - '@inquirer/type@4.0.7(@types/node@26.4.0)': + '@inquirer/type@4.0.7(@types/node@26.4.1)': optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@ioredis/commands@2.0.0': {} @@ -8160,7 +8160,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/caseless@0.12.5': {} @@ -8173,7 +8173,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/crypto-js@4.2.2': {} @@ -8194,11 +8194,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/express-serve-static-core@5.1.3': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8212,7 +8212,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/html-to-text@9.0.4': {} @@ -8228,7 +8228,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/jsrsasign@10.5.15': {} @@ -8240,7 +8240,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.4.0': + '@types/node@26.4.1': dependencies: undici-types: 8.3.0 @@ -8256,7 +8256,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8266,16 +8266,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@types/statuses@2.0.6': {} @@ -8475,7 +8475,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/expect@4.1.11': dependencies: @@ -8486,14 +8486,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + msw: 2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) '@vitest/pretty-format@4.1.11': dependencies: @@ -9262,12 +9262,12 @@ snapshots: eslint: 10.9.1(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.4.0)(eslint@10.9.1(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.4.1)(eslint@10.9.1(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.4.0) - '@inquirer/confirm': 5.1.21(@types/node@26.4.0) - '@inquirer/select': 4.4.2(@types/node@26.4.0) + '@inquirer/checkbox': 4.3.2(@types/node@26.4.1) + '@inquirer/confirm': 5.1.21(@types/node@26.4.1) + '@inquirer/select': 4.4.2(@types/node@26.4.1) eslint: 10.9.1(jiti@2.7.0) eslint-filtered-fix: 0.3.0(eslint@10.9.1(jiti@2.7.0)) optionator: 0.9.4 @@ -10047,7 +10047,7 @@ snapshots: jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 - '@types/node': 26.4.0 + '@types/node': 26.4.1 jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: @@ -10678,9 +10678,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.4.0) + '@inquirer/confirm': 6.1.1(@types/node@26.4.1) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -11139,7 +11139,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.4.0 + '@types/node': 26.4.1 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -11971,16 +11971,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): + vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11989,17 +11989,17 @@ snapshots: rollup: 4.63.1 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.4.0 + '@types/node': 26.4.1 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 yaml: 2.9.0 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.4.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -12016,11 +12016,11 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.4.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.4.0 + '@types/node': 26.4.1 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: From 0133460a33102c747da1e3c1c408099a3d2a9851 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 22:00:30 +0800 Subject: [PATCH 0736/1190] fix(worker): retry user feeds in browsers and skip subtitles --- lib/routes/bilibili/cache.ts | 4 +- lib/routes/bilibili/check-cookie.ts | 6 + lib/utils/playwright-fetch.test.ts | 21 +++- lib/utils/playwright-fetch.ts | 11 +- tests/bilibili-subtitles.test.ts | 141 ++++++++++++++++++++++++ tests/bilibili-subtitles.worker.test.ts | 1 + 6 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 tests/bilibili-subtitles.test.ts create mode 100644 tests/bilibili-subtitles.worker.test.ts diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index a0f953615c18..9100d703a666 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -4,6 +4,7 @@ import { RateLimiterMemory, RateLimiterQueue } from 'rate-limiter-flexible'; import { config } from '@/config'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { isWorker } from '@/utils/is-worker'; import logger from '@/utils/logger'; import { getPlaywrightPage } from '@/utils/playwright'; @@ -262,7 +263,8 @@ const getVideoSubtitle = async ( lan_doc: string; }> > => { - if (!bvid) { + // Workers skip subtitle metadata and downloads, regardless of route configuration. + if (isWorker || !bvid) { return []; } diff --git a/lib/routes/bilibili/check-cookie.ts b/lib/routes/bilibili/check-cookie.ts index 38d0d0bd535f..6410a536108c 100644 --- a/lib/routes/bilibili/check-cookie.ts +++ b/lib/routes/bilibili/check-cookie.ts @@ -1,4 +1,5 @@ import type { APIRoute } from '@/types'; +import { isWorker } from '@/utils/is-worker'; import ofetch from '@/utils/ofetch'; import cacheIn from './cache'; @@ -27,6 +28,11 @@ async function handler() { }); const isResponseValid = response.code === 0 && !!response.data.mid; + // Workers do not fetch subtitles, so only validate the login session there. + if (isWorker) { + return { code: isResponseValid ? 0 : -1 }; + } + const subtitleResponse = await ofetch('https://api.bilibili.com/x/player/wbi/v2?bvid=BV1iU411o7R2&cid=1550543560', { headers: { Referer: 'https://www.bilibili.com/video/BV1iU411o7R2', diff --git a/lib/utils/playwright-fetch.test.ts b/lib/utils/playwright-fetch.test.ts index 8ab284c1b651..385ccab71b46 100644 --- a/lib/utils/playwright-fetch.test.ts +++ b/lib/utils/playwright-fetch.test.ts @@ -14,6 +14,11 @@ vi.mock('@/utils/logger', () => ({ default: { warn } })); const biliUrl = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1'; const rankingUrl = 'https://app-api.pixiv.net/v1/illust/ranking?mode=week_r18&filter=for_ios'; +const dynamicUrl = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=2267573&features=itemOpusStyle%2ClistOnlyfans'; +const videosUrl = 'https://api.bilibili.com/x/space/wbi/arc/search?mid=946974&ps=30&pn=1&w_rid=signed-fixture'; +const userIllustsUrl = 'https://app-api.pixiv.net/v1/user/illusts?user_id=15288095&filter=for_ios'; +const searchUrl = 'https://app-api.pixiv.net/v1/search/illust?word=%E7%8C%AB%20%26%20dog&sort=date_desc&filter=for_ios'; +const popularSearchUrl = 'https://app-api.pixiv.net/v1/search/popular-preview/illust?word=Nezuko&filter=for_ios'; const oauthUrl = 'https://oauth.secure.pixiv.net/auth/token'; const form = 'grant_type=refresh_token&refresh_token=refresh-fixture%2Bvalue&client_secret=client-fixture'; @@ -81,6 +86,15 @@ describe('targeted Playwright fetch retry', () => { ['https://api.bilibili.com:8443/x/web-interface/popular', 'GET', 412], [rankingUrl, 'GET', 401], [rankingUrl, 'GET', 412], + [dynamicUrl, 'GET', 200], + [videosUrl, 'POST', 412], + [videosUrl.replace('pn=1', 'pn=2'), 'GET', 412], + [userIllustsUrl, 'GET', 401], + [searchUrl, 'GET', 200], + [popularSearchUrl, 'POST', 403], + ['https://app-api.pixiv.net/v1/search/illust/other', 'GET', 403], + ['https://app-api.pixiv.net.evil.test/v1/user/illusts', 'GET', 403], + ['https://app-api.pixiv.net/v1/user/bookmarks/illust', 'GET', 403], [oauthUrl, 'GET', 403], [oauthUrl, 'POST', 403], ['https://oauth.secure.pixiv.net/auth/other', 'POST', 403], @@ -110,9 +124,14 @@ describe('targeted Playwright fetch retry', () => { it.each([ [biliUrl, 412], [rankingUrl, 403], + [dynamicUrl, 412], + [videosUrl, 412], + [userIllustsUrl, 403], + [searchUrl, 403], + [popularSearchUrl, 403], ])('replays one allowlisted GET and preserves response data for %s', async (url, status) => { const { instance, session } = makeBrowser(url as string); - const request = new Request(url as string, { headers: { authorization: 'Bearer fixture', 'user-agent': 'upstream-client', referer: 'https://source.test/' } }); + const request = new Request(url as string, { headers: { authorization: 'Bearer fixture', cookie: 'SESSDATA=cookie-fixture', 'user-agent': 'upstream-client', referer: 'https://source.test/' } }); const native = vi.fn(() => Promise.resolve(new Response('denied', { status: status as number }))); const response = await fetchWithPlaywrightRetry(request, native); diff --git a/lib/utils/playwright-fetch.ts b/lib/utils/playwright-fetch.ts index d7e115af378e..4c7e6b541cf6 100644 --- a/lib/utils/playwright-fetch.ts +++ b/lib/utils/playwright-fetch.ts @@ -3,6 +3,8 @@ import logger from '@/utils/logger'; import type { getPlaywrightPage } from '@/utils/playwright'; const maxFormBytes = 16 * 1024; +const bilibiliRetryPaths = new Set(['/x/web-interface/popular', '/x/polymer/web-dynamic/v1/feed/space', '/x/space/wbi/arc/search']); +const pixivRetryPaths = new Set(['/v1/illust/ranking', '/v1/user/illusts', '/v1/search/illust', '/v1/search/popular-preview/illust']); const responseHeadersToRemove = new Set(['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'transfer-encoding', 'upgrade', 'content-encoding', 'content-length']); type PlaywrightPage = Awaited>; @@ -41,10 +43,15 @@ async function discardBody(body: { cancel(): Promise }) { function retryStatus(request: Request): number | undefined { const url = new URL(request.url); if (request.method === 'GET') { - if (url.origin === 'https://api.bilibili.com' && url.pathname === '/x/web-interface/popular') { + if ( + url.origin === 'https://api.bilibili.com' && + bilibiliRetryPaths.has(url.pathname) && + // The legacy video-all route fetches later pages concurrently. + (url.pathname !== '/x/space/wbi/arc/search' || url.searchParams.get('pn') === '1') + ) { return 412; } - if (url.origin === 'https://app-api.pixiv.net' && url.pathname === '/v1/illust/ranking') { + if (url.origin === 'https://app-api.pixiv.net' && pixivRetryPaths.has(url.pathname)) { return 403; } } else if ( diff --git a/tests/bilibili-subtitles.test.ts b/tests/bilibili-subtitles.test.ts new file mode 100644 index 000000000000..552c79532e3d --- /dev/null +++ b/tests/bilibili-subtitles.test.ts @@ -0,0 +1,141 @@ +import { Hono } from 'hono'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import biliCache from '../lib/routes/bilibili/cache'; +import { apiRoute } from '../lib/routes/bilibili/check-cookie'; +import { getSrtAttachment, getSrtAttachmentBatch, getSubtitlesByVideoId } from '../lib/routes/youtube/api/subtitles'; +import { isWorker } from '../lib/utils/is-worker'; + +const mocks = vi.hoisted(() => ({ + config: { bilibili: { cookies: { '1': 'test-cookie' }, excludeSubtitles: false } }, + cached: new Map(), + tryGet: vi.fn(), + got: vi.fn(), + ofetch: vi.fn(), + removeTokens: vi.fn(), + getPlaywrightPage: vi.fn(), + getSubtitles: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ config: mocks.config })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/utils/got', () => ({ default: mocks.got })); +vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); +vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); +vi.mock('../lib/utils/logger', () => ({ default: {} })); +vi.mock('../lib/routes/bilibili/utils', () => ({ default: { lsid: vi.fn() } })); +vi.mock('youtube-caption-extractor', () => ({ getSubtitles: mocks.getSubtitles })); +vi.mock('rate-limiter-flexible', () => ({ + RateLimiterMemory: class {}, + RateLimiterQueue: class { + removeTokens = mocks.removeTokens; + }, +})); + +const bvid = 'BV1testvideo'; +const subtitleUrl = 'https://subtitle.test/caption.json'; +const expectedSrt = '1\n00:00:00,250 --> 00:00:01,750\nHello & 字幕\n\n2\n00:01:01,125 --> 00:01:03,625\nSecond caption\n'; +const subtitleResult = [{ content: expectedSrt, lan_doc: '中文' }]; +const app = new Hono().get('/check-cookie', async (c) => c.json(await apiRoute.handler(c))); + +beforeEach(() => { + vi.resetAllMocks(); + mocks.config.bilibili.excludeSubtitles = false; + mocks.config.bilibili.cookies = { '1': 'test-cookie' }; + mocks.cached.clear(); + mocks.tryGet.mockImplementation((key: string, getValue: () => Promise) => (mocks.cached.has(key) ? Promise.resolve(mocks.cached.get(key)) : getValue())); + mocks.got.mockImplementation((url: string) => { + if (url === `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`) { + return Promise.resolve({ data: { data: { pages: [{ cid: 42 }] } } }); + } + if (url === `https://api.bilibili.com/x/player/wbi/v2?bvid=${bvid}&cid=42`) { + return Promise.resolve({ data: { data: { subtitle: { subtitles: [{ subtitle_url: '//subtitle.test/caption.json', lan_doc: '中文' }] } } } }); + } + if (url === subtitleUrl) { + return Promise.resolve({ + data: { + body: [ + { from: 0.25, to: 1.75, content: 'Hello & 字幕' }, + { from: 61.125, to: 63.625, content: 'Second caption' }, + ], + }, + }); + } + throw new Error('Unexpected HTTP request in subtitle test'); + }); + mocks.ofetch.mockResolvedValue({ code: 0, data: { mid: 42, permission: '1', subtitle: { subtitles: [{ lan_doc: '中文' }] } } }); +}); + +describe.runIf(isWorker)('Worker subtitle restrictions', () => { + it('skips Bilibili subtitle and attachment requests even when explicitly enabled and already cached', async () => { + mocks.cached.set(`bili-cid-from-id-${bvid}-1`, 42); + mocks.cached.set(`bili-video-subtitle-${bvid}`, subtitleResult); + + expect(mocks.config.bilibili.excludeSubtitles).toBe(false); + expect(await biliCache.getVideoSubtitle(bvid)).toEqual([]); + expect(await biliCache.getVideoSubtitleAttachment(bvid)).toEqual([]); + expect(mocks.got).not.toHaveBeenCalled(); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.removeTokens).not.toHaveBeenCalled(); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it.each([ + { code: 0, data: { mid: 42 }, expected: 0 }, + { code: -101, data: {}, expected: -1 }, + ])('checks cookie login validity without requesting subtitle permissions: $expected', async ({ code, data, expected }) => { + mocks.ofetch.mockResolvedValue({ code, data }); + + expect(await (await app.request('/check-cookie')).json()).toEqual({ code: expected }); + expect(mocks.ofetch).toHaveBeenCalledExactlyOnceWith('https://api.bilibili.com/x/web-interface/nav', { + headers: { Referer: 'https://space.bilibili.com/1/', Cookie: 'test-cookie' }, + }); + }); + + it('keeps every YouTube subtitle entry point disabled with the real Worker override', async () => { + mocks.cached.set('youtube:getSubtitlesByVideoId:cached', expectedSrt); + + expect(await getSubtitlesByVideoId('cached')).toBe(''); + expect(await getSrtAttachment('cached')).toEqual([]); + expect(await getSrtAttachmentBatch(['cached', 'fresh'])).toEqual({}); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.getSubtitles).not.toHaveBeenCalled(); + }); +}); + +describe.runIf(!isWorker)('Node Bilibili subtitle behavior', () => { + it('keeps the default CID, subtitle metadata and subtitle download flow and SRT conversion', async () => { + expect(await biliCache.getVideoSubtitle(bvid)).toEqual(subtitleResult); + expect(mocks.got).toHaveBeenCalledTimes(3); + expect(mocks.got).toHaveBeenNthCalledWith(2, `https://api.bilibili.com/x/player/wbi/v2?bvid=${bvid}&cid=42`, { + headers: { Referer: `https://www.bilibili.com/video/${bvid}`, Cookie: 'test-cookie' }, + }); + expect(mocks.got).toHaveBeenNthCalledWith(3, subtitleUrl); + expect(mocks.removeTokens).toHaveBeenCalledExactlyOnceWith(1); + expect(mocks.tryGet.mock.calls.map(([key]) => key)).toEqual([`bili-cid-from-id-${bvid}-1`, `bili-video-subtitle-${bvid}`, subtitleUrl]); + }); + + it('preserves cached subtitles as SRT attachments without HTTP requests', async () => { + mocks.cached.set(`bili-cid-from-id-${bvid}-1`, 42); + mocks.cached.set(`bili-video-subtitle-${bvid}`, subtitleResult); + + expect(await biliCache.getVideoSubtitleAttachment(bvid)).toEqual([{ url: `data:text/plain;charset=utf-8,${encodeURIComponent(expectedSrt)}`, mime_type: 'text/srt', title: '字幕 - 中文' }]); + expect(mocks.got).not.toHaveBeenCalled(); + expect(mocks.removeTokens).not.toHaveBeenCalled(); + }); + + it('continues checking subtitle permissions as well as login validity on Node', async () => { + expect(await (await app.request('/check-cookie')).json()).toEqual({ code: 0 }); + expect(mocks.ofetch).toHaveBeenCalledTimes(2); + expect(mocks.ofetch).toHaveBeenNthCalledWith(2, 'https://api.bilibili.com/x/player/wbi/v2?bvid=BV1iU411o7R2&cid=1550543560', { + headers: { Referer: 'https://www.bilibili.com/video/BV1iU411o7R2', Cookie: 'test-cookie' }, + }); + }); + + it('keeps rejecting cookies without subtitle access on Node', async () => { + mocks.ofetch.mockResolvedValueOnce({ code: 0, data: { mid: 42 } }).mockResolvedValueOnce({ data: { permission: '0', subtitle: { subtitles: [] } } }); + + expect(await (await app.request('/check-cookie')).json()).toEqual({ code: -1 }); + expect(mocks.ofetch).toHaveBeenCalledTimes(2); + }); +}); diff --git a/tests/bilibili-subtitles.worker.test.ts b/tests/bilibili-subtitles.worker.test.ts new file mode 100644 index 000000000000..4a7884b7ea1f --- /dev/null +++ b/tests/bilibili-subtitles.worker.test.ts @@ -0,0 +1 @@ +import './bilibili-subtitles.test'; From a93d9600ae7eec0df771a5a1b9e664149674f61c Mon Sep 17 00:00:00 2001 From: DIYgod Date: Mon, 7 Sep 2026 22:10:56 +0800 Subject: [PATCH 0737/1190] fix(worker): preserve browser retry cancellation across request clones --- lib/utils/playwright-fetch.test.ts | 9 +++++++-- lib/utils/playwright-fetch.ts | 11 ++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/utils/playwright-fetch.test.ts b/lib/utils/playwright-fetch.test.ts index 385ccab71b46..6923d0a33384 100644 --- a/lib/utils/playwright-fetch.test.ts +++ b/lib/utils/playwright-fetch.test.ts @@ -242,14 +242,19 @@ describe('targeted Playwright fetch retry', () => { expect(JSON.stringify(warn.mock.calls)).not.toContain('endpoint-fixture'); }); - it('propagates cancellation during navigation and awaits cleanup', async () => { + it.each([false, true])('propagates cancellation during navigation and awaits cleanup (detached clone signal: %s)', async (detachedCloneSignal) => { const { page, emit, instance } = makeBrowser(biliUrl); page.goto.mockImplementation(() => { emit(); return new Promise(() => {}); }); const controller = new AbortController(); - const result = fetchWithPlaywrightRetry(new Request(biliUrl, { signal: controller.signal }), () => Promise.resolve(new Response('denied', { status: 412 }))); + const request = new Request(biliUrl, { signal: controller.signal }); + if (detachedCloneSignal) { + // Reproduce the clone losing signal propagation after its internal controller is collected. + vi.spyOn(request, 'clone').mockImplementation(() => new Request(biliUrl)); + } + const result = fetchWithPlaywrightRetry(request, () => Promise.resolve(new Response('denied', { status: 412 }))); const assertion = expect(result).rejects.toMatchObject({ name: 'AbortError' }); await vi.waitFor(() => expect(page.goto).toHaveBeenCalled()); controller.abort(); diff --git a/lib/utils/playwright-fetch.ts b/lib/utils/playwright-fetch.ts index 4c7e6b541cf6..cab1906893c4 100644 --- a/lib/utils/playwright-fetch.ts +++ b/lib/utils/playwright-fetch.ts @@ -122,17 +122,17 @@ async function destroyPage(instance: PlaywrightPage) { } } -async function replayRequest(request: Request, body?: Uint8Array): Promise { +async function replayRequest(request: Request, signal: AbortSignal, body?: Uint8Array): Promise { const controller = new AbortController(); const onAbort = () => controller.abort(); - request.signal.addEventListener('abort', onAbort, { once: true }); + signal.addEventListener('abort', onAbort, { once: true }); const timeout = Math.max(1, Math.min(config.requestTimeout || 30000, 30000)); const timer = setTimeout(onAbort, timeout); const wait = (promise: Promise) => abortable(promise, controller.signal); let instance: PlaywrightPage | undefined; let closing = false; try { - if (request.signal.aborted) { + if (signal.aborted) { throw abortError(); } const { getPlaywrightPage } = await wait(import('@/utils/playwright')); @@ -198,7 +198,7 @@ async function replayRequest(request: Request, body?: Uint8Array): Promise Date: Mon, 7 Sep 2026 22:41:25 +0800 Subject: [PATCH 0738/1190] fix(uniqlo,weibo): restore new arrivals and keyword feeds --- lib/routes/uniqlo/new.ts | 26 +++--- lib/routes/weibo/keyword.ts | 44 ++++++---- lib/routes/weibo/utils.ts | 125 +++++++++++++++------------- tests/uniqlo-new.test.ts | 83 +++++++++++++++++++ tests/weibo-cookies.test.ts | 157 ++++++++++++++++++++++++++++++++++++ tests/weibo-keyword.test.ts | 112 +++++++++++++++++++++++++ 6 files changed, 464 insertions(+), 83 deletions(-) create mode 100644 tests/uniqlo-new.test.ts create mode 100644 tests/weibo-cookies.test.ts create mode 100644 tests/weibo-keyword.test.ts diff --git a/lib/routes/uniqlo/new.ts b/lib/routes/uniqlo/new.ts index 90f3e0504b35..9dcf54ce43a0 100644 --- a/lib/routes/uniqlo/new.ts +++ b/lib/routes/uniqlo/new.ts @@ -24,7 +24,7 @@ async function handler(ctx) { const map = { sg: { - url: 'https://www.uniqlo.com/sg/api/commerce/v3/en/products', + url: 'https://www.uniqlo.com/sg/api/commerce/v5/en/products', withFlag: true, path: { women: 5855, @@ -56,26 +56,32 @@ async function handler(ctx) { }, }; const { data } = await got(map[country].url, { + headers: { + 'x-fr-clientid': `uq.${country}.web-spa`, + }, searchParams: { path: map[country].path[category], - flagCodes: map[country].withFlag ? 'salesStart newSKU,salesStart newSKU,salesStart newSKU' : undefined, + flagCodes: map[country].withFlag ? 'salesStart,newSKU' : undefined, sort: 1, limit: 24, offset: 0, + httpFailure: true, }, }); - const items = data.result.items.map((item) => ({ - title: item.name, - link: `https://www.uniqlo.com/${country}/${map[country].lang || 'en'}/products/${item.productId}`, - description: `${item.longDescription || item.name}

    Price: ${(item.prices.base || item.prices.promo).currency.symbol}${(item.prices.base || item.prices.promo).value}

    ${ - item.images.main.length ? item.images.main.map((image) => ``).join('') : '' - }${item.images.sub.map((image) => ``).join('')}`, - })); + const items = data.result.items.map((item) => { + const price = item.prices.base || item.prices.promo; + const images = [...Object.values<{ url?: string; image?: string }>(item.images.main || {}), ...(item.images.sub || [])]; + return { + title: item.name, + link: `https://www.uniqlo.com/${country}/${map[country].lang || 'en'}/products/${item.productId}`, + description: `${item.longDescription || ''}

    Price: ${price.currency.symbol}${price.value}

    ${images.map((image) => ``).join('')}`, + }; + }); return { title: `Uniqlo ${category} new arrivals in ${country}`, - link: `https://www.uniqlo.com/${country}/${map[country].lang || 'en'}/feature/new/${category}`, + link: `https://www.uniqlo.com/${country}/${map[country].lang || 'en'}/feature/new/${category}/`, item: items, }; } diff --git a/lib/routes/weibo/keyword.ts b/lib/routes/weibo/keyword.ts index 2309f3dfdd45..d850f91d1a4c 100644 --- a/lib/routes/weibo/keyword.ts +++ b/lib/routes/weibo/keyword.ts @@ -5,8 +5,8 @@ import type { Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; import { fallback, queryToBoolean } from '@/utils/readable-social'; -import timezone from '@/utils/timezone'; import weiboUtils from './utils'; @@ -40,7 +40,7 @@ async function handler(ctx) { const data = await weiboUtils.tryWithCookies((cookies, verifier) => cache.tryGet( - `weibo:keyword:${keyword}`, + `weibo:keyword-statuses:${keyword}`, async () => { const _r = await got({ method: 'get', @@ -52,7 +52,20 @@ async function handler(ctx) { }, }); verifier(_r); - return _r.data.data.cards; + const cards = _r.data?.data?.cards; + if (!Array.isArray(cards)) { + throw new TypeError(_r.data?.msg || 'Weibo keyword search returned an invalid response.'); + } + const statuses = cards.filter((card) => card.mblog).map((card) => card.mblog); + if (!statuses.length) { + const message = 'Weibo keyword search returned no posts. Please retry later or check the keyword on Weibo.'; + if (!config.weibo.cookies) { + // Expired visitor sessions can return ok=1 with only a "no results" card. + throw new weiboUtils.RenewWeiboCookiesError(message); + } + throw new Error(message); + } + return statuses; }, config.cache.routeExpire, false @@ -65,17 +78,18 @@ async function handler(ctx) { title: `又有人在微博提到${keyword}了`, link: `http://s.weibo.com/weibo/${encodeURIComponent(keyword)}&b=1&nodup=1`, description: `又有人在微博提到${keyword}了`, - item: data - .filter((i) => i.mblog) - .map((item) => { - item.mblog.created_at = timezone(item.mblog.created_at, 8); - if (item.mblog.retweeted_status && item.mblog.retweeted_status.created_at) { - item.mblog.retweeted_status.created_at = timezone(item.mblog.retweeted_status.created_at, 8); - } - return weiboUtils.formatExtended(ctx, item.mblog, undefined, { - showAuthorInTitle: fallback(undefined, queryToBoolean(routeParams.showAuthorInTitle), true), - showAuthorInDesc: fallback(undefined, queryToBoolean(routeParams.showAuthorInDesc), true), - }); - }), + item: data.map((status) => { + const item = structuredClone(status); + if (item.created_at) { + item.created_at = parseDate(item.created_at); + } + if (item.retweeted_status?.created_at) { + item.retweeted_status.created_at = parseDate(item.retweeted_status.created_at); + } + return weiboUtils.formatExtended(ctx, item, undefined, { + showAuthorInTitle: fallback(undefined, queryToBoolean(routeParams.showAuthorInTitle), true), + showAuthorInDesc: fallback(undefined, queryToBoolean(routeParams.showAuthorInDesc), true), + }); + }), }); } diff --git a/lib/routes/weibo/utils.ts b/lib/routes/weibo/utils.ts index 2b0e8bad4ac4..67ee0bdc8ba1 100644 --- a/lib/routes/weibo/utils.ts +++ b/lib/routes/weibo/utils.ts @@ -64,78 +64,87 @@ const weiboUtils = { RenewWeiboCookiesError, getCookies: (() => { const url = 'https://m.weibo.cn/'; + const cacheKey = 'weibo:visitor-cookies'; const coolingDownMessage = `Cooling down before new visitor Cookies from ${url} may be fetched`; let coolingDown = false; let visitorCookiesPromise: Promise | undefined; - return async (renew: any = false) => { - if (config.weibo.cookies) { - if (renew) { - throw new Error('Cookies expired. Please update WEIBO_COOKIES'); + const fetchVisitorCookies = async (renew: any = false) => { + if (visitorCookiesPromise) { + return await visitorCookiesPromise; + } + if (coolingDown) { + if (renew?.message) { + logger.warn(coolingDownMessage); + throw renew; } - return config.weibo.cookies; + throw new Error(coolingDownMessage); } + coolingDown = true; + setTimeout(() => { + coolingDown = false; + }, config.cache.routeExpire * 1000); - const cacheKey = 'weibo:visitor-cookies'; if (renew) { - cache.set(cacheKey, '', 1); + logger.warn(`Renewing visitor Cookies from ${url}`); + } else { + logger.info(`Fetching visitor Cookies from ${url}`); } - return await cache.tryGet(cacheKey, async () => { - if (visitorCookiesPromise) { - return await visitorCookiesPromise; - } - if (coolingDown) { - if (renew?.message) { - logger.warn(coolingDownMessage); - throw renew; + visitorCookiesPromise = (async () => { + let times = 0; + const { page, destroy } = await getPlaywrightPage(url, { + onBeforeLoad: async (page) => { + const expectResourceTypes = new Set(['document', 'script', 'xhr', 'fetch']); + await page.setExtraHTTPHeaders({ 'User-Agent': weiboUtils.apiHeaders['User-Agent'] }); + await page.route('**/*', (route) => { + const request = route.request(); + // 1st: initial request, 302 to visitor.passport.weibo.cn; 2nd: auth ok + if (!expectResourceTypes.has(request.resourceType()) || times >= 2) { + route.abort(); + return; + } + if (request.url().startsWith(url)) { + times++; + } + route.continue(); + }); + }, + gotoConfig: { waitUntil: 'networkidle' }, + }); + let cookies: string; + try { + cookies = await getCookies(page, 'weibo.cn'); + if (times < 2 || !cookies) { + throw new Error(`Unable to fetch visitor cookies. Please set WEIBO_COOKIES. Redirection: ${times}, last URL: ${page.url()}`); } - throw new Error(coolingDownMessage); + } finally { + await destroy(); } - coolingDown = true; - setTimeout(() => { - coolingDown = false; - }, config.cache.routeExpire * 1000); - if (renew) { - logger.warn(`Renewing visitor Cookies from ${url}`); - } else { - logger.info(`Fetching visitor Cookies from ${url}`); + await cache.set(cacheKey, cookies); } - visitorCookiesPromise = (async () => { - let times = 0; - const { page, destroy } = await getPlaywrightPage(url, { - onBeforeLoad: async (page) => { - const expectResourceTypes = new Set(['document', 'script', 'xhr', 'fetch']); - await page.setExtraHTTPHeaders({ 'User-Agent': weiboUtils.apiHeaders['User-Agent'] }); - await page.route('**/*', (route) => { - const request = route.request(); - // 1st: initial request, 302 to visitor.passport.weibo.cn; 2nd: auth ok - if (!expectResourceTypes.has(request.resourceType()) || times >= 2) { - route.abort(); - return; - } - if (request.url().startsWith(url)) { - times++; - } - route.continue(); - }); - }, - gotoConfig: { waitUntil: 'networkidle' }, - }); - const cookies: string = await getCookies(page, 'weibo.cn'); - await destroy(); - if (times < 2 || !cookies) { - throw new Error(`Unable to fetch visitor cookies. Please set WEIBO_COOKIES. Redirection: ${times}, last URL: ${page.url()}`); - } - return cookies; - })(); + return cookies; + })(); - try { - return await visitorCookiesPromise; - } finally { - visitorCookiesPromise = undefined; + try { + return await visitorCookiesPromise; + } finally { + visitorCookiesPromise = undefined; + } + }; + + return async (renew: any = false) => { + if (config.weibo.cookies) { + if (renew) { + throw new Error('Cookies expired. Please update WEIBO_COOKIES'); } - }); + return config.weibo.cookies; + } + + if (renew) { + return await fetchVisitorCookies(renew); + } + return await cache.tryGet(cacheKey, fetchVisitorCookies); }; })(), tryWithCookies: (() => { @@ -152,7 +161,7 @@ const weiboUtils = { if (error.message?.includes('WEIBO_COOKIES')) { throw error; } - if (errors > 10) { + if (errors > 10 && !config.weibo.cookies) { logger.warn(`Too many errors while fetching data from weibo API, renewing Cookies: ${error.message}`); logger.info('Please open an issue on GitHub if renewing Cookies fixes the error'); } else if ((error.name === 'HTTPError' || error.name === 'FetchError') && error.status === 432) { diff --git a/tests/uniqlo-new.test.ts b/tests/uniqlo-new.test.ts new file mode 100644 index 000000000000..336d7399d237 --- /dev/null +++ b/tests/uniqlo-new.test.ts @@ -0,0 +1,83 @@ +import type { Context } from 'hono'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/uniqlo/new'; +import type { Data } from '../lib/types'; +import got from '../lib/utils/got'; + +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); + +const product = { + productId: 'E489455-000', + name: 'HEATTECH Half Socks Pile', + prices: { base: { currency: { symbol: '$' }, value: 7.9 } }, + images: { + main: { + '17': { image: 'https://image.uniqlo.com/UQ/ST3/AsianCommon/imagesgoods/489455/item/goods_17_489455_3x4.jpg' }, + '09': { image: 'https://image.uniqlo.com/UQ/ST3/AsianCommon/imagesgoods/489455/item/goods_09_489455_3x4.jpg' }, + }, + sub: [{ image: 'https://image.uniqlo.com/UQ/ST3/AsianCommon/imagesgoods/489455/sub/goods_489455_sub21_3x4.jpg' }], + }, +}; + +const invoke = async (country: string) => (await route.handler({ req: { param: () => ({ country, category: 'men' }) } } as unknown as Context)) as Data; + +beforeEach(() => { + vi.resetAllMocks(); + vi.mocked(got).mockResolvedValue({ data: { result: { items: [product] } } }); +}); + +describe('Uniqlo new arrivals', () => { + it('requests the current Singapore API with the storefront client and new arrival flags', async () => { + const feed = await invoke('sg'); + + expect(got).toHaveBeenCalledExactlyOnceWith('https://www.uniqlo.com/sg/api/commerce/v5/en/products', { + headers: { 'x-fr-clientid': 'uq.sg.web-spa' }, + searchParams: { path: 5856, flagCodes: 'salesStart,newSKU', sort: 1, limit: 24, offset: 0, httpFailure: true }, + }); + expect(feed).toMatchObject({ title: 'Uniqlo men new arrivals in sg', link: 'https://www.uniqlo.com/sg/en/feature/new/men/' }); + expect(feed.item).toEqual([ + { + title: product.name, + link: 'https://www.uniqlo.com/sg/en/products/E489455-000', + description: `

    Price: $7.9

    `, + }, + ]); + }); + + it.each([ + { country: 'us', language: 'en', path: 22211 }, + { country: 'jp', language: 'ja', path: 1072 }, + ])('preserves the $country endpoint, category and language without Singapore flags', async ({ country, language, path }) => { + const feed = await invoke(country); + + expect(got).toHaveBeenCalledExactlyOnceWith(`https://www.uniqlo.com/${country}/api/commerce/v5/${language}/products`, { + headers: { 'x-fr-clientid': `uq.${country}.web-spa` }, + searchParams: { path, flagCodes: undefined, sort: 1, limit: 24, offset: 0, httpFailure: true }, + }); + expect(feed.link).toBe(`https://www.uniqlo.com/${country}/${language}/feature/new/men/`); + expect(feed.item[0].link).toBe(`https://www.uniqlo.com/${country}/${language}/products/E489455-000`); + }); + + it('preserves full titles, descriptions and legacy image arrays when optional subimages are absent', async () => { + const name = 'A product name with descriptive details '.repeat(5); + vi.mocked(got).mockResolvedValue({ + data: { + result: { + items: [{ ...product, name, longDescription: '

    Soft and warm.

    ', images: { main: [{ url: 'https://image.uniqlo.com/main.jpg' }] } }], + }, + }, + }); + + const feed = await invoke('sg'); + + expect(feed.item).toEqual([ + { + title: name, + link: 'https://www.uniqlo.com/sg/en/products/E489455-000', + description: '

    Soft and warm.



    Price: $7.9

    ', + }, + ]); + expect(feed.item[0].pubDate).toBeUndefined(); + }); +}); diff --git a/tests/weibo-cookies.test.ts b/tests/weibo-cookies.test.ts new file mode 100644 index 000000000000..74c8c774d373 --- /dev/null +++ b/tests/weibo-cookies.test.ts @@ -0,0 +1,157 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + configuredCookies: '', + tryGet: vi.fn(), + set: vi.fn(), + getPlaywrightPage: vi.fn(), + getCookies: vi.fn(), + destroy: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ + config: { + weibo: { + get cookies() { + return mocks.configuredCookies; + }, + }, + cache: { routeExpire: 300, contentExpire: 3600 }, + }, +})); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet, set: mocks.set } })); +vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); +vi.mock('../lib/utils/logger', () => ({ default: { info: vi.fn(), warn: vi.fn() } })); +vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); +vi.mock('../lib/utils/playwright-utils', () => ({ getCookies: mocks.getCookies })); + +beforeEach(() => { + vi.resetModules(); + vi.resetAllMocks(); + vi.useFakeTimers(); + mocks.configuredCookies = ''; + mocks.tryGet.mockImplementation(async (_key, callback) => await callback()); + mocks.set.mockResolvedValue(undefined); + mocks.getCookies.mockResolvedValue('SUB=fresh-visitor'); + mocks.destroy.mockResolvedValue(undefined); + mocks.getPlaywrightPage.mockImplementation(async (url, options) => { + const page = { + setExtraHTTPHeaders: vi.fn(), + route: vi.fn(), + url: () => url, + }; + await options.onBeforeLoad(page); + const intercept = page.route.mock.calls[0][1]; + const request = { request: () => ({ resourceType: () => 'document', url: () => url }), continue: vi.fn(), abort: vi.fn() }; + intercept(request); + intercept(request); + return { page, destroy: mocks.destroy }; + }); +}); + +afterEach(() => { + vi.clearAllTimers(); + vi.useRealTimers(); +}); + +describe('Weibo visitor cookie renewal', () => { + it('bypasses stale cached cookies and waits for the replacement without a one-second invalidation', async () => { + mocks.tryGet.mockResolvedValue('SUB=stale-visitor'); + const { promise: writeStarted, resolve: startWrite } = Promise.withResolvers(); + const { promise: writeReleased, resolve: releaseWrite } = Promise.withResolvers(); + mocks.set.mockImplementation(() => { + startWrite(); + return writeReleased; + }); + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + const settled = vi.fn(); + const renewal = (async () => { + const cookies = await weiboUtils.getCookies(new Error('Cookies expired')); + settled(); + return cookies; + })(); + + await writeStarted; + const concurrentRenewal = weiboUtils.getCookies(true); + try { + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.set).toHaveBeenCalledExactlyOnceWith('weibo:visitor-cookies', 'SUB=fresh-visitor'); + expect(settled).not.toHaveBeenCalled(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + } finally { + releaseWrite(); + } + await expect(Promise.all([renewal, concurrentRenewal])).resolves.toEqual(['SUB=fresh-visitor', 'SUB=fresh-visitor']); + }); + + it('keeps ordinary cache hits free of browser work', async () => { + mocks.tryGet.mockResolvedValue('SUB=cached-visitor'); + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + + await expect(weiboUtils.getCookies()).resolves.toBe('SUB=cached-visitor'); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + expect(mocks.set).not.toHaveBeenCalled(); + }); + + it('reuses one browser for concurrent renewals and preserves the cooldown', async () => { + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + + await expect(Promise.all([weiboUtils.getCookies(true), weiboUtils.getCookies(true)])).resolves.toEqual(['SUB=fresh-visitor', 'SUB=fresh-visitor']); + expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); + expect(mocks.destroy).toHaveBeenCalledOnce(); + expect(mocks.set).toHaveBeenCalledExactlyOnceWith('weibo:visitor-cookies', 'SUB=fresh-visitor'); + + const expired = new Error('Cookies expired again'); + await expect(weiboUtils.getCookies(expired)).rejects.toBe(expired); + expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); + + await vi.advanceTimersByTimeAsync(300 * 1000); + await expect(weiboUtils.getCookies(true)).resolves.toBe('SUB=fresh-visitor'); + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(2); + }); + + it('closes the browser and preserves the old cache when extracting replacement cookies fails', async () => { + mocks.getCookies.mockRejectedValue(new Error('Cookie extraction failed')); + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + + await expect(weiboUtils.getCookies(true)).rejects.toThrow('Cookie extraction failed'); + expect(mocks.destroy).toHaveBeenCalledOnce(); + expect(mocks.set).not.toHaveBeenCalled(); + }); + + it('requires an explicit update when configured cookies expire', async () => { + mocks.configuredCookies = 'SUB=configured'; + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + + await expect(weiboUtils.getCookies()).resolves.toBe('SUB=configured'); + await expect(weiboUtils.getCookies(true)).rejects.toThrow('Cookies expired. Please update WEIBO_COOKIES'); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.set).not.toHaveBeenCalled(); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it('does not mistake repeated empty searches for configured cookie expiry', async () => { + mocks.configuredCookies = 'SUB=configured'; + const { default: weiboUtils } = await import('../lib/routes/weibo/utils'); + const noResults = new Error('Weibo keyword search returned no posts.'); + const callback = vi.fn().mockRejectedValue(noResults); + + for (let attempt = 0; attempt < 12; attempt++) { + // Requests must complete in order to exercise the accumulated error count. + // eslint-disable-next-line no-await-in-loop + await expect(weiboUtils.tryWithCookies(callback)).rejects.toBe(noResults); + } + + expect(callback).toHaveBeenCalledTimes(12); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.set).not.toHaveBeenCalled(); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + + await expect( + weiboUtils.tryWithCookies((_cookies, verifier) => { + verifier({ data: { ok: -100 } }); + return Promise.resolve(); + }) + ).rejects.toThrow('Cookies expired. Please update WEIBO_COOKIES'); + }); +}); diff --git a/tests/weibo-keyword.test.ts b/tests/weibo-keyword.test.ts new file mode 100644 index 000000000000..3158b3b9b0f2 --- /dev/null +++ b/tests/weibo-keyword.test.ts @@ -0,0 +1,112 @@ +import type { Context } from 'hono'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { config } from '../lib/config'; +import { route } from '../lib/routes/weibo/keyword'; +import weiboUtils from '../lib/routes/weibo/utils'; +import type { Data } from '../lib/types'; + +const mocks = vi.hoisted(() => ({ request: vi.fn(), tryGet: vi.fn(), entries: new Map() })); +vi.mock('../lib/config', () => ({ config: { weibo: { cookies: '' }, cache: { routeExpire: 900 } } })); +vi.mock('../lib/utils/got', () => ({ default: mocks.request })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/utils/logger', () => ({ default: { info: vi.fn(), warn: vi.fn() } })); +vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: vi.fn() })); + +const noResults = { data: { ok: 1, data: { cards: [{ card_type: 4, desc: '抱歉,未找到相关结果。' }] } } }; +const status = { + id: '5323566721532243', + bid: 'R9T4Q6qBB', + text: 'A post about RSSHub', + created_at: 'Wed Jul 22 19:44:35 +0800 2026', + user: { id: '1050089572', screen_name: 'Author' }, +}; +const responseWith = (...statuses: Array>) => ({ data: { ok: 1, data: { cards: statuses.map((mblog) => ({ card_type: 9, mblog })) } } }); +const invoke = async (routeParams?: string) => (await route.handler({ req: { param: (name: string) => ({ keyword: 'RSSHub', routeParams })[name] } } as unknown as Context)) as Data; + +beforeEach(() => { + vi.restoreAllMocks(); + vi.clearAllMocks(); + config.weibo.cookies = ''; + mocks.entries.clear(); + mocks.request.mockRejectedValue(new Error('Unexpected upstream request')); + mocks.tryGet.mockImplementation(async (key, callback) => { + if (mocks.entries.has(key)) { + return mocks.entries.get(key); + } + const value = await callback(); + mocks.entries.set(key, value); + return value; + }); + vi.spyOn(weiboUtils, 'getCookies').mockResolvedValueOnce('SUB=stale-visitor').mockResolvedValue('SUB=fresh-visitor'); +}); + +describe('Weibo keyword visitor sessions', () => { + it('renews a visitor session that silently returns no results and caches only successful posts', async () => { + mocks.entries.set('weibo:keyword:RSSHub', noResults.data.data.cards); + mocks.request.mockResolvedValueOnce(noResults).mockResolvedValueOnce(responseWith(status)); + + const feed = await invoke(); + const cachedFeed = await invoke(); + + expect(feed.item).toHaveLength(1); + expect(cachedFeed).toEqual(feed); + expect(feed.item[0]).toMatchObject({ title: 'Author: A post about RSSHub', link: 'https://weibo.com/1050089572/R9T4Q6qBB', pubDate: new Date('2026-07-22T11:44:35Z') }); + expect(mocks.request).toHaveBeenCalledTimes(2); + expect(mocks.request.mock.calls.map(([options]) => options.headers.Cookie)).toEqual(['SUB=stale-visitor', 'SUB=fresh-visitor']); + expect(weiboUtils.getCookies).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'RenewWeiboCookiesError' })); + expect(mocks.entries.get('weibo:keyword-statuses:RSSHub')).toEqual([status]); + }); + + it('reports a genuinely empty search after one renewal without caching it', async () => { + mocks.request.mockResolvedValue(noResults); + + await expect(invoke()).rejects.toThrow('Weibo keyword search returned no posts'); + expect(mocks.request).toHaveBeenCalledTimes(2); + expect(weiboUtils.getCookies).toHaveBeenCalledTimes(2); + expect(mocks.entries.size).toBe(0); + }); + + it('preserves configured account cookies when their keyword search has no results', async () => { + config.weibo.cookies = 'SUB=configured-account'; + mocks.request.mockResolvedValue(noResults); + + await expect(invoke()).rejects.toThrow('Weibo keyword search returned no posts'); + expect(mocks.request).toHaveBeenCalledTimes(1); + expect(weiboUtils.getCookies).toHaveBeenCalledExactlyOnceWith(false); + expect(mocks.entries.size).toBe(0); + }); + + it('preserves explicit source offsets, missing dates, and formatting options without mutating cached statuses', async () => { + const original = { + ...status, + retweeted_status: { + ...status, + id: '5314741212809485', + bid: 'R6bu9bMSh', + created_at: 'Sun Jun 28 11:15:11 +0800 2026', + text: 'Original content', + }, + }; + const undated = { ...status, id: 'undated-id', bid: 'undated-bid', created_at: undefined }; + const expected = structuredClone([original, undated]); + mocks.request.mockResolvedValue(responseWith(original, undated)); + + const feed = await invoke('showAuthorInTitle=0&showAuthorInDesc=0'); + const repeated = await invoke('showAuthorInTitle=0&showAuthorInDesc=0'); + + expect(feed.item[0].title.startsWith('Author:')).toBe(false); + expect(feed.item[0].pubDate).toEqual(new Date('2026-07-22T11:44:35Z')); + expect(feed.item[1].pubDate).toBeUndefined(); + expect(repeated).toEqual(feed); + expect(mocks.entries.get('weibo:keyword-statuses:RSSHub')).toEqual(expected); + expect(original.retweeted_status.created_at).toBe('Sun Jun 28 11:15:11 +0800 2026'); + }); + + it('reports malformed upstream responses without caching them', async () => { + mocks.request.mockResolvedValue({ data: { ok: 0, msg: 'Search temporarily unavailable' } }); + + await expect(invoke()).rejects.toThrow('Search temporarily unavailable'); + expect(mocks.entries.size).toBe(0); + }); +}); From d4c70a5000c267a89dc05b4035a9b2f94ef99bc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:39:52 +0800 Subject: [PATCH 0739/1190] chore(deps-dev): bump the oxc group with 5 updates (#23226) Bumps the oxc group with 5 updates: | Package | From | To | | --- | --- | --- | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.80.0` | `1.81.0` | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.147.0` | `0.148.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.65.0` | `0.66.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.80.0` | `1.81.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.80.0` | `1.81.0` | Updates `@oxlint/plugins` from 1.80.0 to 1.81.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.81.0/npm/oxlint-plugins) Updates `oxc-parser` from 0.147.0 to 0.148.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.148.0/napi/parser) Updates `oxfmt` from 0.65.0 to 0.66.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.66.0/npm/oxfmt) Updates `oxlint` from 1.80.0 to 1.81.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.81.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.80.0 to 1.81.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.81.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.81.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.148.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.66.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.81.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.81.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +- pnpm-lock.yaml | 518 ++++++++++++++++++++++++------------------------- 2 files changed, 264 insertions(+), 264 deletions(-) diff --git a/package.json b/package.json index ba2f0a3890a8..f0712a156921 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@cloudflare/workers-types": "5.20260901.1", "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.80.0", + "@oxlint/plugins": "1.81.0", "@stylistic/eslint-plugin": "5.10.0", "@types/crypto-js": "4.2.2", "@types/eslint": "9.6.1", @@ -190,10 +190,10 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.147.0", - "oxfmt": "0.65.0", - "oxlint": "1.80.0", - "oxlint-plugin-eslint": "1.80.0", + "oxc-parser": "0.148.0", + "oxfmt": "0.66.0", + "oxlint": "1.81.0", + "oxlint-plugin-eslint": "1.81.0", "oxlint-tsgolint": "7.0.2001", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da5e8f76f7d4..043f50a86c22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,8 +312,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.9.1(jiti@2.7.0)) '@oxlint/plugins': - specifier: 1.80.0 - version: 1.80.0 + specifier: 1.81.0 + version: 1.81.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.9.1(jiti@2.7.0)) @@ -420,17 +420,17 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.10.1)(utf-8-validate@5.0.10) oxc-parser: - specifier: 0.147.0 - version: 0.147.0 + specifier: 0.148.0 + version: 0.148.0 oxfmt: - specifier: 0.65.0 - version: 0.65.0 + specifier: 0.66.0 + version: 0.66.0 oxlint: - specifier: 1.80.0 - version: 1.80.0(oxlint-tsgolint@7.0.2001) + specifier: 1.81.0 + version: 1.81.0(oxlint-tsgolint@7.0.2001) oxlint-plugin-eslint: - specifier: 1.80.0 - version: 1.80.0 + specifier: 1.81.0 + version: 1.81.0 oxlint-tsgolint: specifier: 7.0.2001 version: 7.0.2001 @@ -1821,124 +1821,124 @@ packages: '@otplib/uri@13.5.0': resolution: {integrity: sha512-LsL1hqTEgJHY40U2eb/Qp6OR1tKkNGwHQTrqqjIuJj0cawGWkLEmeUFK1MHzWBQ4bgy5IdeB5Et7gIDSAH/CRw==} - '@oxc-parser/binding-android-arm-eabi@0.147.0': - resolution: {integrity: sha512-fOtoGvIoirkvxQVw9J1WJPxz571XPgLsPf9uhRD+PJteUnvrJHMDmK9pw2yZEGGyismtRoEsp+JcXUdF/JDMDw==} + '@oxc-parser/binding-android-arm-eabi@0.148.0': + resolution: {integrity: sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.147.0': - resolution: {integrity: sha512-emjQHOYJaomo4ykaXQ1EItunr/I94Nk01oqBmU4dSkKSTupIDx6OysVDf2e8Eytm77rb+4ZxzgElyWP7rcEX7A==} + '@oxc-parser/binding-android-arm64@0.148.0': + resolution: {integrity: sha512-sg/6Ez0KdAygsu0POELux9wN1Po2CP93WY8eNl4DBKIGprsd4QSHBXOb471Pu9i2OCD5sLkISSb2agZEhVn2Zw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.147.0': - resolution: {integrity: sha512-kXvBPJL7RmDPJ2mze/vXPPVQimCDtFr9OFLjf7dyhV5Dx64cgcXh9KKrA1sMWvCObvJll9CZZUO0FBlFwD0l6A==} + '@oxc-parser/binding-darwin-arm64@0.148.0': + resolution: {integrity: sha512-yiSJmzGUvCUaJT8X3j40gVcX+ckuHQMuiOtF8DvzTs5+JtB/7XuHFPp4M+vv5u+HlBtDUd4Ks5pyHpWz8mfnkg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.147.0': - resolution: {integrity: sha512-mgFF8pLU6R64LbT27lSrtVRspVC/3IcZ0qyIikzmi78Y3Ik2OPnlAHHI0UEBRcC3qmNgtjaef7zkFt7/uPxIcw==} + '@oxc-parser/binding-darwin-x64@0.148.0': + resolution: {integrity: sha512-6ZeklaamrMy4H2JmhvcJg6iip59tYILtuLaILxyAHT3l5FDxnI5ihVievAft5ZmAbqtlWHErOi1OpJK8gy1wcA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.147.0': - resolution: {integrity: sha512-v38aiF11qufOTBcCAKL4skgQf0zJ4NEvRlivq7B5kHrlyvjCLjvNrMtNWDTz1SDUL6/xVsJRmLDxv2e+Cp4oWw==} + '@oxc-parser/binding-freebsd-x64@0.148.0': + resolution: {integrity: sha512-vFsPx+a/qFECPnz/H8nC6x6MDvnWscLTCo/5muojEF54ERUq1kdgbvnWo95YnkhjF9sTIcG/uDxQBh1gffaufQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.147.0': - resolution: {integrity: sha512-AeIiBbwUaP0H1+4/qGW9l5qHecS/+XA5iMuieVcGb1T+tyc2dVGspFW13BWk/XrLsiGP/CiDJTJqAPLLCzZHkw==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.148.0': + resolution: {integrity: sha512-eOr3M+6iGbbxNL4PSS0VtsyQ2eOUxSBh00BqO22SbolDimPSYsBuLr/LCrZBkiqW2BoabhR6V4R8jrRAay7hjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.147.0': - resolution: {integrity: sha512-/41MKPW4RgPY4DJco0NCF0RYX3IMZaVlRNMNzvhaxRavc7tN3Txm+qllZbh0aMRs0VHdgUlbI8TcAOiTai4TKg==} + '@oxc-parser/binding-linux-arm-musleabihf@0.148.0': + resolution: {integrity: sha512-58ZKDw0mQRbCNfrd2IDyV4o8T7enzGERJn41BH2tjrZVGyiKiFzcfDicuB7Zcpb/1xIOrObovr8Dja6lZi8dLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.147.0': - resolution: {integrity: sha512-bmpw/RPhVXgZbtb3xBDuwW5s8+LvZYdqcDSX/sP2ltL77aTio3DP/B5ZTwwgoJ6Mr9vJs4RrmgEKW9XkLNUU1g==} + '@oxc-parser/binding-linux-arm64-gnu@0.148.0': + resolution: {integrity: sha512-Fnu95O4eZ5i++GPvIzBEZ8y4ddTLR+D9paYa8JRaRk6ZK7nHQiWP5xtrhcPQsXqgat1d7sU/d5rbbI0p1FTHSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.147.0': - resolution: {integrity: sha512-gd7VX/FDVOw6mjQcu45iIcp4QkgybgJwh3a0OFG2NxmPCj628mQWD96QGu1kK8+mZF9qK4b/gIEyC63vQoB7+Q==} + '@oxc-parser/binding-linux-arm64-musl@0.148.0': + resolution: {integrity: sha512-3CQy/BMdx7N7H3qrcPxUL+a2CwUZodUcf6oq8iJuNZ9C6Ol1aq3mcWzsgySJ7CHFLvpX21ZDPp1r1X0QLbu/AQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.147.0': - resolution: {integrity: sha512-HnAzcfki7dSUNHf510Q2NmbJlz8Ys7rn8l9l588Pkx0tYe1BHLZnmELIgqizJ4WPhHGSwN8Ce+B/menVxS3odA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.148.0': + resolution: {integrity: sha512-9LkaYvfiF8hMOw900csAvkf1oxE8XlmMeGowu5BcastSSwV8mKvKRMNU7HsV+ycyj1dQD8pX5qgOw8ja6SJacg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.147.0': - resolution: {integrity: sha512-qlkOL6wT44U+fT5s/+sR6Shx0OdwvQF83JyIPZUxG/ovqZF5/7atOtjH+JPZ5/7ATQLbFBBSmghcy/+2NVB/ew==} + '@oxc-parser/binding-linux-riscv64-gnu@0.148.0': + resolution: {integrity: sha512-2GBiM9h26dR4WJfhoMvnFMnFLf7m/kYs4UMqjvrOfQG4BV1nuTJDH22Zc2MQr3INZF7nSKYQ6xlhD3hQ7A6gug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.147.0': - resolution: {integrity: sha512-DlefD7L7sMXs/3hIBH23Egk0phj8kG0SA81dVGOQ3S1ekjOlmTLH2E+F2Thwfh1slKx6aH+lNc5fQYAw0GU7/g==} + '@oxc-parser/binding-linux-riscv64-musl@0.148.0': + resolution: {integrity: sha512-uPqZexvKJmEgq4mAu36qe2xTfXZE7oyik1R7KtZ5tl8qKlq1U1fIqTFRUEBZqRGvforoTrGIpatRzcoPKO66RA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.147.0': - resolution: {integrity: sha512-Xqpagk/031IvZ4svrk2FF01YEqM/iN3MJV3SVZadKg/CsGlDCGoREqKHXYnoV5+8SfGe/m6RM1szXFLusTu/Uw==} + '@oxc-parser/binding-linux-s390x-gnu@0.148.0': + resolution: {integrity: sha512-9oUHvnTbp7ZraFsTC8PN6XhdhPSSxZumYvixWl7Smi353gEULvK6yV0sXNVrdFMHQeaDKFCi8TgDhNK7/A+Y+Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.147.0': - resolution: {integrity: sha512-QioQOeUbI4ATUr0S2z88uA3Cds2R3Mm5Ge7U8XNYtlTb2GJF3rWlcj70z0AJhhOlbdm0YgVjqPBldUNbFylDIg==} + '@oxc-parser/binding-linux-x64-gnu@0.148.0': + resolution: {integrity: sha512-2qhDSJwKzbSZzF7lDqqk8sr/yXsmwr3PeUa4/nazIF+zFAYz1gVPEfC34GQtGxzJUUmklaYAL63368LEfrMeyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.147.0': - resolution: {integrity: sha512-NXy1tv/OdC+pPTwf9RiCZWPK53V/Xq/2cjSnjOSyKopajdaDqMIkgtDY+jXZemp2e8px5FeWfY2L2LwhKZQovg==} + '@oxc-parser/binding-linux-x64-musl@0.148.0': + resolution: {integrity: sha512-qQoPDZUFV0bh9xA09XydmkjMBpgc1ukJuhMvzQ9QeVmFaHTS9W5TE5CoLmSl3QQyUP9OuHO3x/WPZTIIZPWR3Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.147.0': - resolution: {integrity: sha512-GpGWZ6oKz4bjCWW9Mz5pCaGPyk2Aaze6zEoaslIQqpSLtpx5pXj/ap5gUNb5Jn2LIbqWyjkGLX9yv3NMuNcBVQ==} + '@oxc-parser/binding-openharmony-arm64@0.148.0': + resolution: {integrity: sha512-1UGbaQWEXUCLqAmaR5kwRDjx/R4S5LQKZkM9CHmaHkuKhriOF32aRLfS0jCRNE2yGQJLMEA1z9UucbBVqjXnDw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-win32-arm64-msvc@0.147.0': - resolution: {integrity: sha512-a8mlt7CC8z7LUdCfaxhff4kCd+vSjE+NEFL0cxA8ukfuSnvAto/pWTjytW4BuVLnQGcCVdHJwcRKOfs++H+tjw==} + '@oxc-parser/binding-win32-arm64-msvc@0.148.0': + resolution: {integrity: sha512-pWKdzRDNG2+NK4h/V6U/CYERcfYD6u28h5IB/VJVsrZaD3muvE58tUj22lieL5vLZ+XFi1GPv9YXckZbJZ9BLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.147.0': - resolution: {integrity: sha512-M5ViVDBcFLnl2632AuuWuP35zEL5oikK1jTx8r3+902VEDeSNHxFZAB6RZyfZ7MU6Oi5QTOG8MmMkIeHsudSaw==} + '@oxc-parser/binding-win32-ia32-msvc@0.148.0': + resolution: {integrity: sha512-i3p4x+mvwtjcE1J5HM6V7ggsbXiznExN/4MkNyOy3dfXrVV3bnkSfmZxvo6/84qCVX4ShkpNE1SKt9biIF31GQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.147.0': - resolution: {integrity: sha512-DUaE13OwnUSlHpLZNcC/nuT10ivlWqc5EZgsfgXuAmWYw0r3nDxGeLD1zlGwYwIgVk1/ZMAxoXpV+05stvbHaA==} + '@oxc-parser/binding-win32-x64-msvc@0.148.0': + resolution: {integrity: sha512-Ye6vB7VQulghWYkYkECOBYFRVEizz4XyRTUAv+t8BuyurhKU7uD0P9eowL+mKG5Mf8MSYx+DI3Cm8SKZvYG7bQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1946,8 +1946,8 @@ packages: '@oxc-project/types@0.140.0': resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} - '@oxc-project/types@0.147.0': - resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} + '@oxc-project/types@0.148.0': + resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} @@ -2052,124 +2052,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.65.0': - resolution: {integrity: sha512-M10Gs1SSpTNI6ahGx3M/OlIdUF4hkaP6OgUb+MS79t/Pgflk3r1nW5gPFqsZGUAXg0H1AfANT9AvLdBSTIhZKg==} + '@oxfmt/binding-android-arm-eabi@0.66.0': + resolution: {integrity: sha512-2Me9eoptv6ERdEuI2P8AOlYdHHraXebJaM6SC0kc2Dfb+mLrep2db+fedBPKaYn673h/vBgvP4tkOdAbaudX6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.65.0': - resolution: {integrity: sha512-6DXH5sftNlaHpWJG50hFMF+Qxtq5D2TmahvcDPxWNcGIf8qrC9Y0YgHYcYZ2hlWzaccKXh/f3GcssH8vtkl4JA==} + '@oxfmt/binding-android-arm64@0.66.0': + resolution: {integrity: sha512-u7O+bSSF0HGsDKkQQxBqvLGVepu93RA+JKu+ONqvfh4sCnCEbj31wZj4iG5gk3XfRwrmYj0/8catkO2LcblQKQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.65.0': - resolution: {integrity: sha512-K9m7lr53pcOLETNsC88sWes/GWHUGjZyHx95UhYcSXy0r30haLdeXlSufSenEAtoLaW753WN8/l4M7GYcRt6cg==} + '@oxfmt/binding-darwin-arm64@0.66.0': + resolution: {integrity: sha512-/ikyMIVjX/sdo7KtjxoEsSUosfPzveVhT9RWMx9yGqFDKFJ89JAEKuEeLBmurDjrkb4w8tOnAdSO3SBaplY3bw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.65.0': - resolution: {integrity: sha512-sTNwIx1gre3MyiHOPLu7IGW4UyMScYL4DTmJT01p4vzB0En+OJUQz6KuH8t0PpsClRSaMuY3b0QmtoPItfO8Lg==} + '@oxfmt/binding-darwin-x64@0.66.0': + resolution: {integrity: sha512-q5xUsKeFqawa9NXa6ZGXWimFV19m8MogKPdTaSVDAAk2EQKBmBZRDeluwcl1p8ty/OFc9s9888OKEh3xfPVH0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.65.0': - resolution: {integrity: sha512-lYZMVIiIpnjGu5hJb2jxA8NYQ/e0OTGuaiAf4dqlGPNnPmUTu23FZRMltmjro/KkQm1uE4NT4n5yJ2zWmKcpfA==} + '@oxfmt/binding-freebsd-x64@0.66.0': + resolution: {integrity: sha512-CR+x4VzMY0pRXLK/xFQ/RzsSFkP5t2Z2mef0QY6OP/rTRcMUoMLCOM62/3Fp/t0K+UDoBKxvMyeb6D0zPMjleA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.65.0': - resolution: {integrity: sha512-gIdXFAt/bURnjxuoedDEWdZ0PEWEmdDcm8qdpoFYYvW3QMk/5D4vUaH4mlMeRpeTdST4izUgHVO6RawQ4QulJw==} + '@oxfmt/binding-linux-arm-gnueabihf@0.66.0': + resolution: {integrity: sha512-ZEYmO/LbH9tTQCADILHGZE4GeOXOAj2VzedHkASNwjmwlwtutJCLpCJbIs37wRGTFgWRoEcD72jpMX+IBJUGjQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.65.0': - resolution: {integrity: sha512-jJVyADto7gA2AaX5qAjAexrxx9PJQaKWOe8PICE7yKMbjBRyOHcmj9TtVJ+MZYDUQ3hodU0AcoTj0jFQ1W4C6Q==} + '@oxfmt/binding-linux-arm-musleabihf@0.66.0': + resolution: {integrity: sha512-hNtR9/oU0CeTkq7JnRkmBQwqe17v2ZaAMLC4VcN7IIOWeRyWDk0knSPWS9iiLmtbZ2RRBBtsG01jQgkZmKCJeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.65.0': - resolution: {integrity: sha512-p3RFkB+u7u+8up99b/NEcI1hdpLDiGgJYNwDorB60n7eH+eKposAKuMBxx+NqB3b+sJP4CZmYDh9G7X62tUsKg==} + '@oxfmt/binding-linux-arm64-gnu@0.66.0': + resolution: {integrity: sha512-uwOVQ8i6I1LT/+eDzfsgrrcZp8Fn6NPVUPn8fF5gdFGekFf0PddF+LEuwsD0/pbNUcKZhDj2rQ5UpITh9gF4iQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.65.0': - resolution: {integrity: sha512-5Prb0uFzJHr+OUD/qS/TmU526wD+PaHDsm3KoRiUXbMIDpTSErjeQYkK3OQeshAvD/PuLa9WGEi9WPajjdOZJg==} + '@oxfmt/binding-linux-arm64-musl@0.66.0': + resolution: {integrity: sha512-tTkF2Dmx4nGAjmBlZb+UtTGqR/EK4ZrW9qBfzte07a9XWqzoGGKzpFFlyNDhQe+Uwql94+ReCTeNbhOXscw1Dg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.65.0': - resolution: {integrity: sha512-S8svxTp81obnF3admN9yd+u2rOYXtyzThLGBTg1PY6TPtGcC09BaaXLQD+TBSMa7yvqhCDZ8DFri+S/yG60qCg==} + '@oxfmt/binding-linux-ppc64-gnu@0.66.0': + resolution: {integrity: sha512-F3cKHUav4yXOHn6GFnwpBhSYsJOYKKf9eqO/9jlEuqPxNw9zb98E9ZFct79gcg8pibUGkbveEu9WDlmXJpDzKw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.65.0': - resolution: {integrity: sha512-WtXBr75G/h2qOHy8SiGtC1R6aS3jt4mE52v1D8AtwMXIgoOmSNP9lKvbSaTRoL0e5wsMPoi6T72QWDYPu+S+nA==} + '@oxfmt/binding-linux-riscv64-gnu@0.66.0': + resolution: {integrity: sha512-K5fDaNZfDyQMYA/3qL21bqyN0X9T15LLwwbFPt2aHc94+ZG7bh0vZEsy2y7NlRnjjHFSwN+Hzg6ldJtbOriH4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.65.0': - resolution: {integrity: sha512-YwSLVvpaz4o/nv/miiPEBJz+eJ+VmbgNIrao6RccK9ce+L5EA8wP+ZD0uFeq6wKOza6zoWv/dR0sj6lip6R3EA==} + '@oxfmt/binding-linux-riscv64-musl@0.66.0': + resolution: {integrity: sha512-44Yc+I+qOmTElRcEhm5hUKIUJEQIOugymz4ua4tB0Wox7tGAfIbjzmXz/HDAtw1Ij6gmBwZlzh4hc9679RhWeA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.65.0': - resolution: {integrity: sha512-XQTPqgvyrgkKcFq+Tp2eK6JS7sqqJ+nRmy2Fav4j3I+i4dJoPJm7YwEdoeSDX9xkqj9jZ/lWfF3bXUWztIrn6A==} + '@oxfmt/binding-linux-s390x-gnu@0.66.0': + resolution: {integrity: sha512-1e29Eg9hEj2kRBB19M0seIehPbbXHCk35GvImjDvb79rjjYjXCRmtbUNHJcgoktZAMIzXrTbxDBKmTc1V4bg3A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.65.0': - resolution: {integrity: sha512-cjZlx6S/VkeCNWCbwZriTnLnZeTcV3DEyeRGSw/2wwLP9viq+C0bJ4bC1k/ZLkFxDcB1lUgSasPkYGP1bdraOg==} + '@oxfmt/binding-linux-x64-gnu@0.66.0': + resolution: {integrity: sha512-vODY1UQo10gngn0+D4xHKU84F1Twm1LqrzV4SqPXvmQKSd87paehvZ6jqA5wKs6XQrlWul9clYMDVHcoW9CPMA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.65.0': - resolution: {integrity: sha512-2azCjxdLtK4zCcIOU1dlXlU0xxfbPi6EjwWx7Ac7teWPidIIDOcIhudup83xNCKYhtqeVd/gaVDOxbUq4syXWA==} + '@oxfmt/binding-linux-x64-musl@0.66.0': + resolution: {integrity: sha512-YDzXx2JsT4+HL4MdkVrYjO55NS5lUKNm8rLC4ZPou8+seu0v0jhecSh+ufoO6+xEa8gccEezMlI2WHJi4ApUgw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.65.0': - resolution: {integrity: sha512-KXQ7xi1e/voP0IQaw6fG6XY4Z5+Llf1XmRSZS1t7pVFCecFJ0iXaboKmVwjFtp5MLlT5iWQrJ2U1C3GJdZ2u+Q==} + '@oxfmt/binding-openharmony-arm64@0.66.0': + resolution: {integrity: sha512-mJjUYd8lj0+j4JkYyEM+5qKBf1Rnrpgjn/SVYKJhicVDqLz566ooa7Fs8zflPqt+dnZDV7X054rVIQX6ZcQNlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.65.0': - resolution: {integrity: sha512-2FbbjG5jEqLSLKVJwBap84uJfpn5Y5A53KEO0aUNr+zeiRB9nyPUIFMcSbZVMFLitfBytFWRNngozXYjb6Rsbw==} + '@oxfmt/binding-win32-arm64-msvc@0.66.0': + resolution: {integrity: sha512-soV+0vESv7e5ntCHWC61x4gg8OSak6IHHnWsZmHrJFlvMj2AK+kmldErCNkVkrvc1Ts2/++rJXn+IuAb2WMXhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.65.0': - resolution: {integrity: sha512-LJ+ZacAPSjegDOnSLyA1TMWAhdDrsK4el3REdr1oL2UtVBCMhO2II/Sb3cEW6mF2MfLhl8hDNCSvc7KSbgk3LQ==} + '@oxfmt/binding-win32-ia32-msvc@0.66.0': + resolution: {integrity: sha512-YCPi23uRIEYuIKTZohAkKbPFpujQ5QBuUM5iDv+UqbCmTPAkaFsxjsSuB8xlBpRT0G7eP/4HMF+cPDSqHtOD9A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.65.0': - resolution: {integrity: sha512-higu9cWEO6XXFzATD1jf0mCK34rNfN2H9JrJie7QB1IhleVpTh0QlLH9Ip2C1H/Nd5n0v5pvRtC+5R0uE4HpVg==} + '@oxfmt/binding-win32-x64-msvc@0.66.0': + resolution: {integrity: sha512-bwTQcv/JVRPkOqQtMF0X7vpvpncDQiBcXHxZ9S2hR12Hlo8bvBdUR5x5XnxzDZ3kM0qoZw1rv7KaD66Ly+pFWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2204,130 +2204,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.80.0': - resolution: {integrity: sha512-RM3Plj+biQpxa5d1GOOX6ciDlcUROmm4OZ/pLTpitkQt2mJv4jhtY4cbgaetOm5UKWZe05/TGQ6o1Vl8EOHkrA==} + '@oxlint/binding-android-arm-eabi@1.81.0': + resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.80.0': - resolution: {integrity: sha512-YlO5JEf0Yr2bUUlu8O8daVcUxtcGGbcSmyV7E7nSbJbfAdxTE0PFPwgnIlw7wXJaTYjb+qs5hI5q3jxUkI7cAw==} + '@oxlint/binding-android-arm64@1.81.0': + resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.80.0': - resolution: {integrity: sha512-BULDOyO3AhsmdWfQeIUCykDt3dd7XZBGLhp1eIh56skRv01O+cNjNPwXMIbeW1x4+pxcln5if72wcRgViVo7PA==} + '@oxlint/binding-darwin-arm64@1.81.0': + resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.80.0': - resolution: {integrity: sha512-YJ4JzLw7N5TDSQFlA0hAQGHvnDZgyypm1yunObVWcWiF9KM7eGCJKYKLgTC2Fi/57OdnBhbj4OkzPGdFQJ6HyA==} + '@oxlint/binding-darwin-x64@1.81.0': + resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.80.0': - resolution: {integrity: sha512-AYUIk5QnL0s8oWAYsREZwkRYy1SupJTXALo93J1TgzHywxQtdM99FecRMQ87MXEdPQ0j1TmEpeeq3fGNkpvMqg==} + '@oxlint/binding-freebsd-x64@1.81.0': + resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.80.0': - resolution: {integrity: sha512-9hBZVANupQ89W9dXyE0n8doCyaW5pDyGn3y6XlIMPZ+rIKuyqkr3SNUXmVJIhuvUq0NBU3RBiSXXE69l4XI6KA==} + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': + resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.80.0': - resolution: {integrity: sha512-SvS2uKqzY+pbfuvAHzH4338R6Zwo805GAwrIMVvK1KxoOWCIjZUdfzTCvilD7z6JK91v011+zYMryabhDo2AsQ==} + '@oxlint/binding-linux-arm-musleabihf@1.81.0': + resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.80.0': - resolution: {integrity: sha512-tCLadyqRVL3pQTRPNg7cjXKvcvS4fbyXeQHhKk5BTJ1oftQln5/yIIWbu/Xom/DX41zv2P9QGt6+D/TtQVtY3A==} + '@oxlint/binding-linux-arm64-gnu@1.81.0': + resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.80.0': - resolution: {integrity: sha512-XfpCNRlOPcLlJl4Bn/FUhjqlR6BVavEykERBf/MV7YA9VZDa5g5znVqYhyviMafcxS9Pe/i/kPvHNO0U6svEHQ==} + '@oxlint/binding-linux-arm64-musl@1.81.0': + resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.80.0': - resolution: {integrity: sha512-3I4yMwcFG9NeO8ioY6JBBuKsIm5GL/x7MATt1S4tVWaxPu5HcJ+XnLUbcVBTxG8q2Wu56HSj+NmXQiVYb1lp6A==} + '@oxlint/binding-linux-ppc64-gnu@1.81.0': + resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.80.0': - resolution: {integrity: sha512-E1wAKymkpe1/E8helzBKdm81OBOF+ezxRyXRMEuik3ZpWDER5CPOKZwF66RsdwW98uwZv8UTFremUQtC1CzdJA==} + '@oxlint/binding-linux-riscv64-gnu@1.81.0': + resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.80.0': - resolution: {integrity: sha512-+gLRGD4sIo3+VA++iham5UxD9tKSoJ/VOrROCEXIcknrYtQg6iIQgvjN0cpiRF7N6UYC7pJbvHJlDnMge5LRpQ==} + '@oxlint/binding-linux-riscv64-musl@1.81.0': + resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.80.0': - resolution: {integrity: sha512-aR0PrzHj9leW3NmzBAAP4EzdoBNoJcs9sjnIQPIwyRnBGYrRbXUIpEB5Q39AqK3PLY5JK5uEhDQDiUa1QSAstw==} + '@oxlint/binding-linux-s390x-gnu@1.81.0': + resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.80.0': - resolution: {integrity: sha512-vSVh5cSo3Xxs6ghBCcFJlpbkbENzDog1qXtoXLa/HC3aCrR4XO76GZbXmQoCPHnu99nQpdCeC3H9tdNICfDh7A==} + '@oxlint/binding-linux-x64-gnu@1.81.0': + resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.80.0': - resolution: {integrity: sha512-FfzBXpNQ8u7/ZI/p8bl73MeZ508Ax3hxWp3SiJpEFiC+BB9XcXy5FAZHTLKDPSzrUpxQZSZJAVdDmuJp/+HDBQ==} + '@oxlint/binding-linux-x64-musl@1.81.0': + resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.80.0': - resolution: {integrity: sha512-zMzbkumtmprCgRwoYNzcB3iC39fXdJIMLMU33KdCjEGLlJGOEt1+LwQ4LF8ndLzAEKVz4BR0y3V6Xrkk3Nm3yA==} + '@oxlint/binding-openharmony-arm64@1.81.0': + resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.80.0': - resolution: {integrity: sha512-ib6iRcrXsk4t1fm3iKcwksyWh1ZkZXC/2mEzakl0ai2+6HZunf1WWMZ/xP9EJAvw9g9K4UVTC3NF/+G2qLrbTQ==} + '@oxlint/binding-win32-arm64-msvc@1.81.0': + resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.80.0': - resolution: {integrity: sha512-xhRWBMpLxZvgKAH6+DJZmpP+W8Y8UdQOSU1JfxSWNXsaBaRGW77j+1hCuNHlzj7OH4SPN8fYd1q0o2qrDtoVyw==} + '@oxlint/binding-win32-ia32-msvc@1.81.0': + resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.80.0': - resolution: {integrity: sha512-yAnO7lwBYQnz2pcfBPIGQQZWIX5zd5R/1aAKIF3oE+TVj7IhoHcROjOkz3sRDngzqhfPKfFaXqug5j5rE5dn6Q==} + '@oxlint/binding-win32-x64-msvc@1.81.0': + resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.80.0': - resolution: {integrity: sha512-QRgH1XqQEYNHa4f1vvPQ5fAdNdncHGIUG1ZWLlGIZHky3qwCEeAKYitZNbZMtaXtAQAAFFTOwqUfzESvimqZNA==} + '@oxlint/plugins@1.81.0': + resolution: {integrity: sha512-HhD8kd3r6XpelZkhxhRty/po8V6yK1VV63Eq4kSsOS2IoHUywG3DV2EX+z/ZHw46aLI74Y6aA604NRiAqLKW9w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -3738,7 +3738,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -5194,15 +5194,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.147.0: - resolution: {integrity: sha512-5xaug6t7GfV3BO5Iv+xHW1rmQkDEQ3BEu3L8g3InsvWO5i8CYGc4tCZ2X985QcwWNycFJam+aOns6Nr2XAThTA==} + oxc-parser@0.148.0: + resolution: {integrity: sha512-syxUKHeUll89RIABQADcI7sikYrwyssvA6gj4phSSIPezKVM8yMaLAiLLSc7fmzVvrwybfFGFbW5zme9sX87rg==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.65.0: - resolution: {integrity: sha512-SgS5VgnP42T0zl3zWD+xoH8FCqg1SAFnSRoOT/qeoa6gxcYIqrDMOmcXIg/EWSN92Du4ogB4riuKhKd6Y4CGhw==} + oxfmt@0.66.0: + resolution: {integrity: sha512-FfvqR8RFtV6JJpRrpkfqyVCQ7HDvZ/VriWFx7veftCgL1B5ZO9qNr+1rvPieycMQnNfVG0PWyJQiy7p0hq1I5w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5214,16 +5214,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.80.0: - resolution: {integrity: sha512-ElAyNkGRwxc+sMiSn/aJG0Qta7DkPqeLqDENz3iRm5UMgVaISO1Golm0vRo3UM8+DYQhHos2548gjS7lrjHc1w==} + oxlint-plugin-eslint@1.81.0: + resolution: {integrity: sha512-rP192UkflmY1cAubbqlGccVRGpyK3MMSzZhN2V5njKzbNJW+KmJE/9JjJaVl9mQPq686xZ2Zc0xFcUAiP8oIMw==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@7.0.2001: resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.80.0: - resolution: {integrity: sha512-5nTiSps4qdbCWLbxzuO00alHkEO2exR9YMN/ig6QXWrLsYSG0KaObOAM+l6oU2LcKPWoSAGYbkZIGEu1ViiWKA==} + oxlint@1.81.0: + resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7577,66 +7577,66 @@ snapshots: dependencies: '@otplib/core': 13.5.0 - '@oxc-parser/binding-android-arm-eabi@0.147.0': + '@oxc-parser/binding-android-arm-eabi@0.148.0': optional: true - '@oxc-parser/binding-android-arm64@0.147.0': + '@oxc-parser/binding-android-arm64@0.148.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.147.0': + '@oxc-parser/binding-darwin-arm64@0.148.0': optional: true - '@oxc-parser/binding-darwin-x64@0.147.0': + '@oxc-parser/binding-darwin-x64@0.148.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.147.0': + '@oxc-parser/binding-freebsd-x64@0.148.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.147.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.148.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.147.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.148.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.147.0': + '@oxc-parser/binding-linux-arm64-gnu@0.148.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.147.0': + '@oxc-parser/binding-linux-arm64-musl@0.148.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.147.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.148.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.147.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.148.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.147.0': + '@oxc-parser/binding-linux-riscv64-musl@0.148.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.147.0': + '@oxc-parser/binding-linux-s390x-gnu@0.148.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.147.0': + '@oxc-parser/binding-linux-x64-gnu@0.148.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.147.0': + '@oxc-parser/binding-linux-x64-musl@0.148.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.147.0': + '@oxc-parser/binding-openharmony-arm64@0.148.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.147.0': + '@oxc-parser/binding-win32-arm64-msvc@0.148.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.147.0': + '@oxc-parser/binding-win32-ia32-msvc@0.148.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.147.0': + '@oxc-parser/binding-win32-x64-msvc@0.148.0': optional: true '@oxc-project/types@0.140.0': {} - '@oxc-project/types@0.147.0': {} + '@oxc-project/types@0.148.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7699,61 +7699,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.65.0': + '@oxfmt/binding-android-arm-eabi@0.66.0': optional: true - '@oxfmt/binding-android-arm64@0.65.0': + '@oxfmt/binding-android-arm64@0.66.0': optional: true - '@oxfmt/binding-darwin-arm64@0.65.0': + '@oxfmt/binding-darwin-arm64@0.66.0': optional: true - '@oxfmt/binding-darwin-x64@0.65.0': + '@oxfmt/binding-darwin-x64@0.66.0': optional: true - '@oxfmt/binding-freebsd-x64@0.65.0': + '@oxfmt/binding-freebsd-x64@0.66.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.65.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.66.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.65.0': + '@oxfmt/binding-linux-arm-musleabihf@0.66.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.65.0': + '@oxfmt/binding-linux-arm64-gnu@0.66.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.65.0': + '@oxfmt/binding-linux-arm64-musl@0.66.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.65.0': + '@oxfmt/binding-linux-ppc64-gnu@0.66.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.65.0': + '@oxfmt/binding-linux-riscv64-gnu@0.66.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.65.0': + '@oxfmt/binding-linux-riscv64-musl@0.66.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.65.0': + '@oxfmt/binding-linux-s390x-gnu@0.66.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.65.0': + '@oxfmt/binding-linux-x64-gnu@0.66.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.65.0': + '@oxfmt/binding-linux-x64-musl@0.66.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.65.0': + '@oxfmt/binding-openharmony-arm64@0.66.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.65.0': + '@oxfmt/binding-win32-arm64-msvc@0.66.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.65.0': + '@oxfmt/binding-win32-ia32-msvc@0.66.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.65.0': + '@oxfmt/binding-win32-x64-msvc@0.66.0': optional: true '@oxlint-tsgolint/darwin-arm64@7.0.2001': @@ -7774,64 +7774,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.80.0': + '@oxlint/binding-android-arm-eabi@1.81.0': optional: true - '@oxlint/binding-android-arm64@1.80.0': + '@oxlint/binding-android-arm64@1.81.0': optional: true - '@oxlint/binding-darwin-arm64@1.80.0': + '@oxlint/binding-darwin-arm64@1.81.0': optional: true - '@oxlint/binding-darwin-x64@1.80.0': + '@oxlint/binding-darwin-x64@1.81.0': optional: true - '@oxlint/binding-freebsd-x64@1.80.0': + '@oxlint/binding-freebsd-x64@1.81.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.80.0': + '@oxlint/binding-linux-arm-gnueabihf@1.81.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.80.0': + '@oxlint/binding-linux-arm-musleabihf@1.81.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.80.0': + '@oxlint/binding-linux-arm64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.80.0': + '@oxlint/binding-linux-arm64-musl@1.81.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.80.0': + '@oxlint/binding-linux-ppc64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.80.0': + '@oxlint/binding-linux-riscv64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.80.0': + '@oxlint/binding-linux-riscv64-musl@1.81.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.80.0': + '@oxlint/binding-linux-s390x-gnu@1.81.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.80.0': + '@oxlint/binding-linux-x64-gnu@1.81.0': optional: true - '@oxlint/binding-linux-x64-musl@1.80.0': + '@oxlint/binding-linux-x64-musl@1.81.0': optional: true - '@oxlint/binding-openharmony-arm64@1.80.0': + '@oxlint/binding-openharmony-arm64@1.81.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.80.0': + '@oxlint/binding-win32-arm64-msvc@1.81.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.80.0': + '@oxlint/binding-win32-ia32-msvc@1.81.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.80.0': + '@oxlint/binding-win32-x64-msvc@1.81.0': optional: true - '@oxlint/plugins@1.80.0': {} + '@oxlint/plugins@1.81.0': {} '@pinojs/redact@0.4.0': {} @@ -10848,29 +10848,29 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.147.0: + oxc-parser@0.148.0: dependencies: - '@oxc-project/types': 0.147.0 + '@oxc-project/types': 0.148.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.147.0 - '@oxc-parser/binding-android-arm64': 0.147.0 - '@oxc-parser/binding-darwin-arm64': 0.147.0 - '@oxc-parser/binding-darwin-x64': 0.147.0 - '@oxc-parser/binding-freebsd-x64': 0.147.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.147.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.147.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.147.0 - '@oxc-parser/binding-linux-arm64-musl': 0.147.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.147.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.147.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.147.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.147.0 - '@oxc-parser/binding-linux-x64-gnu': 0.147.0 - '@oxc-parser/binding-linux-x64-musl': 0.147.0 - '@oxc-parser/binding-openharmony-arm64': 0.147.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.147.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.147.0 - '@oxc-parser/binding-win32-x64-msvc': 0.147.0 + '@oxc-parser/binding-android-arm-eabi': 0.148.0 + '@oxc-parser/binding-android-arm64': 0.148.0 + '@oxc-parser/binding-darwin-arm64': 0.148.0 + '@oxc-parser/binding-darwin-x64': 0.148.0 + '@oxc-parser/binding-freebsd-x64': 0.148.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.148.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.148.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.148.0 + '@oxc-parser/binding-linux-arm64-musl': 0.148.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.148.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.148.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.148.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.148.0 + '@oxc-parser/binding-linux-x64-gnu': 0.148.0 + '@oxc-parser/binding-linux-x64-musl': 0.148.0 + '@oxc-parser/binding-openharmony-arm64': 0.148.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.148.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.148.0 + '@oxc-parser/binding-win32-x64-msvc': 0.148.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10894,31 +10894,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.65.0: + oxfmt@0.66.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.65.0 - '@oxfmt/binding-android-arm64': 0.65.0 - '@oxfmt/binding-darwin-arm64': 0.65.0 - '@oxfmt/binding-darwin-x64': 0.65.0 - '@oxfmt/binding-freebsd-x64': 0.65.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.65.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.65.0 - '@oxfmt/binding-linux-arm64-gnu': 0.65.0 - '@oxfmt/binding-linux-arm64-musl': 0.65.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.65.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.65.0 - '@oxfmt/binding-linux-riscv64-musl': 0.65.0 - '@oxfmt/binding-linux-s390x-gnu': 0.65.0 - '@oxfmt/binding-linux-x64-gnu': 0.65.0 - '@oxfmt/binding-linux-x64-musl': 0.65.0 - '@oxfmt/binding-openharmony-arm64': 0.65.0 - '@oxfmt/binding-win32-arm64-msvc': 0.65.0 - '@oxfmt/binding-win32-ia32-msvc': 0.65.0 - '@oxfmt/binding-win32-x64-msvc': 0.65.0 - - oxlint-plugin-eslint@1.80.0: {} + '@oxfmt/binding-android-arm-eabi': 0.66.0 + '@oxfmt/binding-android-arm64': 0.66.0 + '@oxfmt/binding-darwin-arm64': 0.66.0 + '@oxfmt/binding-darwin-x64': 0.66.0 + '@oxfmt/binding-freebsd-x64': 0.66.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.66.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.66.0 + '@oxfmt/binding-linux-arm64-gnu': 0.66.0 + '@oxfmt/binding-linux-arm64-musl': 0.66.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.66.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.66.0 + '@oxfmt/binding-linux-riscv64-musl': 0.66.0 + '@oxfmt/binding-linux-s390x-gnu': 0.66.0 + '@oxfmt/binding-linux-x64-gnu': 0.66.0 + '@oxfmt/binding-linux-x64-musl': 0.66.0 + '@oxfmt/binding-openharmony-arm64': 0.66.0 + '@oxfmt/binding-win32-arm64-msvc': 0.66.0 + '@oxfmt/binding-win32-ia32-msvc': 0.66.0 + '@oxfmt/binding-win32-x64-msvc': 0.66.0 + + oxlint-plugin-eslint@1.81.0: {} oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -10929,27 +10929,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.80.0(oxlint-tsgolint@7.0.2001): + oxlint@1.81.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.80.0 - '@oxlint/binding-android-arm64': 1.80.0 - '@oxlint/binding-darwin-arm64': 1.80.0 - '@oxlint/binding-darwin-x64': 1.80.0 - '@oxlint/binding-freebsd-x64': 1.80.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.80.0 - '@oxlint/binding-linux-arm-musleabihf': 1.80.0 - '@oxlint/binding-linux-arm64-gnu': 1.80.0 - '@oxlint/binding-linux-arm64-musl': 1.80.0 - '@oxlint/binding-linux-ppc64-gnu': 1.80.0 - '@oxlint/binding-linux-riscv64-gnu': 1.80.0 - '@oxlint/binding-linux-riscv64-musl': 1.80.0 - '@oxlint/binding-linux-s390x-gnu': 1.80.0 - '@oxlint/binding-linux-x64-gnu': 1.80.0 - '@oxlint/binding-linux-x64-musl': 1.80.0 - '@oxlint/binding-openharmony-arm64': 1.80.0 - '@oxlint/binding-win32-arm64-msvc': 1.80.0 - '@oxlint/binding-win32-ia32-msvc': 1.80.0 - '@oxlint/binding-win32-x64-msvc': 1.80.0 + '@oxlint/binding-android-arm-eabi': 1.81.0 + '@oxlint/binding-android-arm64': 1.81.0 + '@oxlint/binding-darwin-arm64': 1.81.0 + '@oxlint/binding-darwin-x64': 1.81.0 + '@oxlint/binding-freebsd-x64': 1.81.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.81.0 + '@oxlint/binding-linux-arm-musleabihf': 1.81.0 + '@oxlint/binding-linux-arm64-gnu': 1.81.0 + '@oxlint/binding-linux-arm64-musl': 1.81.0 + '@oxlint/binding-linux-ppc64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-gnu': 1.81.0 + '@oxlint/binding-linux-riscv64-musl': 1.81.0 + '@oxlint/binding-linux-s390x-gnu': 1.81.0 + '@oxlint/binding-linux-x64-gnu': 1.81.0 + '@oxlint/binding-linux-x64-musl': 1.81.0 + '@oxlint/binding-openharmony-arm64': 1.81.0 + '@oxlint/binding-win32-arm64-msvc': 1.81.0 + '@oxlint/binding-win32-ia32-msvc': 1.81.0 + '@oxlint/binding-win32-x64-msvc': 1.81.0 oxlint-tsgolint: 7.0.2001 p-cancelable@4.0.1: {} From 21b794b68172d9384d6f93347b559cae704148b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:57:30 +0800 Subject: [PATCH 0740/1190] chore(deps-dev): bump the eslint group across 1 directory with 2 updates (#23240) * chore(deps-dev): bump the eslint group across 1 directory with 2 updates Bumps the eslint group with 2 updates in the / directory: [@eslint/eslintrc](https://github.com/eslint/eslintrc) and [eslint](https://github.com/eslint/eslint). Updates `@eslint/eslintrc` from 3.3.6 to 3.3.7 - [Release notes](https://github.com/eslint/eslintrc/releases) - [Changelog](https://github.com/eslint/eslintrc/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.6...eslintrc-v3.3.7) Updates `eslint` from 10.9.1 to 10.10.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0) --- updated-dependencies: - dependency-name: "@eslint/eslintrc" dependency-version: 3.3.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: eslint - dependency-name: eslint dependency-version: 10.10.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] * chore: remove eslint/eslintrc --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 3 +- pnpm-lock.yaml | 324 +++++++++++++++++++++++-------------------------- 2 files changed, 150 insertions(+), 177 deletions(-) diff --git a/package.json b/package.json index f0712a156921..4769693d604f 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,6 @@ "@cloudflare/playwright": "1.3.6", "@cloudflare/vitest-pool-workers": "0.22.0", "@cloudflare/workers-types": "5.20260901.1", - "@eslint/eslintrc": "3.3.6", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.81.0", "@stylistic/eslint-plugin": "5.10.0", @@ -174,7 +173,7 @@ "discord-api-types": "0.38.55", "domhandler": "6.0.1", "esbuild": "0.28.1", - "eslint": "10.9.1", + "eslint": "10.10.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.3.0", "eslint-plugin-regexp": "3.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 043f50a86c22..07dbc157f252 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -305,18 +305,15 @@ importers: '@cloudflare/workers-types': specifier: 5.20260901.1 version: 5.20260901.1 - '@eslint/eslintrc': - specifier: 3.3.6 - version: 3.3.6 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.9.1(jiti@2.7.0)) + version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) '@oxlint/plugins': specifier: 1.81.0 version: 1.81.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.9.1(jiti@2.7.0)) + version: 5.10.0(eslint@10.10.0(jiti@2.7.0)) '@types/crypto-js': specifier: 4.2.2 version: 4.2.2 @@ -352,10 +349,10 @@ importers: version: 2.16.1 '@typescript-eslint/eslint-plugin': specifier: 8.69.0 - version: 8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + version: 8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/parser': specifier: 8.69.0 - version: 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + version: 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 version: 1.11.0(rollup@4.63.1) @@ -372,26 +369,26 @@ importers: specifier: 0.28.1 version: 0.28.1 eslint: - specifier: 10.9.1 - version: 10.9.1(jiti@2.7.0) + specifier: 10.10.0 + version: 10.10.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.4.1)(eslint@10.9.1(jiti@2.7.0)) + version: 9.1.1(@types/node@26.4.1)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 - version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-regexp: specifier: 3.3.0 - version: 3.3.0(eslint@10.9.1(jiti@2.7.0)) + version: 3.3.0(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: 14.0.0 - version: 14.0.0(eslint@10.9.1(jiti@2.7.0)) + version: 14.0.0(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-unicorn: specifier: 74.0.0 - version: 74.0.0(eslint@10.9.1(jiti@2.7.0)) + version: 74.0.0(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-yml: specifier: 3.8.1 - version: 3.8.1(eslint@10.9.1(jiti@2.7.0)) + version: 3.8.1(eslint@10.10.0(jiti@2.7.0)) fast-string-width: specifier: 3.0.2 version: 3.0.2 @@ -564,6 +561,12 @@ packages: '@bufbuild/protobuf@2.14.0': resolution: {integrity: sha512-C3UGsiCwSprE2NKIIFA3hCDlpXTMCAXRZuEVp88L1GY36Y41+rYL5fryE+nOFhp4p4JPQvdV8PQ4DWgHgeTE+w==} + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + '@cloudflare/containers@0.3.7': resolution: {integrity: sha512-DM9dm3FnIBSyiSJ1FLavKwl/lk3oAmTaynCzZQ9pZR0ncRPquSxkxd8Nu2MFILxmDDsPkxKsSNEh9mHHMty4Fw==} @@ -1214,10 +1217,6 @@ packages: resolution: {integrity: sha512-iPmijIAq4hlIJB86PYmY/fcZORHtjphSqICDbwuw32A/JmkhZQ/K/6TjHE03zqf3n5yABpVcbRAMG8Mi9ojy8g==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.6': - resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@10.0.1': resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -1235,6 +1234,10 @@ packages: resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/plugin-kit@0.7.3': + resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@exodus/bytes@1.15.1': resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1588,6 +1591,12 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} @@ -3298,9 +3307,6 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - argparse@3.0.0: resolution: {integrity: sha512-BOp5NMrHqKxmq/OLr+clzzrRxgOKSLkcjmkWuChp7Irqwn4s74WjOBPIgWfA/HMcBnVkZ5XEuf9uUqzlpfCQ6A==} @@ -3397,9 +3403,6 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.18: - resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} - brace-expansion@2.1.4: resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} @@ -3442,6 +3445,9 @@ packages: resolution: {integrity: sha512-SVXGH037+Mo1aIMO5B2UcleR43FGjFdN+M8JObSyEoQ2Mn4CODRWx28gN5jiTF0n5ItsgtIZfyargMNs8GX4kg==} engines: {node: '>=18'} + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -3558,9 +3564,6 @@ packages: resolution: {integrity: sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==} engines: {node: '>= 12.0.0'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -3738,7 +3741,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -3983,8 +3986,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.9.1: - resolution: {integrity: sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==} + eslint@10.10.0: + resolution: {integrity: sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -4098,9 +4101,8 @@ packages: fflate@0.8.3: resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -4117,9 +4119,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} @@ -4222,10 +4223,6 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} @@ -4280,6 +4277,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -4304,6 +4305,12 @@ packages: hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -4400,10 +4407,6 @@ packages: imapflow@1.7.8: resolution: {integrity: sha512-dJoCIdZOJh26Rn2PdwEzwj0bRDgGBxxX38pio534FagIHVuR2l0SAfLr6sJo32YfuJHiSs/U2ntNDHnMg3/Hlg==} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - import-in-the-middle@3.4.0: resolution: {integrity: sha512-Xfjwfarhe+LGmoaof+sexeNo3sGRysb5x56WrZLwHtmqT0OilSKtVWkv0lrCcMgSKyKP9oBogBAisHZvtJH0aw==} engines: {node: '>=18'} @@ -4576,10 +4579,6 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.3.1: - resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} - hasBin: true - jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} @@ -4604,9 +4603,6 @@ packages: json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-nd@1.0.0: resolution: {integrity: sha512-8TIp0HZAY0VVrwRQJJPb4+nOTSPoOWZeEKBTLizUfQO4oym5Fc/MKqN8vEbLCxcyxDf2vwNxOQ1q84O49GWPyQ==} @@ -4646,9 +4642,6 @@ packages: jws@4.0.1: resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} @@ -5004,9 +4997,6 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -5276,10 +5266,6 @@ packages: resolution: {integrity: sha512-weZKJIwwy5gjt4STGVUH9bix3BGk7wZ2ahtIypwe3e/mllsrIZIvtfLx1dPX56GcpZFOCFKmeqI1qVuB9enRzA==} hasBin: true - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-srcset@1.0.2: resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} @@ -5414,6 +5400,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + qs@6.14.2: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} @@ -5554,10 +5544,6 @@ packages: resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -5793,10 +5779,6 @@ packages: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - striptags@3.2.0: resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} @@ -6632,6 +6614,18 @@ snapshots: '@bufbuild/protobuf@2.14.0': {} + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + '@cloudflare/containers@0.3.7': {} '@cloudflare/kv-asset-handler@0.5.0': {} @@ -6996,9 +6990,9 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.9.1(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.10.0(jiti@2.7.0))': dependencies: - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -7024,23 +7018,9 @@ snapshots: mdn-data: 2.29.0 source-map-js: 1.2.1 - '@eslint/eslintrc@3.3.6': - dependencies: - ajv: 6.15.0 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.3.1 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@10.0.1(eslint@10.9.1(jiti@2.7.0))': + '@eslint/js@10.0.1(eslint@10.10.0(jiti@2.7.0))': optionalDependencies: - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) '@eslint/object-schema@3.0.5': {} @@ -7049,6 +7029,11 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 + '@eslint/plugin-kit@0.7.3': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + '@exodus/bytes@1.15.1(@noble/hashes@2.3.0)': optionalDependencies: '@noble/hashes': 2.3.0 @@ -7331,6 +7316,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + '@keyv/serialize@1.1.1': {} '@lifeomic/attempt@3.1.0': {} @@ -8138,11 +8129,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.9.1(jiti@2.7.0))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.10.0(jiti@2.7.0))': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/types': 8.65.0 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -8287,15 +8278,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0))': + '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + '@typescript-eslint/parser': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/type-utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) - '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + '@typescript-eslint/type-utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/visitor-keys': 8.69.0 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) ignore: 7.0.8 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8303,14 +8294,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0))': + '@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: '@typescript-eslint/scope-manager': 8.69.0 '@typescript-eslint/types': 8.69.0 '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) '@typescript-eslint/visitor-keys': 8.69.0 debug: 4.4.3 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8333,13 +8324,13 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0))': + '@typescript-eslint/type-utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: '@typescript-eslint/types': 8.69.0 '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)) + '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) debug: 4.4.3 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -8364,13 +8355,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0))': + '@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.69.0 '@typescript-eslint/types': 8.69.0 '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8638,8 +8629,6 @@ snapshots: arg@5.0.2: {} - argparse@2.0.1: {} - argparse@3.0.0: {} asn1@0.2.6: @@ -8714,11 +8703,6 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.18: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.1.4: dependencies: balanced-match: 1.0.2 @@ -8765,6 +8749,14 @@ snapshots: normalize-url: 8.1.1 responselike: 4.0.2 + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + callsites@3.1.0: {} camelcase-keys@9.1.3: @@ -8872,8 +8864,6 @@ snapshots: comment-parser@1.4.8: {} - concat-map@0.0.1: {} - config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -9252,43 +9242,43 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.9.1(jiti@2.7.0)): + eslint-compat-utils@0.5.1(eslint@10.10.0(jiti@2.7.0)): dependencies: - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) semver: 7.8.5 - eslint-filtered-fix@0.3.0(eslint@10.9.1(jiti@2.7.0)): + eslint-filtered-fix@0.3.0(eslint@10.10.0(jiti@2.7.0)): dependencies: - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.4.1)(eslint@10.9.1(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.4.1)(eslint@10.10.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 '@inquirer/checkbox': 4.3.2(@types/node@26.4.1) '@inquirer/confirm': 5.1.21(@types/node@26.4.1) '@inquirer/select': 4.4.2(@types/node@26.4.1) - eslint: 10.9.1(jiti@2.7.0) - eslint-filtered-fix: 0.3.0(eslint@10.9.1(jiti@2.7.0)) + eslint: 10.10.0(jiti@2.7.0) + eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) optionator: 0.9.4 text-table: 0.2.0 yoctocolors: 2.1.2 transitivePeerDependencies: - '@types/node' - eslint-plugin-es-x@7.8.0(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-es-x@7.8.0(eslint@10.10.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 - eslint: 10.9.1(jiti@2.7.0) - eslint-compat-utils: 0.5.1(eslint@10.9.1(jiti@2.7.0)) + eslint: 10.10.0(jiti@2.7.0) + eslint-compat-utils: 0.5.1(eslint@10.10.0(jiti@2.7.0)) - eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) enhanced-resolve: 5.24.5 - eslint: 10.9.1(jiti@2.7.0) - eslint-plugin-es-x: 7.8.0(eslint@10.9.1(jiti@2.7.0)) + eslint: 10.10.0(jiti@2.7.0) + eslint-plugin-es-x: 7.8.0(eslint@10.10.0(jiti@2.7.0)) get-tsconfig: 4.14.1 globals: 15.15.0 globrex: 0.1.2 @@ -9297,24 +9287,24 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.3.0(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-regexp@3.3.0(eslint@10.10.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.8 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) jsdoc-type-pratt-parser: 9.2.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@14.0.0(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-simple-import-sort@14.0.0(eslint@10.10.0(jiti@2.7.0)): dependencies: - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) - eslint-plugin-unicorn@74.0.0(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-unicorn@74.0.0(eslint@10.10.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@eslint/css-tree': 4.0.5 browserslist: 4.28.8 change-case: 5.4.4 @@ -9322,7 +9312,7 @@ snapshots: core-js-compat: 3.50.0 detect-indent: 7.0.2 entities: 8.0.0 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) find-up-simple: 1.0.1 globals: 17.12.0 indent-string: 5.0.0 @@ -9336,14 +9326,14 @@ snapshots: strip-indent: 4.1.1 yaml: 2.9.0 - eslint-plugin-yml@3.8.1(eslint@10.9.1(jiti@2.7.0)): + eslint-plugin-yml@3.8.1(eslint@10.10.0(jiti@2.7.0)): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.9.1(jiti@2.7.0) + eslint: 10.10.0(jiti@2.7.0) natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -9360,14 +9350,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.9.1(jiti@2.7.0): + eslint@10.10.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 - '@eslint/plugin-kit': 0.7.2 + '@eslint/plugin-kit': 0.7.3 '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -9382,7 +9372,7 @@ snapshots: esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 + file-entry-cache: 11.1.5 find-up: 5.0.0 glob-parent: 6.0.2 ignore: 5.3.2 @@ -9509,9 +9499,9 @@ snapshots: fflate@0.8.3: {} - file-entry-cache@8.0.0: + file-entry-cache@11.1.5: dependencies: - flat-cache: 4.0.1 + flat-cache: 6.1.23 file-uri-to-path@1.0.0: {} @@ -9524,10 +9514,11 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@4.0.1: + flat-cache@6.1.23: dependencies: + cacheable: 2.5.0 flatted: 3.4.4 - keyv: 4.5.4 + hookified: 1.15.1 flatted@3.4.4: {} @@ -9651,8 +9642,6 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 - globals@14.0.0: {} - globals@15.15.0: {} globals@17.12.0: {} @@ -9730,6 +9719,10 @@ snapshots: has-flag@4.0.0: {} + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + he@1.2.0: {} header-generator@2.1.88: @@ -9752,6 +9745,10 @@ snapshots: hookable@6.1.1: {} + hookified@1.15.1: {} + + hookified@2.2.0: {} + html-encoding-sniffer@6.0.0(@noble/hashes@2.3.0): dependencies: '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) @@ -9871,11 +9868,6 @@ snapshots: pino: 10.3.1 socks: 2.8.9 - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - import-in-the-middle@3.4.0: dependencies: cjs-module-lexer: 2.2.1 @@ -10038,10 +10030,6 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.3.1: - dependencies: - argparse: 2.0.1 - jsbn@0.1.1: {} jsdoc-type-pratt-parser@9.2.1: @@ -10082,8 +10070,6 @@ snapshots: dependencies: bignumber.js: 9.3.1 - json-buffer@3.0.1: {} - json-nd@1.0.0: {} json-schema-traverse@0.4.1: {} @@ -10129,10 +10115,6 @@ snapshots: jwa: 2.0.1 safe-buffer: '@nolyfill/safe-buffer@1.0.44' - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -10654,10 +10636,6 @@ snapshots: dependencies: brace-expansion: 5.0.9 - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.18 - minimatch@9.0.9: dependencies: brace-expansion: 2.1.4 @@ -10994,10 +10972,6 @@ snapshots: pangu@4.0.7: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-srcset@1.0.2: {} parse5-htmlparser2-tree-adapter@7.1.0: @@ -11152,6 +11126,10 @@ snapshots: punycode@2.3.1: {} + qified@0.10.1: + dependencies: + hookified: 2.2.0 + qs@6.14.2: dependencies: side-channel: '@nolyfill/side-channel@1.0.44' @@ -11313,8 +11291,6 @@ snapshots: resolve-alpn@1.2.1: {} - resolve-from@4.0.0: {} - resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -11608,8 +11584,6 @@ snapshots: strip-indent@4.1.1: {} - strip-json-comments@3.1.1: {} - striptags@3.2.0: {} super-regex@1.1.0: From d2cad8a9c34cd6d8345ddda79a3f91a0ea7bc28f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 01:00:10 +0800 Subject: [PATCH 0741/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 2 updates (#23223) * chore(deps-dev): bump the cloudflare group across 1 directory with 2 updates Bumps the cloudflare group with 2 updates in the / directory: [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/workers-types` from 5.20260901.1 to 5.20260904.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.127.1 to 4.129.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.129.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260904.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.129.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] * chore: replace cloudflare/vitest-pool-workers with cloudflare/vitest-plugin https://developers.cloudflare.com/changelog/post/2026-08-19-vitest-plugin/ * build: sync compatibility_flags --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: TonyRL --- package.json | 6 +- pnpm-lock.yaml | 178 +++++++++++++++++++-------------------- vitest.workers.config.ts | 6 +- wrangler-container.toml | 1 - wrangler.production.toml | 2 +- wrangler.toml | 2 +- 6 files changed, 97 insertions(+), 98 deletions(-) diff --git a/package.json b/package.json index 4769693d604f..32e5fb9adbb6 100644 --- a/package.json +++ b/package.json @@ -150,8 +150,8 @@ "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", - "@cloudflare/vitest-pool-workers": "0.22.0", - "@cloudflare/workers-types": "5.20260901.1", + "@cloudflare/vitest-plugin": "1.1.5", + "@cloudflare/workers-types": "5.20260907.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.81.0", "@stylistic/eslint-plugin": "5.10.0", @@ -205,7 +205,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.3", "vitest": "4.1.11", - "wrangler": "4.127.1", + "wrangler": "4.129.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07dbc157f252..7034c3fdae72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -299,12 +299,12 @@ importers: '@cloudflare/playwright': specifier: 1.3.6 version: 1.3.6 - '@cloudflare/vitest-pool-workers': - specifier: 0.22.0 - version: 0.22.0(@cloudflare/workers-types@5.20260901.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + '@cloudflare/vitest-plugin': + specifier: 1.1.5 + version: 1.1.5(@cloudflare/workers-types@5.20260907.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260901.1 - version: 5.20260901.1 + specifier: 5.20260907.1 + version: 5.20260907.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) @@ -465,8 +465,8 @@ importers: specifier: 4.1.11 version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: - specifier: 4.127.1 - version: 4.127.1(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.129.0 + version: 4.129.0(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -591,75 +591,75 @@ packages: workerd: optional: true - '@cloudflare/vitest-pool-workers@0.22.0': - resolution: {integrity: sha512-OJv/qikkOgxnKxJ5xrLS7zuOLZhc/6iziU+llqZm4tiQf2CJUYwlMuXN68VaWIQebORd1AUx4w6A0oy8XRbuaQ==} + '@cloudflare/vitest-plugin@1.1.5': + resolution: {integrity: sha512-bXO4ObOJllPTzUYbklEsFy+Kvy0E4MFvpzXmbe/UFQpEFHAxiUE9vl9glnyqfBwrFjNDxvQQbA8CNA9WuPpAIg==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260815.1': - resolution: {integrity: sha512-7PsLdcz6pT9EMd1EJGZEgMyYRfs0CHxGs62PS2L1w3s6+xGmQcRXKm/zoMftmqZF45JBa4MzFeownRKbRt/x5g==} + '@cloudflare/workerd-darwin-64@1.20260903.1': + resolution: {integrity: sha512-FG+4mGxAXhKiL/1temH42alevIkumtYXNidhTa//3yULpzux6APw5UNVocI/vCQ1yG1YOEZRfbVy8lyuipM9MQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-64@1.20260828.1': - resolution: {integrity: sha512-CVd+xPhqUESg8Xhq09TZx0wl4FSirfJGOzvbPz2yHhBIvmNHFFQkSN3rkd7wEwnhQQk37Xi0/aD6ykPLJbmGiQ==} + '@cloudflare/workerd-darwin-64@1.20260907.1': + resolution: {integrity: sha512-Xaum46kviN8xixl0axXm7mfLTf2GAG+baotnEIv8M5EbvyHxHagvS5Z8bU5B/WJlTWwwvGzItFfKZvga78o3ew==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260815.1': - resolution: {integrity: sha512-60wtg8ng7FVWeOg/UMbZ9Ye0sslpRRAKoftPbdtuH2volq676quxVr6Zm2EjVULH/JFZeCn72dbLlrnbh0Mpcw==} + '@cloudflare/workerd-darwin-arm64@1.20260903.1': + resolution: {integrity: sha512-o241VefnjG8eG+kGap5CjgV4zOT5UaAmS3OR1VZCpNj7vkXGxvp9KftKvtQgcCsIqJKaKx+7Xd7xq0L1DjkfPQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260828.1': - resolution: {integrity: sha512-5HDPXRM152vU5JveByGFk34X57TVyIsfp4cabepAf45DC0MKvm52ucJqAjW1h8bvW4X+zRw9GU35OHF9FEC9Ww==} + '@cloudflare/workerd-darwin-arm64@1.20260907.1': + resolution: {integrity: sha512-P2o7xpZbxvoES9xY5uHqJPXvsWBWi0w1STCGoYLqxI1rh1gSUdlr4HruMiWq/TA94PPoQCg0eUfoshyQxMdGpA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260815.1': - resolution: {integrity: sha512-MuqKIHPo0Qyo8MZMmy0lP2B5PeAL7f4T9Fu4Usk3QdbV4JIrKG/OoybN3Ign7m/Dff+L1Oo/ZHydB+hEg1ueFw==} + '@cloudflare/workerd-linux-64@1.20260903.1': + resolution: {integrity: sha512-/VEvvtQ/XKf6HlBbg6FbvpwcpfYUcx6Fv6RkASY8DyEmUyuJ8rc7Qxil83ClRFoBzz/GY0BV94UZ6F+wers/hg==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-64@1.20260828.1': - resolution: {integrity: sha512-MQ1Ll9P7F72HHUKizbb7BlDfbY8fRoNMpbIpZoU6uKsSkneFICWSKv6UlgU9EQZ+w0i7TMa12iUgJ8l29eRI9A==} + '@cloudflare/workerd-linux-64@1.20260907.1': + resolution: {integrity: sha512-4aXjFMtD76FgVmDm7vUtl96LWNmFScfrYZ9lA6JpP5+svRfUQOYaJKcYK4g9IVmAxemItJizTTIA8jhEZaw0vg==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260815.1': - resolution: {integrity: sha512-XNFtJ5rIqJxnY6ISjkfbhT/ODiWJ6LcBvNbntuPD6I/F2k7aZeKgPaXrvWvKde66LXyzFKzc8Hn+Ydx4shevQg==} + '@cloudflare/workerd-linux-arm64@1.20260903.1': + resolution: {integrity: sha512-OWhihGC6KoTXF4u2C1AonfpgXeM4/7p/1IXuALqXESmFUpLLP5gZhRzjSk/gWW+mrCZDfSrvnjifl+lRqselbA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260828.1': - resolution: {integrity: sha512-FBTaUQ1xcU9jcp4OyBPcH8x0QiFvc1iuZL2GkD8zp2q1WyTVHYOptRDQUU+cuHjt0rQ2EIKVPBjahPxfa0joBw==} + '@cloudflare/workerd-linux-arm64@1.20260907.1': + resolution: {integrity: sha512-nex+emDfyOYnQlhfPoCfLYQN6L+Ng0fVg5f3+8Ot4XRZaKJUzpiBFiRkgpoUuEN47U8Pm8BwhhOK2dQwtKwivQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260815.1': - resolution: {integrity: sha512-PiIUWrhbMg3quolwjgMvPOd75vKESjT4aDm7nL6mSjL5IOgmpO/zKstXnYfnEH3pq7sC0UCvKlF8ZPcfsh8NMw==} + '@cloudflare/workerd-windows-64@1.20260903.1': + resolution: {integrity: sha512-soPMF9/aMHlHKK7M0vq5HrRRicPbnZO1F6ZZ7JWN8EltxWJU5L7CEq7CxjO878DzyPx7gYvqBFy3SVgdZKZjMQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workerd-windows-64@1.20260828.1': - resolution: {integrity: sha512-yvr77hC7dUbvK5K+SCg062kkPq3sx+drV1PcgHslzHDYcJBtT0V3X80qLE49LW1vq2svaeNmsVQS+vHsqWu8cQ==} + '@cloudflare/workerd-windows-64@1.20260907.1': + resolution: {integrity: sha512-/ZHtEmpdSUKZyQCBUSqmGp+I7GwWVR2eUaGE5NzFJIu31HBsoQEakXaDpTrbwXydpBzpI9N0DC83W7qekkGPMQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260901.1': - resolution: {integrity: sha512-m1rNbR3UYC1pgaEyXkSlwLFLDB11QziYUlY0z/nqjwuYtg5dw9zBrgDudoSfYM6ebbPDKU81ogBQAzLp6h1y4w==} + '@cloudflare/workers-types@5.20260907.1': + resolution: {integrity: sha512-HYI7eFb/MOcNzvbUW7ZwHVZL5YFXb4r6yHN3vx8gJCqde7yhn3bhuPKiQPw90UtGWWp7uOI+Que3kf8Svjn5cw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4985,12 +4985,12 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@5.20260815.0-alpha: - resolution: {integrity: sha512-YAaGj4Sh5f4fqHKiMQ8zRHDOOM5IGUVtMhnLIeyjuQfU+9P6hcOTrHUVtbfj/ZPay9Kzik4pWELB39pGgefjiQ==} + miniflare@5.20260903.0-alpha: + resolution: {integrity: sha512-VCZIFxOqFXeibRBJWwTlppqbv2lkeO10IX3QBRGK9j1QiMAfH/OSsCvbjp1MslBMhVZmy2VTRlVaVnsKnbDp6A==} engines: {node: '>=22.0.0'} - miniflare@5.20260828.0-alpha: - resolution: {integrity: sha512-6nbxhZEcz/UET3Y1OnYPsrAUjUmuFoib3ynUqteRdn1YnDxsLg8cwgZJZCk9QmtOmGzXwzXzgE/d/C0dJAPtVw==} + miniflare@5.20260907.0-alpha: + resolution: {integrity: sha512-56F13BeJQuDbTxcoGGN4MGxN8oNNIH3pK48VyJ1JGmjbzE9MMu6S2kxoON3nOBHlLdNjXyzg1Rz3jdHx+JpuZA==} engines: {node: '>=22.0.0'} minimatch@10.2.6: @@ -6328,32 +6328,32 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260815.1: - resolution: {integrity: sha512-8bArFkHmlp7qFEKVPyNzDzHzS35gc2fg0PYBcDtaNLF7UCDryCX2BQnpkUkTHYIy824IRrHOTwOEoTj0sUO2Fg==} + workerd@1.20260903.1: + resolution: {integrity: sha512-xJzt2RnCy7ulOULmZy/4JLbEPg1uisp9lVoOUsEz+UVhDsTmrSQ0rBXZMGcXuhr2HCGKs89bg8nnbbzvBSX1Ig==} engines: {node: '>=16'} hasBin: true - workerd@1.20260828.1: - resolution: {integrity: sha512-pB9yvt0kkwZDAGZHmpY59r0o3hM0DzdW6BJERqwZOhunZ3ssOyDSgQxOQer2cSZW4YCFeOTIQYN1qwhK5wv/Cw==} + workerd@1.20260907.1: + resolution: {integrity: sha512-MN/jgZkg2y9ZOOXdN9ecYynRFaqTCRkmpt3jS6LQNPoSx8HxTgcG947SduxJEB38YisB7RJ3Gu/y7fpd7fZ5bA==} engines: {node: '>=16'} hasBin: true - wrangler@4.124.0: - resolution: {integrity: sha512-75euoZKjVTJYFy+Xhctt/5JlZL4M6A4xmovZsUlep+6GHcCm14n9VtdGzNIybOW2t8wuNxRj5iMUwjT5E7Ctog==} + wrangler@4.129.0: + resolution: {integrity: sha512-PGPvs9UPoFrwxT0VogpESSZGvZIctAuTK3wGsLLPHtHsSgS85kNdvtpa2d14UzG8gwLWD64XUGFPGG9tOXG9VQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260815.1 + '@cloudflare/workers-types': ^5.20260903.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true - wrangler@4.127.1: - resolution: {integrity: sha512-OzsiNgaI8i681L/+KnAKc+uEZ5D57xK5JuNvCOpRKICF4/5Q3Cu1oTGuUiT/f3GDUqQb3gzXNT0tfOHGMEtknw==} + wrangler@4.129.1: + resolution: {integrity: sha512-u1Q8dMAczlmj41K0xk5LkfcHHmbwq5xZRctlyrErVqpcoGsSIW07i74phv3B9xt6mTEO1dX3U5d9WVxR3GFRfA==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260828.1 + '@cloudflare/workers-types': ^5.20260907.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6632,64 +6632,64 @@ snapshots: '@cloudflare/playwright@1.3.6': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260815.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260815.1 + workerd: 1.20260903.1 - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260828.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260828.1 + workerd: 1.20260907.1 - '@cloudflare/vitest-pool-workers@0.22.0(@cloudflare/workers-types@5.20260901.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.5(@cloudflare/workers-types@5.20260907.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260815.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) - wrangler: 4.124.0(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.129.1(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260815.1': + '@cloudflare/workerd-darwin-64@1.20260903.1': optional: true - '@cloudflare/workerd-darwin-64@1.20260828.1': + '@cloudflare/workerd-darwin-64@1.20260907.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260815.1': + '@cloudflare/workerd-darwin-arm64@1.20260903.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260828.1': + '@cloudflare/workerd-darwin-arm64@1.20260907.1': optional: true - '@cloudflare/workerd-linux-64@1.20260815.1': + '@cloudflare/workerd-linux-64@1.20260903.1': optional: true - '@cloudflare/workerd-linux-64@1.20260828.1': + '@cloudflare/workerd-linux-64@1.20260907.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260815.1': + '@cloudflare/workerd-linux-arm64@1.20260903.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260828.1': + '@cloudflare/workerd-linux-arm64@1.20260907.1': optional: true - '@cloudflare/workerd-windows-64@1.20260815.1': + '@cloudflare/workerd-windows-64@1.20260903.1': optional: true - '@cloudflare/workerd-windows-64@1.20260828.1': + '@cloudflare/workerd-windows-64@1.20260907.1': optional: true - '@cloudflare/workers-types@5.20260901.1': {} + '@cloudflare/workers-types@5.20260907.1': {} '@colors/colors@1.6.0': {} @@ -10608,24 +10608,24 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260815.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260903.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.2 undici: 7.29.0 - workerd: 1.20260815.1 + workerd: 1.20260903.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: - bufferutil - utf-8-validate - miniflare@5.20260828.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.2 undici: 7.29.0 - workerd: 1.20260828.1 + workerd: 1.20260907.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -12082,51 +12082,51 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260815.1: + workerd@1.20260903.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260815.1 - '@cloudflare/workerd-darwin-arm64': 1.20260815.1 - '@cloudflare/workerd-linux-64': 1.20260815.1 - '@cloudflare/workerd-linux-arm64': 1.20260815.1 - '@cloudflare/workerd-windows-64': 1.20260815.1 + '@cloudflare/workerd-darwin-64': 1.20260903.1 + '@cloudflare/workerd-darwin-arm64': 1.20260903.1 + '@cloudflare/workerd-linux-64': 1.20260903.1 + '@cloudflare/workerd-linux-arm64': 1.20260903.1 + '@cloudflare/workerd-windows-64': 1.20260903.1 - workerd@1.20260828.1: + workerd@1.20260907.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260828.1 - '@cloudflare/workerd-darwin-arm64': 1.20260828.1 - '@cloudflare/workerd-linux-64': 1.20260828.1 - '@cloudflare/workerd-linux-arm64': 1.20260828.1 - '@cloudflare/workerd-windows-64': 1.20260828.1 + '@cloudflare/workerd-darwin-64': 1.20260907.1 + '@cloudflare/workerd-darwin-arm64': 1.20260907.1 + '@cloudflare/workerd-linux-64': 1.20260907.1 + '@cloudflare/workerd-linux-arm64': 1.20260907.1 + '@cloudflare/workerd-windows-64': 1.20260907.1 - wrangler@4.124.0(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.129.0(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260815.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260815.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260903.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260815.1 + workerd: 1.20260903.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260901.1 + '@cloudflare/workers-types': 5.20260907.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil - utf-8-validate - wrangler@4.127.1(@cloudflare/workers-types@5.20260901.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.129.1(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260828.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260828.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260828.1 + workerd: 1.20260907.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260901.1 + '@cloudflare/workers-types': 5.20260907.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index 08d312e0a188..e162ed9ad6b8 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; -import { cloudflareTest } from '@cloudflare/vitest-pool-workers'; +import { cloudflareTest } from '@cloudflare/vitest-plugin'; import tsconfigPaths from 'vite-tsconfig-paths'; import { defineConfig, type Plugin } from 'vitest/config'; @@ -55,8 +55,8 @@ export default defineConfig({ plugins: [ cloudflareTest({ miniflare: { - compatibilityDate: '2026-08-22', - compatibilityFlags: ['nodejs_compat'], + compatibilityDate: '2026-09-01', + compatibilityFlags: ['global_fetch_strictly_public'], kvNamespaces: ['CACHE'], modulesRules: [{ type: 'CompiledWasm', include: ['**/*.wasm'] }], }, diff --git a/wrangler-container.toml b/wrangler-container.toml index eb49552f9857..5fb624a12dfb 100644 --- a/wrangler-container.toml +++ b/wrangler-container.toml @@ -1,7 +1,6 @@ name = "rsshub-container" main = "dist-container/container.mjs" compatibility_date = "2026-09-01" -compatibility_flags = ["nodejs_compat"] [build] command = "pnpm run container-build" diff --git a/wrangler.production.toml b/wrangler.production.toml index 14502a5c4193..ae9b523d0258 100644 --- a/wrangler.production.toml +++ b/wrangler.production.toml @@ -2,7 +2,7 @@ name = "rsshub" main = "dist-worker/worker.mjs" compatibility_date = "2026-09-01" -compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"] +compatibility_flags = ["global_fetch_strictly_public"] find_additional_modules = true base_dir = "dist-worker/modules" diff --git a/wrangler.toml b/wrangler.toml index 7c2c9109fd1b..65eddb7dae13 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,7 +1,7 @@ name = "rsshub" main = "dist-worker/worker.mjs" compatibility_date = "2026-09-01" -compatibility_flags = ["nodejs_compat", "global_fetch_strictly_public"] +compatibility_flags = ["global_fetch_strictly_public"] find_additional_modules = true base_dir = "dist-worker/modules" From f752dd755918b12e91620ed3b324972411c607c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 03:57:20 +0800 Subject: [PATCH 0742/1190] chore(deps-dev): bump tsdown from 0.22.14 to 0.23.0 (#23231) * chore(deps-dev): bump tsdown from 0.22.14 to 0.23.0 Bumps [tsdown](https://github.com/rolldown/tsdown) from 0.22.14 to 0.23.0. - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](https://github.com/rolldown/tsdown/compare/v0.22.14...v0.23.0) --- updated-dependencies: - dependency-name: tsdown dependency-version: 0.23.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * build: replace esbuild w/ rolldown --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tony --- package.json | 4 +- pnpm-lock.yaml | 473 ++++++++++---------- scripts/workflow/build-playwright-worker.ts | 74 +-- tests/playwright-worker-build.test.ts | 8 +- 4 files changed, 286 insertions(+), 273 deletions(-) diff --git a/package.json b/package.json index 32e5fb9adbb6..34df7bc2f73a 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,6 @@ "@vitest/coverage-v8": "4.1.11", "discord-api-types": "0.38.55", "domhandler": "6.0.1", - "esbuild": "0.28.1", "eslint": "10.10.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.3.0", @@ -198,7 +197,8 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.22.14", + "rolldown": "1.2.7", + "tsdown": "0.23.0", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", "unified": "11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7034c3fdae72..be4b905d8b5c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -365,9 +365,6 @@ importers: domhandler: specifier: 6.0.1 version: 6.0.1 - esbuild: - specifier: 0.28.1 - version: 0.28.1 eslint: specifier: 10.10.0 version: 10.10.0(jiti@2.7.0) @@ -443,9 +440,12 @@ importers: remark-parse: specifier: 11.0.0 version: 11.0.0 + rolldown: + specifier: 1.2.7 + version: 1.2.7 tsdown: - specifier: 0.22.14 - version: 0.22.14(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1) + specifier: 0.23.0 + version: 0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -1952,9 +1952,6 @@ packages: cpu: [x64] os: [win32] - '@oxc-project/types@0.140.0': - resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} - '@oxc-project/types@0.148.0': resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} @@ -2396,97 +2393,98 @@ packages: '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rolldown/binding-android-arm64@1.2.0': - resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} + '@rolldown/binding-android-arm-eabi@1.2.7': + resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.7': + resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.2.0': - resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} + '@rolldown/binding-darwin-arm64@1.2.7': + resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.0': - resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} + '@rolldown/binding-darwin-x64@1.2.7': + resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.0': - resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} + '@rolldown/binding-freebsd-x64@1.2.7': + resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.0': - resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.0': - resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} + '@rolldown/binding-linux-arm64-gnu@1.2.7': + resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.2.0': - resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} + '@rolldown/binding-linux-arm64-musl@1.2.7': + resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.2.0': - resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.0': - resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + '@rolldown/binding-linux-s390x-gnu@1.2.7': + resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.0': - resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} + '@rolldown/binding-linux-x64-gnu@1.2.7': + resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.2.0': - resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} + '@rolldown/binding-linux-x64-musl@1.2.7': + resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.2.0': - resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} + '@rolldown/binding-openharmony-arm64@1.2.7': + resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.2.0': - resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.2.0': - resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} + '@rolldown/binding-win32-arm64-msvc@1.2.7': + resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.0': - resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} + '@rolldown/binding-win32-x64-msvc@1.2.7': + resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -3114,130 +3112,140 @@ packages: '@vitest/utils@4.1.11': resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - '@yuku-codegen/binding-darwin-arm64@0.7.4': - resolution: {integrity: sha512-fjkATm+fg4r6Ss8o82u3j33PfIqhSs4A0WEEw9kOwhMx/ui/RQ0ZAsCtF6e7UG2CWGOGXAJspLlfk2tr3rjjKQ==} + '@yuku-codegen/binding-android-arm64@0.9.3': + resolution: {integrity: sha512-viote6xAyL5cKLquV2X2wRfopSckH+msDYbaI8Hh8JAaogYs8MJZVRUbSrbsY29TaPrIFZwNRwQ8+YSxs0dkGw==} + cpu: [arm64] + os: [android] + + '@yuku-codegen/binding-darwin-arm64@0.9.3': + resolution: {integrity: sha512-y2PGOLyxc724EJ+Et/5PxGfutQuV1Z2J9cxHo6W1I5CR3nk0i03J4yPrnw6rNJOfrtlISzcc7q0RrSyfPndpIg==} cpu: [arm64] os: [darwin] - '@yuku-codegen/binding-darwin-x64@0.7.4': - resolution: {integrity: sha512-Hj0KvHpS1RJY/bgM3BADzmXXkKO3+bq3M8bMq4b0j0LsVi1SeWYpD/hJLJFwHeNMS+jO4vlZ343yjb4DEb9XXQ==} + '@yuku-codegen/binding-darwin-x64@0.9.3': + resolution: {integrity: sha512-xZ9UpXUOLmsrKVUp7MRXxWU3drNiilRC42OLpjWEhnOehIGVF1bZgzHcqRYJxVyU53RMrkRMsaxplkGd6Qo/ow==} cpu: [x64] os: [darwin] - '@yuku-codegen/binding-freebsd-x64@0.7.4': - resolution: {integrity: sha512-GLKBZvqVFvC1bvNPoPZRj0UxUaAZZKCzb8IPNyvRhXesOk+Af9UbhbVPwx9Do4o2AVf6R5FPzyRWWIihQdCp+A==} + '@yuku-codegen/binding-freebsd-x64@0.9.3': + resolution: {integrity: sha512-RGCYSZw3VonreVTpur9iOfnbMngR2/f7UOE7gwcDx5WoHjIhtmTK9EIq9qs78ARdMFAPzKp5OzHljl5QMaGJ7g==} cpu: [x64] os: [freebsd] - '@yuku-codegen/binding-linux-arm-gnu@0.7.4': - resolution: {integrity: sha512-CAjbJexBJUqHgIPgOCJO7EYRnFluNLt5VD3jNS40wmsNZqa04HbewVVcgfmzfzuBhjX6ookntLDG3lWieyTAVw==} + '@yuku-codegen/binding-linux-arm-gnu@0.9.3': + resolution: {integrity: sha512-kjIJIw39GSPTF0hnq+jnM0tR+J5WlariAAWetxMtawNskITDT1ZigaK3YF5hMhDz2mAofaM1t+63qtx+7aXjeg==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm-musl@0.7.4': - resolution: {integrity: sha512-HQMuKBltnKFUqhUh8wNiivd76coRwDngdCxbcAULlatAlc2OXo8L6jLTkVnNeUuOw9JYzSq9LY2/7zvrg63Ddg==} + '@yuku-codegen/binding-linux-arm-musl@0.9.3': + resolution: {integrity: sha512-TVHeVdzaS4ub86URrQufiy4t01ZtdyKDZ5sTPqWFKAUbQJ7rQ0o5vIT+/jCeHQbP+Yf9p5peRdmPbcZewoDNiA==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-arm64-gnu@0.7.4': - resolution: {integrity: sha512-cnU7ZVxK/Oq/TIS2iq56rouy1dqnLYgWR2puWcrxGCtmbdxiMISANYhI5t2tMLzTGZMyhawM2zYlyI+gnpBupQ==} + '@yuku-codegen/binding-linux-arm64-gnu@0.9.3': + resolution: {integrity: sha512-9aQeeLh1qaCa2GcyzHnwLKGfFrsI+KOyhnh4+fICSq5kyhzCWQfXVCCK4RTEZPLcyuVwfN5Id0JEYavRN4oNTQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-arm64-musl@0.7.4': - resolution: {integrity: sha512-NyuabTumcxPtZv/Q+pMVvrcKxLn1SPcpdBGtekVJz7JwI36SuExTq4IG4EZsk7YDmFKP8wDEvmKYOpV/a8Lwhw==} + '@yuku-codegen/binding-linux-arm64-musl@0.9.3': + resolution: {integrity: sha512-TLXA5Hd1nr8VSP2MtxcFddsBIHj+DPpyG5eberEGMATndgG7STlKQmle51MV0MZ8UgsdYM6CybIVpzlCPQwP9w==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-codegen/binding-linux-x64-gnu@0.7.4': - resolution: {integrity: sha512-w3EnCLPD2vpJw0F+0qVV/1KSOAw4SgzjbGUbbwXUh9w5Kxo1Hdc3mN+/Nvk50oA6cCbSOCEaILnPHXPCauArvg==} + '@yuku-codegen/binding-linux-x64-gnu@0.9.3': + resolution: {integrity: sha512-tk0BFbF3Clb9k9biPH3qmr+Qwk24rRM26+HY91hYXmZlzlepZG0scQ6OffZFWtzwKE5JjlZpMdcWj1lTiHbEjA==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-codegen/binding-linux-x64-musl@0.7.4': - resolution: {integrity: sha512-FXKQlFjDM8FxqJ/TncAni7e7JyaXIB3Hv6boApxSs5ZUlvqP4TbrjYVk3mYDQt6xQAE/kEHplxM6m5SKx5sfRg==} + '@yuku-codegen/binding-linux-x64-musl@0.9.3': + resolution: {integrity: sha512-xo+pXshsCXruEEkDMbwHqkeTyC4XHb6A6oXr6x2YK3jOMcS5kZz0e26BmTCr40J0nY/K3ysmwG9R1xHygtiuwQ==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-codegen/binding-win32-arm64@0.7.4': - resolution: {integrity: sha512-rWr899KEvZIWMx9yUXQl4i90OGIs4gaw4X1UsS2rxsI3qnp8acLIVKI3N5WDqaertkW10crDRZvIxxyw7kTMTQ==} + '@yuku-codegen/binding-win32-arm64@0.9.3': + resolution: {integrity: sha512-70gAQo6HZzMgIJTjeMZOEO2XaRj4GcNGP/n81R9tXQv0x8d4ZHnZDv+ygeWfHo+xchAUPwpnrVZA4p6RhJa3GQ==} cpu: [arm64] os: [win32] - '@yuku-codegen/binding-win32-x64@0.7.4': - resolution: {integrity: sha512-3Olgmkd5rDrIN9g7wJFMRRC9jub5zAwiQOtwOVhvNe/nZE/rSufFRa7vrFupqSCQml+Zxbcy9npzo3Qne3rA2A==} + '@yuku-codegen/binding-win32-x64@0.9.3': + resolution: {integrity: sha512-7Hz3NGlq6qBBR8zMEk6GMZivofNjnYZpMXSoUwUgz9Ur2LaivuP23HQh0ern+T6HVkTJEvilw4VJrg1rX1Ugcg==} cpu: [x64] os: [win32] - '@yuku-parser/binding-darwin-arm64@0.7.4': - resolution: {integrity: sha512-rUetRGukIlPOkDCy+Fo0YTee66n9A04XRQMONptbemWSU27CCV6RDj56+4ne4eSE4gJ0139RWUfbqMtt744pWQ==} + '@yuku-parser/binding-android-arm64@0.9.3': + resolution: {integrity: sha512-z3tDGTaUXD5Q4IuebFOY/QHxhR/SqujMhkMv9C5bh25upyFEXdafp0MsXQIIheWhVZzn5VMOniyxFni/7s9LgQ==} + cpu: [arm64] + os: [android] + + '@yuku-parser/binding-darwin-arm64@0.9.3': + resolution: {integrity: sha512-hzKvKSKS7z3ufnu1VkQYEoxmS6A5uNnkwukKnc2atxpWdq648nLVOqut4h1jUXjbM2WcoTfuZLF6AHAGFf8cmg==} cpu: [arm64] os: [darwin] - '@yuku-parser/binding-darwin-x64@0.7.4': - resolution: {integrity: sha512-9bHrGQUot2vWTg0YTdyIBHGd38fy2BSQH1WaqUDVuNqSn7HffrTUzWOgbaWRNd5GTOvDdrt9SDZIG7nfqzeBtg==} + '@yuku-parser/binding-darwin-x64@0.9.3': + resolution: {integrity: sha512-OM2PiVlPATvzlj/KGHNlu+t8FC3YzM5joVNjhsz/EaigQ8x2UUQ1Q0agQLiv5GZ2L8TSzrLUwBCZ7YOvP8q+tw==} cpu: [x64] os: [darwin] - '@yuku-parser/binding-freebsd-x64@0.7.4': - resolution: {integrity: sha512-159565OJ/LR6cCP781nH8DxB5GqlqqWk3uyOLZIznQhsj9zeht4X7+jz9IQH1l/TUio+ojEXf8yt2+DJrN+QVw==} + '@yuku-parser/binding-freebsd-x64@0.9.3': + resolution: {integrity: sha512-WMn9M4LHNVysGNwsAJ9gpRPqQIW2lcPrDNGcyk0agx3IYqCJq1MQugh6Be8Otc5U08eGdE39o8Kx9YWJmXz7GA==} cpu: [x64] os: [freebsd] - '@yuku-parser/binding-linux-arm-gnu@0.7.4': - resolution: {integrity: sha512-j3s3LJxEbOyP58hmzuXpJlKzgxaswuOTu8nAbDpE119b5W3gP1SW+HndLscGUOACpyMdH5WJ6gBHRxq0HCRVBw==} + '@yuku-parser/binding-linux-arm-gnu@0.9.3': + resolution: {integrity: sha512-vqKjwiyW1FWvbykYMEQIcAJwA17WxK3rxxqDuyCvQwcNVaLEUxI4BXiUdlF3kHucc7MnoFQhoRPkySgOzlLM+Q==} cpu: [arm] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm-musl@0.7.4': - resolution: {integrity: sha512-PUpHPmvWIDxhYTS5oah08RQ28t6dlFBxRPJcftS6HgquiPsm/e0gL5vKwPJpyjaPBHzRH61IAUDDfrRe8iFs8g==} + '@yuku-parser/binding-linux-arm-musl@0.9.3': + resolution: {integrity: sha512-qohilhYOT+zkt2gYzym4F1T6BzRdvPqS9/sFB03pmnVV8LrvjOXVsGwEBAa3CEvzJKhAZjdTmVz7zsVdjyHWLg==} cpu: [arm] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-arm64-gnu@0.7.4': - resolution: {integrity: sha512-Hi3w0et5mu3i7S+qE0UgOev4RqJ/U9DW8xn79t4nttiICYCx3NveC0Goo78uqzxttsDI3hfRY43lrrF26svqcw==} + '@yuku-parser/binding-linux-arm64-gnu@0.9.3': + resolution: {integrity: sha512-tvTIyUvTGkeee68i4JIo9o27As+Ug6LXOZvElGgFbTs6+KBdb5LGhvugaIQljdfIsm2XnIbOLG6OnQSXHMLB9w==} cpu: [arm64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-arm64-musl@0.7.4': - resolution: {integrity: sha512-i073u4ENL9DJeWBRKioRCz8i5hg6EmUcH89eH1lts9f9AFPA1GphODwK6OXOXUbpi2AwZ1deFIUWgLGP2mdmmA==} + '@yuku-parser/binding-linux-arm64-musl@0.9.3': + resolution: {integrity: sha512-OWZBHW1wuChBpFlrF+On1yiBcFPMRrj2g0VKsM/PCBGffu1OM0ORkS+vLS3Snu6wYwndM5Dd9Ctvux6eUlrQjA==} cpu: [arm64] os: [linux] libc: [musl] - '@yuku-parser/binding-linux-x64-gnu@0.7.4': - resolution: {integrity: sha512-j5pt0LyDGxCzfoqRTR3cmMG21+bgSxIufAzJXFOWXkbg/22Ih51eGEsbInnSD5Q9FvJ3ooIn/jfok0dBdhudnA==} + '@yuku-parser/binding-linux-x64-gnu@0.9.3': + resolution: {integrity: sha512-/tbk1h0dlADOCngbiQO9V3SHwBIJkoGBq8BDEraSw2CC3nGxPEPTCCYUDp5738Ij2FxVwy1FWVuhFkHvpMrPbQ==} cpu: [x64] os: [linux] libc: [glibc] - '@yuku-parser/binding-linux-x64-musl@0.7.4': - resolution: {integrity: sha512-RR1hNhrSpv3FanLM6u5uD+9CYA9IM8U3uGscgvKKV77gtj7ttO4UubpwN7vcx1KknoEIQHKJFPVKoeVy+avf6w==} + '@yuku-parser/binding-linux-x64-musl@0.9.3': + resolution: {integrity: sha512-u3+0sCso/mcjDvtc3866D2giV7l34PDyDVBkMesAWa3DWbIWPlybehi2SSnmScgArV22ctqSSgUzdBBVJ6zYAg==} cpu: [x64] os: [linux] libc: [musl] - '@yuku-parser/binding-win32-arm64@0.7.4': - resolution: {integrity: sha512-1xhYwOLo9TjppHHwNYi3X/dGgOvnj9xh62jpgP4U8nEtTGA70NtJDCkN45pRQdU3B6/U7oQj1T4esP3aFJg6BA==} + '@yuku-parser/binding-win32-arm64@0.9.3': + resolution: {integrity: sha512-xnGEvdhyjRkXozHtpXVpEEkyGZWAxJ3RoILv7XRV5SvTEztaTCk5GQyfcOzI9An/EJtcL4ERCI8QmS0TpDPJiA==} cpu: [arm64] os: [win32] - '@yuku-parser/binding-win32-x64@0.7.4': - resolution: {integrity: sha512-HonZAapmSKusxLZPnU9WrMzAUdPSBJliGw3CSkA9Er/aq15STEOEy9SOsVGvj4maBv95EmWxt2LThHXnFnGfNg==} + '@yuku-parser/binding-win32-x64@0.9.3': + resolution: {integrity: sha512-N5eShGcuwnrXEprePFmEjtng6acZmtnC4zgazK9xxkavcx1q1uX8Nz8lU5RS973EMlNr902cIc6Cd2D4tqZDBg==} cpu: [x64] os: [win32] - '@yuku-toolchain/types@0.7.4': - resolution: {integrity: sha512-iUFXr+UnUJjzVLNI6GIv07poi9NwcG5hTBJSheJh3SdpkYpIjCl9kAGe7dbJMHn5sXeceCL4H12pKa0b6pkouQ==} + '@yuku-toolchain/types@0.9.3': + resolution: {integrity: sha512-rFE+5P4g2wxko5C85MugJOlVjBHEQq87dIkhLkniLXLp63PEtgaFjD954i5HXlfnyzLxPcZHsSOVDVgmo1HToA==} '@zone-eu/mailsplit@5.4.16': resolution: {integrity: sha512-zQ9iXvlT3Wi/hazeC1MdI4rQc1UJwJ6IQ6QzSZ5KDxLZZWQSazWLOzImLFluXadKShJ9WJvI1xH+AyVS8b9azg==} @@ -3300,10 +3308,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - ansis@4.3.1: - resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} - engines: {node: '>=14'} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -4195,10 +4199,6 @@ packages: get-tsconfig@4.14.1: resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==} - get-tsconfig@5.0.0-beta.5: - resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} - engines: {node: '>=20.20.0'} - get-tsconfig@5.0.0-beta.6: resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==} engines: {node: '>=20.20.0'} @@ -5565,13 +5565,13 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rolldown-plugin-dts@0.27.13: - resolution: {integrity: sha512-DeVZJbbB0ajp5q6vABqC8ZCJzxftlxbiV60Bk96GFdQaysGVpgTTVjQu0lUt4Lb+aRCtejfOixtQKDRol7IuVQ==} - engines: {node: ^22.18.0 || >=24.11.0} + rolldown-plugin-dts@0.28.5: + resolution: {integrity: sha512-yYd3C9CeJwqjOc9X23m0Tyxcqic491uLZlfg51szT287S8zCCqLR2uoySoElgqy2CLn7PdXcEo1dlkBs4n1WHg==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} peerDependencies: '@typescript/native-preview': '*' '@volar/typescript': ~2.4.0 - rolldown: ^1.0.0 + rolldown: ^1.2.0 typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: @@ -5584,8 +5584,8 @@ packages: vue-tsc: optional: true - rolldown@1.2.0: - resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} + rolldown@1.2.7: + resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -5838,14 +5838,14 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} - engines: {node: '>=18'} - tinyexec@1.3.0: resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -5929,19 +5929,19 @@ packages: ts-xor@1.3.0: resolution: {integrity: sha512-RLXVjliCzc1gfKQFLRpfeD0rrWmjnSTgj7+RFhoq3KRkUYa8LE/TIidYOzM5h+IdFBDSjjSgk9Lto9sdMfDFEA==} - tsdown@0.22.14: - resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} - engines: {node: ^22.18.0 || >=24.11.0} + tsdown@0.23.0: + resolution: {integrity: sha512-BaT+ep1xnj5hdyICLd5r5SYYE+TXnI1ATePLykv9vcKpHpFTWUWRH+x1AF/E2qcu3YB6+vI8IdyxIIIffEqw5Q==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.22.14 - '@tsdown/exe': 0.22.14 + '@tsdown/css': 0.23.0 + '@tsdown/exe': 0.23.0 '@vitejs/devtools': '*' publint: ^0.3.8 tsx: '*' typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 - unplugin-unused: ^0.5.0 + unplugin-unused: '>=0.5.0' unrun: '*' peerDependenciesMeta: '@arethetypeswrong/core': @@ -6158,8 +6158,8 @@ packages: resolution: {integrity: sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg==} engines: {node: '>=0.10.0'} - verkit@0.3.0: - resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==} + verkit@0.4.0: + resolution: {integrity: sha512-sXMwN6DMHeouPfCxkxWkKAmxphWKEenHYY5H1nIBzU3PmDsmJp6kBXJdshjVdpMZuWCmL9SH7KFRx29AylpP6g==} engines: {node: '>=18.12.0'} verror@1.10.0: @@ -6475,14 +6475,14 @@ packages: youtubei.js@18.0.0: resolution: {integrity: sha512-7Tztl5QzNL4nIhiAxa4P/0C8cDDsUe0j4iaUeNiaA+ZfSe/l+HkHxXyoWlQXNW7IPi2n6AW23ysVMJLDh+AwWQ==} - yuku-ast@0.7.4: - resolution: {integrity: sha512-Pn6e7uZOBczeJ+JIiPGtD4aw6eRflzKrZJmAdeg092RxM9tQtvAkZAbEoknNgYmsB6dGYESvXPQcUE7Nu8ai7Q==} + yuku-ast@0.9.3: + resolution: {integrity: sha512-Kt0PXlPCXdKF1fWFTl7N3DaxsVk6DHF8VAXHJMkwPtJnWYgbx20pYgGSweuC/86mIM7k1NUITQ4JffgOLUWTCw==} - yuku-codegen@0.7.4: - resolution: {integrity: sha512-bLdC5yzvn507PtU7+kB4CMBLfnvKW1N2m26Vpl1q4Os+FLROnkKTThM7g+clVb+9tHaOlcc6gqQ+rNBY8L/Dxw==} + yuku-codegen@0.9.3: + resolution: {integrity: sha512-7oTWwetHiMSyrVPFb8089lBBqkU1gaeQZyumNBJ0t5hocMQRaWhnMeSXTz7J1xm/rcw9+ePsajORdZbub2C35w==} - yuku-parser@0.7.4: - resolution: {integrity: sha512-HveMyhZPQQfR4z3xskXv5hHJW9g0KIESZW6JvUZv7Jn52FfMFUhCYL77KHBtnWGFti0KrKeTHMZVTY5AUVgFAA==} + yuku-parser@0.9.3: + resolution: {integrity: sha512-96wPoHnwaXfkZv7UIOUkDb+s7ZH8lv8Qtg+MxdvHUV1LFa5jV9iKOaams1942YQt+krFQrWiD6lSg2ermv5kHA==} zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -7625,8 +7625,6 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.148.0': optional: true - '@oxc-project/types@0.140.0': {} - '@oxc-project/types@0.148.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': @@ -7882,53 +7880,49 @@ snapshots: dependencies: quansync: 1.0.0 - '@rolldown/binding-android-arm64@1.2.0': + '@rolldown/binding-android-arm-eabi@1.2.7': optional: true - '@rolldown/binding-darwin-arm64@1.2.0': + '@rolldown/binding-android-arm64@1.2.7': optional: true - '@rolldown/binding-darwin-x64@1.2.0': + '@rolldown/binding-darwin-arm64@1.2.7': optional: true - '@rolldown/binding-freebsd-x64@1.2.0': + '@rolldown/binding-darwin-x64@1.2.7': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + '@rolldown/binding-freebsd-x64@1.2.7': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.0': + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.0': + '@rolldown/binding-linux-arm64-gnu@1.2.7': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.0': + '@rolldown/binding-linux-arm64-musl@1.2.7': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.0': + '@rolldown/binding-linux-ppc64-gnu@1.2.7': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.0': + '@rolldown/binding-linux-s390x-gnu@1.2.7': optional: true - '@rolldown/binding-linux-x64-musl@1.2.0': + '@rolldown/binding-linux-x64-gnu@1.2.7': optional: true - '@rolldown/binding-openharmony-arm64@1.2.0': + '@rolldown/binding-linux-x64-musl@1.2.7': optional: true - '@rolldown/binding-wasm32-wasi@1.2.0': - dependencies: - '@emnapi/core': 1.11.2 - '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.2.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@rolldown/binding-openharmony-arm64@1.2.7': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.0': + '@rolldown/binding-win32-arm64-msvc@1.2.7': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.0': + '@rolldown/binding-win32-x64-msvc@1.2.7': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -7937,7 +7931,7 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.5 + picomatch: 4.0.7 optionalDependencies: rollup: 4.63.1 @@ -8510,73 +8504,79 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.1 - '@yuku-codegen/binding-darwin-arm64@0.7.4': + '@yuku-codegen/binding-android-arm64@0.9.3': + optional: true + + '@yuku-codegen/binding-darwin-arm64@0.9.3': optional: true - '@yuku-codegen/binding-darwin-x64@0.7.4': + '@yuku-codegen/binding-darwin-x64@0.9.3': optional: true - '@yuku-codegen/binding-freebsd-x64@0.7.4': + '@yuku-codegen/binding-freebsd-x64@0.9.3': optional: true - '@yuku-codegen/binding-linux-arm-gnu@0.7.4': + '@yuku-codegen/binding-linux-arm-gnu@0.9.3': optional: true - '@yuku-codegen/binding-linux-arm-musl@0.7.4': + '@yuku-codegen/binding-linux-arm-musl@0.9.3': optional: true - '@yuku-codegen/binding-linux-arm64-gnu@0.7.4': + '@yuku-codegen/binding-linux-arm64-gnu@0.9.3': optional: true - '@yuku-codegen/binding-linux-arm64-musl@0.7.4': + '@yuku-codegen/binding-linux-arm64-musl@0.9.3': optional: true - '@yuku-codegen/binding-linux-x64-gnu@0.7.4': + '@yuku-codegen/binding-linux-x64-gnu@0.9.3': optional: true - '@yuku-codegen/binding-linux-x64-musl@0.7.4': + '@yuku-codegen/binding-linux-x64-musl@0.9.3': optional: true - '@yuku-codegen/binding-win32-arm64@0.7.4': + '@yuku-codegen/binding-win32-arm64@0.9.3': optional: true - '@yuku-codegen/binding-win32-x64@0.7.4': + '@yuku-codegen/binding-win32-x64@0.9.3': optional: true - '@yuku-parser/binding-darwin-arm64@0.7.4': + '@yuku-parser/binding-android-arm64@0.9.3': optional: true - '@yuku-parser/binding-darwin-x64@0.7.4': + '@yuku-parser/binding-darwin-arm64@0.9.3': optional: true - '@yuku-parser/binding-freebsd-x64@0.7.4': + '@yuku-parser/binding-darwin-x64@0.9.3': optional: true - '@yuku-parser/binding-linux-arm-gnu@0.7.4': + '@yuku-parser/binding-freebsd-x64@0.9.3': optional: true - '@yuku-parser/binding-linux-arm-musl@0.7.4': + '@yuku-parser/binding-linux-arm-gnu@0.9.3': optional: true - '@yuku-parser/binding-linux-arm64-gnu@0.7.4': + '@yuku-parser/binding-linux-arm-musl@0.9.3': optional: true - '@yuku-parser/binding-linux-arm64-musl@0.7.4': + '@yuku-parser/binding-linux-arm64-gnu@0.9.3': optional: true - '@yuku-parser/binding-linux-x64-gnu@0.7.4': + '@yuku-parser/binding-linux-arm64-musl@0.9.3': optional: true - '@yuku-parser/binding-linux-x64-musl@0.7.4': + '@yuku-parser/binding-linux-x64-gnu@0.9.3': optional: true - '@yuku-parser/binding-win32-arm64@0.7.4': + '@yuku-parser/binding-linux-x64-musl@0.9.3': optional: true - '@yuku-parser/binding-win32-x64@0.7.4': + '@yuku-parser/binding-win32-arm64@0.9.3': optional: true - '@yuku-toolchain/types@0.7.4': {} + '@yuku-parser/binding-win32-x64@0.9.3': + optional: true + + '@yuku-toolchain/types@0.9.3': {} '@zone-eu/mailsplit@5.4.16': dependencies: @@ -8625,8 +8625,6 @@ snapshots: ansi-styles@6.2.3: {} - ansis@4.3.1: {} - arg@5.0.2: {} argparse@3.0.0: {} @@ -9482,10 +9480,6 @@ snapshots: dependencies: fast-string-width: 3.0.2 - fdir@6.5.0(picomatch@4.0.5): - optionalDependencies: - picomatch: 4.0.5 - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -9603,10 +9597,6 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-tsconfig@5.0.0-beta.5: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@5.0.0-beta.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -11307,40 +11297,40 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.27.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0): + rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.7): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) - get-tsconfig: 5.0.0-beta.5 + get-tsconfig: 5.0.0-beta.6 obug: 2.1.4 - rolldown: 1.2.0 - yuku-ast: 0.7.4 - yuku-codegen: 0.7.4 - yuku-parser: 0.7.4 + rolldown: 1.2.7 + yuku-ast: 0.9.3 + yuku-codegen: 0.9.3 + yuku-parser: 0.9.3 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - oxc-resolver - rolldown@1.2.0: + rolldown@1.2.7: dependencies: - '@oxc-project/types': 0.140.0 + '@oxc-project/types': 0.148.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.2.0 - '@rolldown/binding-darwin-arm64': 1.2.0 - '@rolldown/binding-darwin-x64': 1.2.0 - '@rolldown/binding-freebsd-x64': 1.2.0 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.0 - '@rolldown/binding-linux-arm64-gnu': 1.2.0 - '@rolldown/binding-linux-arm64-musl': 1.2.0 - '@rolldown/binding-linux-ppc64-gnu': 1.2.0 - '@rolldown/binding-linux-s390x-gnu': 1.2.0 - '@rolldown/binding-linux-x64-gnu': 1.2.0 - '@rolldown/binding-linux-x64-musl': 1.2.0 - '@rolldown/binding-openharmony-arm64': 1.2.0 - '@rolldown/binding-wasm32-wasi': 1.2.0 - '@rolldown/binding-win32-arm64-msvc': 1.2.0 - '@rolldown/binding-win32-x64-msvc': 1.2.0 + '@rolldown/binding-android-arm-eabi': 1.2.7 + '@rolldown/binding-android-arm64': 1.2.7 + '@rolldown/binding-darwin-arm64': 1.2.7 + '@rolldown/binding-darwin-x64': 1.2.7 + '@rolldown/binding-freebsd-x64': 1.2.7 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.7 + '@rolldown/binding-linux-arm64-gnu': 1.2.7 + '@rolldown/binding-linux-arm64-musl': 1.2.7 + '@rolldown/binding-linux-ppc64-gnu': 1.2.7 + '@rolldown/binding-linux-s390x-gnu': 1.2.7 + '@rolldown/binding-linux-x64-gnu': 1.2.7 + '@rolldown/binding-linux-x64-musl': 1.2.7 + '@rolldown/binding-openharmony-arm64': 1.2.7 + '@rolldown/binding-win32-arm64-msvc': 1.2.7 + '@rolldown/binding-win32-x64-msvc': 1.2.7 rollup@4.63.1: dependencies: @@ -11642,14 +11632,14 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.2.4: {} - tinyexec@1.3.0: {} + tinyexec@1.3.1: {} + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 tinypool@2.1.0: {} @@ -11713,23 +11703,22 @@ snapshots: ts-xor@1.3.0: {} - tsdown@0.22.14(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1): + tsdown@0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1): dependencies: - ansis: 4.3.1 cac: 7.0.0 defu: 6.1.7 empathic: 2.0.1 hookable: 6.1.1 import-without-cache: 0.4.0 obug: 2.1.4 - picomatch: 4.0.5 - rolldown: 1.2.0 - rolldown-plugin-dts: 0.27.13(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.0) - tinyexec: 1.2.4 + picomatch: 4.0.7 + rolldown: 1.2.7 + rolldown-plugin-dts: 0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.7) + tinyexec: 1.3.1 tinyglobby: 0.2.17 tree-kill: 1.2.2 unconfig-core: 7.5.0 - verkit: 0.3.0 + verkit: 0.4.0 optionalDependencies: tsx: 4.23.13 typescript: '@typescript/typescript6@6.0.2' @@ -11878,7 +11867,7 @@ snapshots: unrun@0.3.1: dependencies: - rolldown: 1.2.0 + rolldown: 1.2.7 until-async@3.0.2: {} @@ -11927,7 +11916,7 @@ snapshots: vali-date@1.0.0: {} - verkit@0.3.0: {} + verkit@0.4.0: {} verror@1.10.0: dependencies: @@ -12248,42 +12237,44 @@ snapshots: fflate: 0.8.3 meriyah: 7.3.2 - yuku-ast@0.7.4: + yuku-ast@0.9.3: dependencies: - '@yuku-toolchain/types': 0.7.4 + '@yuku-toolchain/types': 0.9.3 - yuku-codegen@0.7.4: + yuku-codegen@0.9.3: dependencies: - '@yuku-toolchain/types': 0.7.4 + '@yuku-toolchain/types': 0.9.3 optionalDependencies: - '@yuku-codegen/binding-darwin-arm64': 0.7.4 - '@yuku-codegen/binding-darwin-x64': 0.7.4 - '@yuku-codegen/binding-freebsd-x64': 0.7.4 - '@yuku-codegen/binding-linux-arm-gnu': 0.7.4 - '@yuku-codegen/binding-linux-arm-musl': 0.7.4 - '@yuku-codegen/binding-linux-arm64-gnu': 0.7.4 - '@yuku-codegen/binding-linux-arm64-musl': 0.7.4 - '@yuku-codegen/binding-linux-x64-gnu': 0.7.4 - '@yuku-codegen/binding-linux-x64-musl': 0.7.4 - '@yuku-codegen/binding-win32-arm64': 0.7.4 - '@yuku-codegen/binding-win32-x64': 0.7.4 - - yuku-parser@0.7.4: - dependencies: - '@yuku-toolchain/types': 0.7.4 - yuku-ast: 0.7.4 + '@yuku-codegen/binding-android-arm64': 0.9.3 + '@yuku-codegen/binding-darwin-arm64': 0.9.3 + '@yuku-codegen/binding-darwin-x64': 0.9.3 + '@yuku-codegen/binding-freebsd-x64': 0.9.3 + '@yuku-codegen/binding-linux-arm-gnu': 0.9.3 + '@yuku-codegen/binding-linux-arm-musl': 0.9.3 + '@yuku-codegen/binding-linux-arm64-gnu': 0.9.3 + '@yuku-codegen/binding-linux-arm64-musl': 0.9.3 + '@yuku-codegen/binding-linux-x64-gnu': 0.9.3 + '@yuku-codegen/binding-linux-x64-musl': 0.9.3 + '@yuku-codegen/binding-win32-arm64': 0.9.3 + '@yuku-codegen/binding-win32-x64': 0.9.3 + + yuku-parser@0.9.3: + dependencies: + '@yuku-toolchain/types': 0.9.3 + yuku-ast: 0.9.3 optionalDependencies: - '@yuku-parser/binding-darwin-arm64': 0.7.4 - '@yuku-parser/binding-darwin-x64': 0.7.4 - '@yuku-parser/binding-freebsd-x64': 0.7.4 - '@yuku-parser/binding-linux-arm-gnu': 0.7.4 - '@yuku-parser/binding-linux-arm-musl': 0.7.4 - '@yuku-parser/binding-linux-arm64-gnu': 0.7.4 - '@yuku-parser/binding-linux-arm64-musl': 0.7.4 - '@yuku-parser/binding-linux-x64-gnu': 0.7.4 - '@yuku-parser/binding-linux-x64-musl': 0.7.4 - '@yuku-parser/binding-win32-arm64': 0.7.4 - '@yuku-parser/binding-win32-x64': 0.7.4 + '@yuku-parser/binding-android-arm64': 0.9.3 + '@yuku-parser/binding-darwin-arm64': 0.9.3 + '@yuku-parser/binding-darwin-x64': 0.9.3 + '@yuku-parser/binding-freebsd-x64': 0.9.3 + '@yuku-parser/binding-linux-arm-gnu': 0.9.3 + '@yuku-parser/binding-linux-arm-musl': 0.9.3 + '@yuku-parser/binding-linux-arm64-gnu': 0.9.3 + '@yuku-parser/binding-linux-arm64-musl': 0.9.3 + '@yuku-parser/binding-linux-x64-gnu': 0.9.3 + '@yuku-parser/binding-linux-x64-musl': 0.9.3 + '@yuku-parser/binding-win32-arm64': 0.9.3 + '@yuku-parser/binding-win32-x64': 0.9.3 zod@4.4.3: {} diff --git a/scripts/workflow/build-playwright-worker.ts b/scripts/workflow/build-playwright-worker.ts index 69e641530f52..47ddf293fd1f 100644 --- a/scripts/workflow/build-playwright-worker.ts +++ b/scripts/workflow/build-playwright-worker.ts @@ -3,7 +3,7 @@ import { builtinModules, createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -import { build } from 'esbuild'; +import { rolldown } from 'rolldown'; const repoRoot = fileURLToPath(new URL('../..', import.meta.url)); const packageRequire = createRequire(new URL('../../package.json', import.meta.url)); @@ -12,6 +12,8 @@ const corePath = patchrightRequire.resolve('patchright-core/lib/coreBundle'); const packageDir = path.dirname(path.dirname(corePath)); const builtinNames = new Set(builtinModules.map((name) => name.replace(/^node:/, ''))); +const workerEntryId = '\0virtual:playwright-worker-entry.mjs'; + type Metadata = Record<'package.json' | 'browsers.json', unknown>; export function inlinePackageMetadata(source: string, metadata: Metadata) { @@ -30,6 +32,16 @@ export function inlinePackageMetadata(source: string, metadata: Metadata) { } export function addBuiltinRequireMap(source: string) { + const importLine = 'import { createRequire } from "node:module";\n'; + if (source.split(importLine).length - 1 !== 1) { + throw new Error('Playwright Worker build expected exactly one createRequire import'); + } + source = source.replace(importLine, ''); + const helperPattern = /^var __require = .*createRequire\(import\.meta\.url\).*;$/gm; + if (source.matchAll(helperPattern).toArray().length !== 1) { + throw new Error('Playwright Worker build expected exactly one createRequire runtime helper'); + } + source = source.replace(helperPattern, 'var __require = (...args) => require(...args);'); const usedBuiltins = [ ...new Set( source @@ -62,50 +74,54 @@ export async function buildPlaywrightWorker(outputDir = path.join(repoRoot, 'ass 'package.json': packageMetadata, 'browsers.json': JSON.parse(browsersJson), }); - const result = await build({ - stdin: { - // Patchright initializes AbortController and other request-scoped state. - // Keep its first import inside the caller's Worker request handler. - contents: `let corePromise; -export function getPlaywrightCore() { - return corePromise ??= import('rsshub-playwright-core').then((module) => module.default ?? module); -}`, - loader: 'js', - resolveDir: repoRoot, - sourcefile: 'playwright-worker.mjs', - }, - bundle: true, - format: 'esm', + const bundle = await rolldown({ + input: workerEntryId, platform: 'node', - target: 'esnext', + treeshake: false, // Remote Chromium needs neither the local BiDi mapper nor macOS file watching. // A runtime call to an unbundled optional module fails through the require map. - external: ['chromium-bidi/*', 'fsevents'], - write: false, - define: { - __dirname: JSON.stringify('/bundle/lib'), - __filename: JSON.stringify('/bundle/lib/coreBundle.js'), + external: (id) => id === 'fsevents' || id.startsWith('chromium-bidi/'), + transform: { + define: { + __dirname: JSON.stringify('/bundle/lib'), + __filename: JSON.stringify('/bundle/lib/coreBundle.js'), + }, }, + logLevel: 'silent', plugins: [ { name: 'playwright-worker-metadata', - setup(builder) { - builder.onResolve({ filter: /^rsshub-playwright-core$/ }, () => ({ path: corePath })); - builder.onLoad({ filter: /\/lib\/coreBundle\.js$/ }, (args) => { - if (args.path === corePath) { - return { contents: source, loader: 'js', resolveDir: path.dirname(corePath) }; - } - }); + resolveId(id) { + if (id === workerEntryId || id === 'rsshub-playwright-core') { + return id === workerEntryId ? workerEntryId : corePath; + } + return null; + }, + load(id) { + if (id === workerEntryId) { + // Patchright initializes AbortController and other request-scoped state. + // Keep its first import inside the caller's Worker request handler. + return `let corePromise; +export function getPlaywrightCore() { + return corePromise ??= import('rsshub-playwright-core').then((module) => module.default ?? module); +}`; + } + if (id === corePath) { + return source; + } + return null; }, }, ], }); - const generated = addBuiltinRequireMap(result.outputFiles[0].text); + const { output } = await bundle.generate({ format: 'esm', codeSplitting: false }); + const generated = addBuiltinRequireMap(output[0].code); await mkdir(outputDir, { recursive: true }); await Promise.all([ writeFile(path.join(outputDir, 'playwright-worker.mjs'), generated.source), writeFile(path.join(outputDir, 'playwright-worker.json'), JSON.stringify({ version: packageMetadata.version, builtins: generated.usedBuiltins }, null, 2) + '\n'), ]); + await bundle.close(); return { version: packageMetadata.version, bytes: Buffer.byteLength(generated.source), builtins: generated.usedBuiltins }; } diff --git a/tests/playwright-worker-build.test.ts b/tests/playwright-worker-build.test.ts index 131dce73d1ed..f708fb48e0d7 100644 --- a/tests/playwright-worker-build.test.ts +++ b/tests/playwright-worker-build.test.ts @@ -20,10 +20,14 @@ afterAll(async () => { describe('Playwright Worker client build', () => { it('keeps the native module namespace out of the Worker shim dependency cycle', () => { - const result = addBuiltinRequireMap('require("node:module"); require("process");'); + const input = 'import { createRequire } from "node:module";\nvar __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();\nrequire("node:module"); require("process");'; + const result = addBuiltinRequireMap(input); + expect(result.source).not.toContain('createRequire'); + expect(result.source).toContain('var __require = (...args) => require(...args);'); expect(result.source).toContain('import * as builtin0 from "module";'); expect(result.source).not.toContain('from "node:module"'); expect(result.source).toContain('"node:module": (builtin0.default ?? builtin0)'); + expect(() => addBuiltinRequireMap('require("node:module");')).toThrow('exactly one createRequire import'); }); it('fails visibly when a dependency update changes or duplicates a metadata require', () => { @@ -60,6 +64,8 @@ describe('Playwright Worker client build', () => { const { stdout } = await exec(process.execPath, ['--input-type=module', '-e', script], { timeout: 20000 }); expect(stdout).toBe('lazy-client-ready'); expect(result.builtins).toContain('process'); + const bundleSource = await readFile(path.join(outputDir, 'playwright-worker.mjs'), 'utf8'); + expect(bundleSource).not.toContain('createRequire(import.meta.url)'); const manifest = JSON.parse(await readFile(path.join(outputDir, 'playwright-worker.json'), 'utf8')); expect(manifest.version).toBe(result.version); }, 30000); From b694e093e7bc59a4058afeaf91607f75c32d277c Mon Sep 17 00:00:00 2001 From: Ryan Chou <88779759+ryanchou1994@users.noreply.github.com> Date: Tue, 8 Sep 2026 06:13:09 +0800 Subject: [PATCH 0743/1190] fix(core): listen on unix domain socket when SOCKET is set (#23238) The deploy docs still describe SOCKET, but the hono migration dropped it from the config parser and lib/index.ts always calls serve() with a TCP port, so the variable had no effect and PORT=null on top crashed the process with ERR_SOCKET_BAD_PORT. Parse SOCKET into config.connect.socket again and, when it is set, create the server with createAdaptorServer and listen on the socket path instead of a port. A stale socket file left by an unclean shutdown is removed before binding; only the binding process does that (cluster primary before forking), workers share the primary's handle and never touch the file. Fixes #18738 --- lib/config.test.ts | 18 ++++++++++ lib/config.ts | 3 ++ lib/index.test.ts | 58 ++++++++++++++++++++++++++++++++ lib/index.ts | 82 +++++++++++++++++++++++++++------------------- 4 files changed, 128 insertions(+), 33 deletions(-) diff --git a/lib/config.test.ts b/lib/config.test.ts index 1f107bb1168c..4d89fc0564f8 100644 --- a/lib/config.test.ts +++ b/lib/config.test.ts @@ -22,6 +22,24 @@ describe('config', () => { delete process.env.BILIBILI_COOKIE_34; }); + it('unix socket', async () => { + process.env.SOCKET = '/tmp/rsshub.sock'; + + const { config } = await import('./config'); + expect(config.connect.socket).toBe('/tmp/rsshub.sock'); + + delete process.env.SOCKET; + }); + + it('unix socket defaults to undefined', async () => { + process.env.SOCKET = ''; + + const { config } = await import('./config'); + expect(config.connect.socket).toBeUndefined(); + + delete process.env.SOCKET; + }); + it('email config', async () => { process.env['EMAIL_CONFIG_xx.qq.com'] = 'token1'; process.env['EMAIL_CONFIG_oo.qq.com'] = 'token2'; diff --git a/lib/config.ts b/lib/config.ts index 73d88db5f2af..345a2800fdd8 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -14,6 +14,7 @@ type ConfigEnvKeys = | 'CHROMIUM_EXECUTABLE_PATH' // Network | 'PORT' + | 'SOCKET' | 'LISTEN_INADDR_ANY' | 'DISABLE_IPV6' | 'REQUEST_RETRY' @@ -270,6 +271,7 @@ export type Config = { // network connect: { port: number; + socket?: string; }; listenInaddrAny: boolean; disableIPv6: boolean; @@ -776,6 +778,7 @@ const calculateValue = () => { // network connect: { port: toInt(envs.PORT, 1200), // 监听端口 + socket: envs.SOCKET || undefined, // listen on a unix socket instead of a TCP port }, listenInaddrAny: toBoolean(envs.LISTEN_INADDR_ANY, true), // 是否允许公网连接,取值 0 1 disableIPv6: toBoolean(envs.DISABLE_IPV6, false), diff --git a/lib/index.test.ts b/lib/index.test.ts index 930df7feeede..2aebc4106964 100644 --- a/lib/index.test.ts +++ b/lib/index.test.ts @@ -1,6 +1,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; const serve = vi.fn<(...args: any[]) => any>(() => ({ close: vi.fn() })); +const listen = vi.fn(); +const createAdaptorServer = vi.fn<(...args: any[]) => any>(() => ({ listen, close: vi.fn() })); +const rmSync = vi.fn(); const logger = { info: vi.fn(), warn: vi.fn(), @@ -20,6 +23,13 @@ const availableParallelism = vi.fn(() => 2); vi.mock('@hono/node-server', () => ({ serve, + createAdaptorServer, +})); +vi.mock('node:fs', () => ({ + __esModule: true, + default: { + rmSync, + }, })); vi.mock('@/utils/logger', () => ({ default: logger, @@ -46,6 +56,9 @@ describe('index', () => { vi.resetModules(); vi.unstubAllEnvs(); serve.mockClear(); + createAdaptorServer.mockClear(); + listen.mockClear(); + rmSync.mockClear(); fork.mockClear(); availableParallelism.mockClear(); logger.info.mockClear(); @@ -64,6 +77,7 @@ describe('index', () => { hostname: '127.0.0.1', port: 12345, }); + expect(createAdaptorServer).not.toHaveBeenCalled(); }); it('forks workers when cluster is enabled and primary', async () => { @@ -93,4 +107,48 @@ describe('index', () => { port: 12347, }); }); + + it('listens on a unix socket instead of a port when SOCKET is set', async () => { + vi.stubEnv('ENABLE_CLUSTER', ''); + vi.stubEnv('LISTEN_INADDR_ANY', ''); + vi.stubEnv('SOCKET', '/tmp/rsshub-test.sock'); + vi.stubEnv('PORT', 'null'); + + const module = await import('@/index'); + expect(module.default).toBeDefined(); + expect(rmSync).toHaveBeenCalledWith('/tmp/rsshub-test.sock', { force: true }); + expect(createAdaptorServer).toHaveBeenCalledTimes(1); + expect(createAdaptorServer.mock.calls[0][0]).toMatchObject({ + hostname: '127.0.0.1', + }); + expect(listen).toHaveBeenCalledWith('/tmp/rsshub-test.sock'); + expect(serve).not.toHaveBeenCalled(); + }); + + it('cluster primary removes a stale unix socket before forking', async () => { + clusterState.isPrimary = true; + vi.stubEnv('ENABLE_CLUSTER', 'true'); + vi.stubEnv('SOCKET', '/tmp/rsshub-test.sock'); + availableParallelism.mockReturnValue(2); + + await import('@/index'); + + expect(rmSync).toHaveBeenCalledWith('/tmp/rsshub-test.sock', { force: true }); + expect(fork).toHaveBeenCalledTimes(2); + expect(createAdaptorServer).not.toHaveBeenCalled(); + expect(serve).not.toHaveBeenCalled(); + }); + + it('cluster worker listens on the unix socket without removing it', async () => { + clusterState.isPrimary = false; + vi.stubEnv('ENABLE_CLUSTER', 'true'); + vi.stubEnv('SOCKET', '/tmp/rsshub-test.sock'); + + await import('@/index'); + + expect(rmSync).not.toHaveBeenCalled(); + expect(createAdaptorServer).toHaveBeenCalledTimes(1); + expect(listen).toHaveBeenCalledWith('/tmp/rsshub-test.sock'); + expect(serve).not.toHaveBeenCalled(); + }); }); diff --git a/lib/index.ts b/lib/index.ts index d21bac888c48..efce51229607 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,8 +1,9 @@ import cluster from 'node:cluster'; +import fs from 'node:fs'; import os from 'node:os'; import process from 'node:process'; -import { serve } from '@hono/node-server'; +import { createAdaptorServer, serve } from '@hono/node-server'; import app from '@/app'; import { config } from '@/config'; @@ -10,20 +11,55 @@ import { getLocalhostAddress } from '@/utils/common-utils'; import logger from '@/utils/logger'; const port = config.connect.port; +const socketPath = config.connect.socket; const hostIPList = getLocalhostAddress(); +const serverOptions = { + fetch: app.fetch, + hostname: config.listenInaddrAny ? (config.disableIPv6 ? '0.0.0.0' : '::') : '127.0.0.1', + serverOptions: { + maxHeaderSize: 1024 * 32, + }, +}; + +const logListening = () => { + if (socketPath) { + logger.info(`🎉 RSSHub is running on unix socket ${socketPath}! Cheers!`); + return; + } + logger.info(`🎉 RSSHub is running on port ${port}! Cheers!`); + logger.info(`🔗 Local: 👉 http://localhost:${port}`); + if (config.listenInaddrAny) { + for (const ip of hostIPList) { + logger.info(`🔗 Network: 👉 http://${ip}:${port}`); + } + } +}; + +// A socket file left behind by an unclean shutdown makes listen() fail with EADDRINUSE. +// Only the process that binds the socket may remove it: a cluster worker must not touch +// the file because the primary already holds the listening handle. +const removeStaleSocket = () => { + if (socketPath) { + fs.rmSync(socketPath, { force: true }); + } +}; + +const listen = () => { + if (socketPath) { + const socketServer = createAdaptorServer(serverOptions); + socketServer.listen(socketPath); + return socketServer; + } + return serve({ ...serverOptions, port }); +}; + let server; if (config.enableCluster) { if (cluster.isPrimary) { - logger.info(`🎉 RSSHub is running on port ${port}! Cheers!`); - logger.info(`🔗 Local: 👉 http://localhost:${port}`); - if (config.listenInaddrAny) { - for (const ip of hostIPList) { - logger.info(`🔗 Network: 👉 http://${ip}:${port}`); - } - } - + logListening(); logger.info(`Primary ${process.pid} is running`); + removeStaleSocket(); const numCPUs = os.availableParallelism(); @@ -32,32 +68,12 @@ if (config.enableCluster) { } } else { logger.info(`Worker ${process.pid} is running`); - serve({ - fetch: app.fetch, - hostname: config.listenInaddrAny ? (config.disableIPv6 ? '0.0.0.0' : '::') : '127.0.0.1', - port, - serverOptions: { - maxHeaderSize: 1024 * 32, - }, - }); + listen(); } } else { - logger.info(`🎉 RSSHub is running on port ${port}! Cheers!`); - logger.info(`🔗 Local: 👉 http://localhost:${port}`); - if (config.listenInaddrAny) { - for (const ip of hostIPList) { - logger.info(`🔗 Network: 👉 http://${ip}:${port}`); - } - } - - server = serve({ - fetch: app.fetch, - hostname: config.listenInaddrAny ? (config.disableIPv6 ? '0.0.0.0' : '::') : '127.0.0.1', - port, - serverOptions: { - maxHeaderSize: 1024 * 32, - }, - }); + logListening(); + removeStaleSocket(); + server = listen(); } export default server; From c6eaf778b35af87213af3ca11cbe200776d85809 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Tue, 8 Sep 2026 10:45:23 +0800 Subject: [PATCH 0744/1190] fix(zhihu): use JSDOM outside Workers --- lib/routes/zhihu/activities.ts | 5 +- lib/routes/zhihu/answers.ts | 5 +- lib/routes/zhihu/jsdom.ts | 197 +++++++++++++++++++++++ lib/routes/zhihu/jsdom.worker.ts | 6 + lib/routes/zhihu/posts.ts | 5 +- lib/routes/zhihu/question.ts | 5 +- lib/routes/zhihu/topic.ts | 5 +- lib/routes/zhihu/utils.ts | 6 + lib/routes/zhihu/zhuanlan.ts | 5 +- package.json | 2 + pnpm-lock.yaml | 73 ++++----- tests/zhihu-browser.test.ts | 1 + tests/zhihu-jsdom.test.ts | 241 +++++++++++++++++++++++++++++ tests/zhihu-session.worker.test.ts | 64 ++++++++ 14 files changed, 567 insertions(+), 53 deletions(-) create mode 100644 lib/routes/zhihu/jsdom.ts create mode 100644 lib/routes/zhihu/jsdom.worker.ts create mode 100644 tests/zhihu-jsdom.test.ts create mode 100644 tests/zhihu-session.worker.test.ts diff --git a/lib/routes/zhihu/activities.ts b/lib/routes/zhihu/activities.ts index c7e17cd63cef..e7e83238b76c 100644 --- a/lib/routes/zhihu/activities.ts +++ b/lib/routes/zhihu/activities.ts @@ -2,6 +2,7 @@ import sanitizeHtml from 'sanitize-html'; import type { Route } from '@/types'; import { ViewType } from '@/types'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -16,11 +17,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', optional: true, }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/answers.ts b/lib/routes/zhihu/answers.ts index e31ebb2c49c5..157ec11b352e 100644 --- a/lib/routes/zhihu/answers.ts +++ b/lib/routes/zhihu/answers.ts @@ -1,4 +1,5 @@ import type { Route } from '@/types'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -12,11 +13,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', optional: true, }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/jsdom.ts b/lib/routes/zhihu/jsdom.ts new file mode 100644 index 000000000000..2e3b1046213a --- /dev/null +++ b/lib/routes/zhihu/jsdom.ts @@ -0,0 +1,197 @@ +import { JSDOM, VirtualConsole } from 'jsdom'; +import { VM } from 'vm2'; + +import { config } from '@/config'; +import cache from '@/utils/cache'; +import { generateHeaders } from '@/utils/header-generator'; +import md5 from '@/utils/md5'; +import ofetch from '@/utils/ofetch'; + +import { getSignedHeaders } from './sign'; +import type { ZhihuClient } from './utils'; + +let isUnreachableRuntimeErrorGuarded = false; +const pendingZseCredentials = new Map>(); + +const preventUnreachableRuntimeError = () => { + if (isUnreachableRuntimeErrorGuarded) { + return; + } + isUnreachableRuntimeErrorGuarded = true; + process.on('unhandledRejection', (reason) => { + const error = reason as { name?: string; message?: string } | undefined; + if (error?.name === 'RuntimeError' && error.message === 'unreachable') { + return; + } + throw reason; + }); +}; + +const generateZseCk = async (url: string, apiPath: string, configuredDc0: string) => { + preventUnreachableRuntimeError(); + + // `__zse_ck` is checked against the user-agent that generated it. + const ua = generateHeaders()['user-agent']; + const headers = { 'user-agent': ua }; + + let dc0 = configuredDc0; + if (!dc0) { + const seed = await ofetch.raw('https://www.zhihu.com/explore', { + headers, + redirect: 'manual', + ignoreResponseError: true, + }); + dc0 = + (seed.headers.getSetCookie?.() ?? []) + .find((line) => line.startsWith('d_c0=')) + ?.split(';', 1)[0] + .slice('d_c0='.length) + .split('|', 1)[0] + .replace(/=+$/, '') || ''; + } + if (!dc0) { + throw new Error('zhihu: failed to obtain a guest d_c0 cookie'); + } + + const challenge = await ofetch.raw(`https://www.zhihu.com${apiPath}`, { + headers: { + ...headers, + cookie: `d_c0=${dc0}; __zse_ck=005_x-x`, + referer: url, + 'x-requested-with': 'fetch', + }, + ignoreResponseError: true, + }); + const html = challenge._data ?? ''; + const meta = html.match(/id="zh-zse-ck"[^>]*content="([^"]*)"/)?.[1]; + const hash = html.match(/zse-ck\/v4\/([a-f0-9]+)\.js/)?.[1]; + if (!meta || !hash) { + throw new Error('zhihu: challenge page did not contain an URL to __zse_ck meta/script'); + } + + const vmScript = await ofetch(`https://static.zhihu.com/zse-ck/v4/${hash}.js`, { + headers, + parseResponse: (text) => text, + }); + const dom = new JSDOM(``, { + url, + referrer: 'https://www.zhihu.com/', + runScripts: 'outside-only', + pretendToBeVisual: true, + virtualConsole: new VirtualConsole(), + }); + const { window } = dom; + Object.defineProperties(window.navigator, { + userAgent: { value: ua, configurable: true }, + webdriver: { value: false, configurable: true }, + }); + window.TextEncoder = TextEncoder; + window.TextDecoder = TextDecoder as typeof window.TextDecoder; + window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary'); + window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64'); + Object.assign(window, { __g: {} }); + + const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie'); + const setCookie = cookieDescriptor?.set; + if (!cookieDescriptor?.get || !setCookie) { + window.close(); + throw new Error('zhihu: JSDOM did not provide document.cookie accessors'); + } + const tokenPromise = new Promise((resolve) => { + Object.defineProperty(window.document, 'cookie', { + configurable: true, + get: cookieDescriptor.get, + set(value: string) { + setCookie.call(window.document, value); + const token = value.match(/__zse_ck=([^;]+)/)?.[1]; + if (token?.includes('-')) { + resolve(token); + } + }, + }); + }); + + let zseCk: string | undefined; + let timeoutId: ReturnType | number | undefined; + try { + // Zhihu's challenge is intentionally delivered as executable JavaScript. + new VM({ timeout: 3000, sandbox: window }).run(vmScript); + const timeout = new Promise((resolve) => { + timeoutId = setTimeout(resolve, 3000); + }); + zseCk = await Promise.race([tokenPromise, timeout]); + } finally { + clearTimeout(timeoutId); + window.close(); + } + if (!zseCk) { + throw new Error('zhihu: WASM VM did not produce a __zse_ck'); + } + return { dc0, zseCk, ua }; +}; + +const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: string) => { + const cacheKey = `zhihu:zse-ck:v4:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; + const pending = pendingZseCredentials.get(cacheKey); + if (pending) { + return pending; + } + + const created = (async () => { + try { + return await cache.tryGet(cacheKey, () => generateZseCk(url, apiPath, configuredDc0), config.cache.contentExpire, false); + } finally { + pendingZseCredentials.delete(cacheKey); + } + })(); + pendingZseCredentials.set(cacheKey, created); + return created; +}; + +const mergeGeneratedCookies = (configured: string, dc0: string, zseCk: string) => { + const remaining = configured + .split(';') + .map((pair) => pair.trim()) + .filter((pair) => { + const name = pair.split('=', 1)[0]; + return name && name !== 'd_c0' && name !== '__zse_ck'; + }); + return [`__zse_ck=${zseCk}`, `d_c0=${dc0}`, ...remaining].join('; '); +}; + +export const createJSDOMClient = (pageUrl: string, configured: string, configuredDc0: string): ZhihuClient => { + let sessionPromise: Promise<{ dc0: string; headers: { cookie: string; 'user-agent': string } }> | undefined; + const startSession = async (apiPath: string) => { + try { + const { dc0, zseCk, ua } = await getGeneratedZseCredentials(pageUrl, apiPath, configuredDc0); + return { + dc0, + headers: { + // An isolated login cookie must not be mixed with a new guest identity. + cookie: configuredDc0 ? mergeGeneratedCookies(configured, dc0, zseCk) : `__zse_ck=${zseCk}; d_c0=${dc0}`, + 'user-agent': ua, + }, + }; + } catch (error) { + sessionPromise = undefined; + throw error; + } + }; + const getSession = (apiPath = new URL(pageUrl).pathname + new URL(pageUrl).search) => (sessionPromise ??= startSession(apiPath)); + + return { + get: async (apiPath: string): Promise => { + if (!apiPath.startsWith('/api/')) { + throw new Error('zhihu: expected an API path'); + } + const { dc0, headers } = await getSession(apiPath); + return ofetch(`https://www.zhihu.com${apiPath}`, { + headers: { ...getSignedHeaders(apiPath, dc0), ...headers, Referer: pageUrl }, + }); + }, + getPage: async () => { + const { headers } = await getSession(); + return ofetch(pageUrl, { headers: { ...headers, Referer: pageUrl }, parseResponse: (text) => text }); + }, + }; +}; diff --git a/lib/routes/zhihu/jsdom.worker.ts b/lib/routes/zhihu/jsdom.worker.ts new file mode 100644 index 000000000000..053cb52b9f12 --- /dev/null +++ b/lib/routes/zhihu/jsdom.worker.ts @@ -0,0 +1,6 @@ +import type { createJSDOMClient as createNodeJSDOMClient } from './jsdom'; + +// Exclude JSDOM from Worker bundles, including unreachable dynamic import chunks. +export const createJSDOMClient: typeof createNodeJSDOMClient = () => { + throw new Error('zhihu: JSDOM session initialization is unavailable on Workers; use a browser session'); +}; diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 6db09d96fb02..9cd1a9938f64 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import type { Articles } from './types'; @@ -14,11 +15,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', optional: true, }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index cdd9b669044c..abf4811235ef 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.ts @@ -1,4 +1,5 @@ import type { Route } from '@/types'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -12,11 +13,11 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', optional: true, }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/topic.ts b/lib/routes/zhihu/topic.ts index 1249c6ed2bd8..b8b056c7b532 100644 --- a/lib/routes/zhihu/topic.ts +++ b/lib/routes/zhihu/topic.ts @@ -1,5 +1,6 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -13,10 +14,10 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index acbcbb4565fe..a3b31b39896c 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -1,6 +1,7 @@ import { load } from 'cheerio'; import { config } from '@/config'; +import { isWorker } from '@/utils/is-worker'; import ofetch from '@/utils/ofetch'; import type { createBrowserClient } from './browser'; @@ -75,6 +76,11 @@ export const withZhihuClient = async (pageUrl: string, callback: (client: Zhi const configured = config.zhihu.cookies || ''; const dc0 = getCookieValueFrom(configured, 'd_c0'); const hasConfiguredSession = !!dc0 && !!getCookieValueFrom(configured, '__zse_ck'); + if (!isWorker && !hasConfiguredSession) { + const { createJSDOMClient } = await import('./jsdom'); + return callback(createJSDOMClient(pageUrl, configured, dc0)); + } + let browserPromise: ReturnType | undefined; const startBrowser = async (apiPath?: string) => { const { createBrowserClient } = await import('./browser'); diff --git a/lib/routes/zhihu/zhuanlan.ts b/lib/routes/zhihu/zhuanlan.ts index 3013c97cc02d..dfa9ac16adcd 100644 --- a/lib/routes/zhihu/zhuanlan.ts +++ b/lib/routes/zhihu/zhuanlan.ts @@ -1,6 +1,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -14,10 +15,10 @@ export const route: Route = { requireConfig: [ { name: 'ZHIHU_COOKIES', - description: 'A complete d_c0 and __zse_ck cookie pair avoids browser-based session initialization. Otherwise Playwright (BROWSER on Workers) is required.', + description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', }, ], - requirePuppeteer: true, + requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/package.json b/package.json index 34df7bc2f73a..3f1b2b3c6f68 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", + "jsdom": "30.0.1", "json-bigint": "1.0.0", "jsrsasign": "11.1.5", "lru-cache": "11.5.2", @@ -161,6 +162,7 @@ "@types/fs-extra": "11.0.4", "@types/html-to-text": "9.0.4", "@types/js-beautify": "1.14.3", + "@types/jsdom": "30.0.0", "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be4b905d8b5c..b29ca80761e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,6 +160,9 @@ importers: ip-regex: specifier: 5.0.0 version: 5.0.0 + jsdom: + specifier: 30.0.1 + version: 30.0.1(@noble/hashes@2.3.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -332,6 +335,9 @@ importers: '@types/js-beautify': specifier: 1.14.3 version: 1.14.3 + '@types/jsdom': + specifier: 30.0.0 + version: 30.0.0 '@types/json-bigint': specifier: 1.0.4 version: 1.0.4 @@ -2819,6 +2825,9 @@ packages: '@types/js-beautify@1.14.3': resolution: {integrity: sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==} + '@types/jsdom@30.0.0': + resolution: {integrity: sha512-uAHGxujGE0cDaKGdK28zgDotFtNA7MKq5DXl8LrfdxdCI8VHcg15oJz+amHTChPNI5JpgEPQWc2xFdrw3em/nQ==} + '@types/json-bigint@1.0.4': resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} @@ -6036,6 +6045,9 @@ packages: unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@8.10.2: + resolution: {integrity: sha512-7/+aSjzkUoLc92hV22bTW4aGanXf800zbwguhcICs0OAoCF9wDOE4wkopQ+SqfhXZm8mCK8gHpdTs7pZUWzK3w==} + undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} @@ -6533,7 +6545,6 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.2 - optional: true '@asamuzakjp/dom-selector@8.3.2': dependencies: @@ -6541,7 +6552,6 @@ snapshots: css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 - optional: true '@asteasolutions/zod-to-openapi@9.1.0(zod@4.5.4)': dependencies: @@ -6610,7 +6620,6 @@ snapshots: '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 - optional: true '@bufbuild/protobuf@2.14.0': {} @@ -6697,14 +6706,12 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@csstools/color-helpers@6.1.1': - optional: true + '@csstools/color-helpers@6.1.1': {} '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: @@ -6712,20 +6719,16 @@ snapshots: '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-tokenizer': 4.0.0 - optional: true '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 - optional: true - '@csstools/css-tokenizer@4.0.0': - optional: true + '@csstools/css-tokenizer@4.0.0': {} '@dabh/diagnostics@2.0.8': dependencies: @@ -7037,7 +7040,6 @@ snapshots: '@exodus/bytes@1.15.1(@noble/hashes@2.3.0)': optionalDependencies: '@noble/hashes': 2.3.0 - optional: true '@googleapis/youtube@35.0.0': dependencies: @@ -8207,6 +8209,13 @@ snapshots: '@types/js-beautify@1.14.3': {} + '@types/jsdom@30.0.0': + dependencies: + '@types/node': 26.4.1 + '@types/tough-cookie': 4.0.5 + parse5: 8.0.1 + undici-types: 8.10.2 + '@types/json-bigint@1.0.4': {} '@types/json-schema@7.0.15': {} @@ -8680,7 +8689,6 @@ snapshots: bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 - optional: true big-integer@1.6.52: {} @@ -8910,7 +8918,6 @@ snapshots: dependencies: mdn-data: 2.27.1 source-map-js: 1.2.1 - optional: true css-what@6.2.2: {} @@ -8935,7 +8942,6 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' - optional: true dayjs@1.11.23: {} @@ -8956,8 +8962,7 @@ snapshots: decamelize@6.0.1: {} - decimal.js@10.6.0: - optional: true + decimal.js@10.6.0: {} decode-named-character-reference@1.3.0: dependencies: @@ -9744,7 +9749,6 @@ snapshots: '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' - optional: true html-escaper@2.0.2: {} @@ -9955,8 +9959,7 @@ snapshots: is-plain-object@5.0.0: {} - is-potential-custom-element-name@1.0.1: - optional: true + is-potential-custom-element-name@1.0.1: {} is-promise@2.2.2: {} @@ -10052,7 +10055,6 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' - optional: true jsesc@3.1.0: {} @@ -10373,8 +10375,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdn-data@2.27.1: - optional: true + mdn-data@2.27.1: {} mdn-data@2.29.0: {} @@ -10980,7 +10981,6 @@ snapshots: parse5@8.0.1: dependencies: entities: 8.0.0 - optional: true parseley@0.13.1: dependencies: @@ -11265,8 +11265,7 @@ snapshots: require-directory@2.1.1: {} - require-from-string@2.0.2: - optional: true + require-from-string@2.0.2: {} require-in-the-middle@8.0.1: dependencies: @@ -11390,7 +11389,6 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 - optional: true scslre@0.3.0: dependencies: @@ -11588,8 +11586,7 @@ snapshots: dependencies: has-flag: 4.0.0 - symbol-tree@3.2.4: - optional: true + symbol-tree@3.2.4: {} system-architecture@0.1.0: {} @@ -11685,7 +11682,6 @@ snapshots: tr46@6.0.0: dependencies: punycode: 2.3.1 - optional: true tree-kill@1.2.2: {} @@ -11805,6 +11801,8 @@ snapshots: '@quansync/fs': 1.0.0 quansync: 1.0.0 + undici-types@8.10.2: {} + undici-types@8.3.0: {} undici@6.28.0: {} @@ -11997,7 +11995,6 @@ snapshots: w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - optional: true web-streams-polyfill@3.3.3: {} @@ -12005,8 +12002,7 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@8.0.1: - optional: true + webidl-conversions@8.0.1: {} whatwg-encoding@3.1.1: dependencies: @@ -12014,8 +12010,7 @@ snapshots: whatwg-mimetype@4.0.0: {} - whatwg-mimetype@5.0.0: - optional: true + whatwg-mimetype@5.0.0: {} whatwg-url@16.0.1(@noble/hashes@2.3.0): dependencies: @@ -12024,7 +12019,6 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - optional: true whatwg-url@17.1.0(@noble/hashes@2.3.0): dependencies: @@ -12033,7 +12027,6 @@ snapshots: webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - optional: true whatwg-url@5.0.0: dependencies: @@ -12167,8 +12160,7 @@ snapshots: dependencies: lodash: 4.18.1 - xml-name-validator@5.0.0: - optional: true + xml-name-validator@5.0.0: {} xml2js@0.6.2: dependencies: @@ -12177,8 +12169,7 @@ snapshots: xmlbuilder@11.0.1: {} - xmlchars@2.2.0: - optional: true + xmlchars@2.2.0: {} xxhash-wasm@1.1.0: {} diff --git a/tests/zhihu-browser.test.ts b/tests/zhihu-browser.test.ts index 2d36fa802ff8..3eaf8fcbadc4 100644 --- a/tests/zhihu-browser.test.ts +++ b/tests/zhihu-browser.test.ts @@ -25,6 +25,7 @@ vi.mock('../lib/config', () => ({ }, })); vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); +vi.mock('../lib/utils/is-worker', () => ({ isWorker: true })); vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); const origin = 'https://www.zhihu.com'; diff --git a/tests/zhihu-jsdom.test.ts b/tests/zhihu-jsdom.test.ts new file mode 100644 index 000000000000..e77acd0246f6 --- /dev/null +++ b/tests/zhihu-jsdom.test.ts @@ -0,0 +1,241 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + cookies: '', + ofetch: vi.fn(), + raw: vi.fn(), + tryGet: vi.fn(), + generateHeaders: vi.fn(), + createBrowserClient: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ + config: { + zhihu: { + get cookies() { + return mocks.cookies; + }, + }, + cache: { contentExpire: 3600 }, + }, +})); +vi.mock('../lib/utils/ofetch', () => ({ default: Object.assign(mocks.ofetch, { raw: mocks.raw }) })); +vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); +vi.mock('../lib/utils/header-generator', () => ({ generateHeaders: mocks.generateHeaders })); +vi.mock('../lib/utils/is-worker', () => ({ isWorker: false })); +vi.mock('../lib/routes/zhihu/browser', () => ({ createBrowserClient: mocks.createBrowserClient })); + +const origin = 'https://www.zhihu.com'; +const pageUrl = `${origin}/people/example`; +const apiPath = '/api/v4/members/example/activities?limit=20'; +const otherApiPath = '/api/v4/members/example?include=description'; +const scriptUrl = 'https://static.zhihu.com/zse-ck/v4/0123456789abcdef.js'; +const challengeHtml = ''; +const challengeScript = ` +if (navigator.userAgent !== 'RSSHub JSDOM fixture' || navigator.webdriver !== false) { + throw new Error('The challenge did not receive the configured browser fingerprint'); +} +if (document.getElementById('zh-zse-ck').getAttribute('content') !== 'challenge-fixture') { + throw new Error('The challenge did not receive its page metadata'); +} +document.cookie = '__zse_ck=generated-token; path=/'; +`; +const userAgent = 'RSSHub JSDOM fixture'; +const feedData = { data: [{ id: 'api-item' }] }; +const pageHtml = 'Profile content'; +const cached = new Map(); +let initialRejectionListeners: Set[number]>; + +const requestHeaders = (url: string) => { + const request = mocks.ofetch.mock.calls.find(([requestedUrl]) => requestedUrl === url); + expect(request).toBeDefined(); + return new Headers(request?.[1]?.headers); +}; + +beforeEach(() => { + vi.resetModules(); + vi.resetAllMocks(); + cached.clear(); + mocks.cookies = ''; + initialRejectionListeners = new Set(process.listeners('unhandledRejection')); + vi.spyOn(Math, 'random').mockReturnValue(0.25); + mocks.generateHeaders.mockReturnValue({ 'user-agent': userAgent }); + mocks.tryGet.mockImplementation(async (key: string, fetchValue: () => Promise) => { + if (!cached.has(key)) { + cached.set(key, await fetchValue()); + } + return cached.get(key); + }); + mocks.raw.mockImplementation((url: string) => Promise.resolve(url === `${origin}/explore` ? { headers: new Headers({ 'set-cookie': 'd_c0=guest-identity==|fixture; Path=/; Secure' }) } : { _data: challengeHtml })); + mocks.ofetch.mockImplementation((url: string) => { + if (url === scriptUrl) { + return Promise.resolve(challengeScript); + } + if (url === pageUrl) { + return Promise.resolve(pageHtml); + } + if (url.startsWith(`${origin}/api/`)) { + return Promise.resolve(feedData); + } + throw new Error(`Unexpected fixture URL: ${url}`); + }); +}); + +afterEach(() => { + for (const listener of process.listeners('unhandledRejection')) { + if (!initialRejectionListeners.has(listener)) { + process.removeListener('unhandledRejection', listener); + } + } + expect(mocks.createBrowserClient).not.toHaveBeenCalled(); + vi.restoreAllMocks(); +}); + +describe('Zhihu JSDOM client on Node', () => { + it.each(['', 'z_c0=isolated-login', 'd_c0=; z_c0=isolated-login'])('executes the challenge in JSDOM and keeps guest credentials isolated: %s', async (cookies) => { + mocks.cookies = cookies; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); + + expect(mocks.raw).toHaveBeenNthCalledWith(1, `${origin}/explore`, { + headers: { 'user-agent': userAgent }, + redirect: 'manual', + ignoreResponseError: true, + }); + expect(mocks.raw).toHaveBeenNthCalledWith(2, `${origin}${apiPath}`, { + headers: { + 'user-agent': userAgent, + cookie: 'd_c0=guest-identity; __zse_ck=005_x-x', + referer: pageUrl, + 'x-requested-with': 'fetch', + }, + ignoreResponseError: true, + }); + expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=guest-identity'); + expect(requestHeaders(`${origin}${apiPath}`).get('user-agent')).toBe(userAgent); + expect(mocks.ofetch).toHaveBeenCalledWith(scriptUrl, { headers: { 'user-agent': userAgent }, parseResponse: expect.any(Function) }); + expect(mocks.tryGet).toHaveBeenCalledWith('zhihu:zse-ck:v4:guest', expect.any(Function), 3600, false); + }); + + it('preserves the configured identity and login cookies without creating a guest session', async () => { + mocks.cookies = 'd_c0=configured==; z_c0=login=value; __zse_ck=; extra=retained'; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, (client) => client.get(apiPath)); + + expect(mocks.raw).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, expect.objectContaining({ headers: expect.objectContaining({ cookie: 'd_c0=configured==; __zse_ck=005_x-x' }) })); + expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=configured==; z_c0=login=value; extra=retained'); + expect(requestHeaders(`${origin}${apiPath}`).get('user-agent')).toBe(userAgent); + }); + + it('keeps API and HTML requests in the same generated identity and signs each API path separately', async () => { + mocks.cookies = 'd_c0=configured==; z_c0=login'; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + const { getSignedHeaders } = await import('../lib/routes/zhihu/sign'); + + const result = await withZhihuClient(pageUrl, async (client) => ({ + first: await client.get(apiPath), + second: await client.get(otherApiPath), + html: await client.getPage(), + })); + + expect(result).toEqual({ first: feedData, second: feedData, html: pageHtml }); + expect(mocks.raw).toHaveBeenCalledTimes(1); + expect(mocks.generateHeaders).toHaveBeenCalledTimes(1); + for (const url of [`${origin}${apiPath}`, `${origin}${otherApiPath}`, pageUrl]) { + const headers = requestHeaders(url); + expect(headers.get('cookie')).toBe('__zse_ck=generated-token; d_c0=configured==; z_c0=login'); + expect(headers.get('user-agent')).toBe(userAgent); + expect(headers.get('referer')).toBe(pageUrl); + } + expect(requestHeaders(`${origin}${apiPath}`).get('x-zse-96')).toBe(getSignedHeaders(apiPath, 'configured==')['x-zse-96']); + expect(requestHeaders(`${origin}${otherApiPath}`).get('x-zse-96')).toBe(getSignedHeaders(otherApiPath, 'configured==')['x-zse-96']); + expect(requestHeaders(`${origin}${apiPath}`).get('x-zse-96')).not.toBe(requestHeaders(`${origin}${otherApiPath}`).get('x-zse-96')); + const htmlRequest = mocks.ofetch.mock.calls.find(([url]) => url === pageUrl); + expect(htmlRequest?.[1]?.parseResponse(pageHtml)).toBe(pageHtml); + }); + + it('initializes from the page path when HTML is requested before an API', async () => { + mocks.cookies = 'd_c0=configured=='; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, async (client) => { + expect(await client.getPage()).toBe(pageHtml); + expect(await client.get(apiPath)).toEqual(feedData); + }); + + expect(mocks.raw).toHaveBeenCalledExactlyOnceWith(pageUrl, expect.objectContaining({ ignoreResponseError: true })); + expect(requestHeaders(pageUrl).get('cookie')).toBe(requestHeaders(`${origin}${apiPath}`).get('cookie')); + expect(requestHeaders(pageUrl).get('user-agent')).toBe(userAgent); + }); + + it('deduplicates concurrent challenge generation and reuses cached credentials across clients', async () => { + mocks.cookies = 'd_c0=configured=='; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + const results = await Promise.all([withZhihuClient(pageUrl, (client) => client.get(apiPath)), withZhihuClient(pageUrl, (client) => client.get(otherApiPath))]); + expect(results).toEqual([feedData, feedData]); + await withZhihuClient(pageUrl, (client) => client.get(apiPath)); + + expect(mocks.raw).toHaveBeenCalledTimes(1); + expect(mocks.generateHeaders).toHaveBeenCalledTimes(1); + expect(mocks.ofetch.mock.calls.filter(([url]) => url === scriptUrl)).toHaveLength(1); + }); + + it('retries generation after a failed challenge without permanently caching the rejection', async () => { + mocks.cookies = 'd_c0=configured=='; + mocks.raw.mockResolvedValueOnce({ _data: 'No challenge script' }); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, async (client) => { + await expect(client.get(apiPath)).rejects.toThrow('challenge page'); + expect(await client.get(apiPath)).toEqual(feedData); + }); + + expect(mocks.raw).toHaveBeenCalledTimes(2); + expect(mocks.tryGet).toHaveBeenCalledTimes(2); + expect(mocks.ofetch.mock.calls.filter(([url]) => url === `${origin}${apiPath}`)).toHaveLength(1); + }); + + it('does not reuse generated credentials for a different configured identity', async () => { + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + mocks.cookies = 'd_c0=first-identity; z_c0=first-login'; + await withZhihuClient(pageUrl, (client) => client.get(apiPath)); + mocks.cookies = 'd_c0=second-identity; z_c0=second-login'; + await withZhihuClient(pageUrl, (client) => client.get(otherApiPath)); + + expect(mocks.raw).toHaveBeenCalledTimes(2); + expect(mocks.tryGet.mock.calls[0][0]).not.toBe(mocks.tryGet.mock.calls[1][0]); + expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=first-identity; z_c0=first-login'); + expect(requestHeaders(`${origin}${otherApiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=second-identity; z_c0=second-login'); + }); + + it('uses complete configured credentials without initializing JSDOM or a browser', async () => { + mocks.cookies = 'd_c0=configured==; __zse_ck=existing-token; z_c0=login=value'; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient(pageUrl, async (client) => ({ data: await client.get(apiPath), html: await client.getPage() }))).toEqual({ data: feedData, html: pageHtml }); + + expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe(mocks.cookies); + expect(requestHeaders(pageUrl).get('cookie')).toBe(mocks.cookies); + expect(mocks.raw).not.toHaveBeenCalled(); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.generateHeaders).not.toHaveBeenCalled(); + }); + + it.each(['', 'd_c0=configured==; __zse_ck=existing-token'])('rejects non-API URLs before any I/O: %s', async (cookies) => { + mocks.cookies = cookies; + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + await withZhihuClient(pageUrl, async (client) => { + await expect(client.get('https://example.com/api/v4/members/example')).rejects.toThrow('expected an API path'); + await expect(client.get('/people/example')).rejects.toThrow('expected an API path'); + }); + + expect(mocks.ofetch).not.toHaveBeenCalled(); + expect(mocks.raw).not.toHaveBeenCalled(); + expect(mocks.tryGet).not.toHaveBeenCalled(); + expect(mocks.generateHeaders).not.toHaveBeenCalled(); + }); +}); diff --git a/tests/zhihu-session.worker.test.ts b/tests/zhihu-session.worker.test.ts new file mode 100644 index 000000000000..2a8189284d0c --- /dev/null +++ b/tests/zhihu-session.worker.test.ts @@ -0,0 +1,64 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { isWorker } from '@/utils/is-worker'; + +const mocks = vi.hoisted(() => ({ + cookies: '', + ofetch: vi.fn(), + createBrowserClient: vi.fn(), + createJSDOMClient: vi.fn(() => { + throw new Error('JSDOM must never run on Workers'); + }), + get: vi.fn(), + getPage: vi.fn(), + close: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ + config: { + zhihu: { + get cookies() { + return mocks.cookies; + }, + }, + }, +})); +vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); +vi.mock('../lib/routes/zhihu/browser', () => ({ createBrowserClient: mocks.createBrowserClient })); +vi.mock('../lib/routes/zhihu/jsdom', () => ({ createJSDOMClient: mocks.createJSDOMClient })); + +beforeEach(() => { + vi.resetAllMocks(); + mocks.cookies = ''; + mocks.createBrowserClient.mockResolvedValue({ get: mocks.get, getPage: mocks.getPage, close: mocks.close }); +}); + +describe('Zhihu runtime selection in workerd', () => { + it('uses a browser session for both API and HTML requests', async () => { + expect(isWorker).toBe(true); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + mocks.get.mockResolvedValue({ data: [1] }); + mocks.getPage.mockResolvedValue('Column'); + + await withZhihuClient('https://zhuanlan.zhihu.com/example', async (client) => { + expect(await client.get('/api/v4/columns/example/items')).toEqual({ data: [1] }); + expect(await client.getPage()).toBe('Column'); + }); + + expect(mocks.createBrowserClient).toHaveBeenCalledExactlyOnceWith('https://zhuanlan.zhihu.com/example', '', '/api/v4/columns/example/items'); + expect(mocks.close).toHaveBeenCalledOnce(); + expect(mocks.createJSDOMClient).not.toHaveBeenCalled(); + expect(mocks.ofetch).not.toHaveBeenCalled(); + }); + + it('uses complete configured cookies without either initializer', async () => { + mocks.cookies = 'd_c0=configured; __zse_ck=existing'; + mocks.ofetch.mockResolvedValue({ data: [1] }); + const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); + + expect(await withZhihuClient('https://www.zhihu.com/people/example', (client) => client.get('/api/v4/members/example'))).toEqual({ data: [1] }); + expect(mocks.createBrowserClient).not.toHaveBeenCalled(); + expect(mocks.createJSDOMClient).not.toHaveBeenCalled(); + expect(mocks.ofetch).toHaveBeenCalledOnce(); + }); +}); From 16fed0d05e80d7c851fdf052e415e0cffacfac13 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 8 Sep 2026 11:31:34 +0800 Subject: [PATCH 0745/1190] fix(route/everia): use wp api (#23243) --- lib/routes/everia/article.ts | 30 ++++++++++-------------------- lib/routes/everia/category.ts | 22 ++++++++++------------ lib/routes/everia/latest.ts | 15 ++------------- lib/routes/everia/search.ts | 16 +++------------- lib/routes/everia/tag.ts | 22 ++++++++++------------ 5 files changed, 35 insertions(+), 70 deletions(-) diff --git a/lib/routes/everia/article.ts b/lib/routes/everia/article.ts index 25a1d07677f8..bfc3b518dded 100644 --- a/lib/routes/everia/article.ts +++ b/lib/routes/everia/article.ts @@ -1,20 +1,10 @@ -import { load } from 'cheerio'; - -import got from '@/utils/got'; - -export const loadArticle = async (link) => { - const resp = await got(link); - const article = load(resp.body); - - const title = article('h2.entry-title').text().trim(); - const description = article('.wp-block-image') - .toArray() - .map((element) => article.html(element)) - .join(''); - - return { - title, - description, - link, - }; -}; +import { parseDate } from '@/utils/parse-date'; + +export const loadArticle = (post) => ({ + title: post.title.rendered, + description: post.content.rendered, + pubDate: parseDate(post.date_gmt), + link: post.link, + author: post._embedded?.author?.[0]?.name, + category: post._embedded?.['wp:term']?.flat().map((term) => term.name) ?? [], +}); diff --git a/lib/routes/everia/category.ts b/lib/routes/everia/category.ts index a0cfa3921f99..5663bdecec09 100644 --- a/lib/routes/everia/category.ts +++ b/lib/routes/everia/category.ts @@ -1,5 +1,4 @@ -import { load } from 'cheerio'; - +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -39,19 +38,18 @@ async function handler(ctx) { const category = ctx.req.param('category'); const categoryUrl = `${SUB_URL}category/${category}/`; - const response = await got(categoryUrl); - const $ = load(response.body); - const itemRaw = $('article.blog-entry').slice(0, limit).toArray(); + const categoryId = await cache.tryGet(`everia:category:${category}`, async () => { + const { data: categories } = await got(`${SUB_URL}wp-json/wp/v2/categories?slug=${category}`); + if (!categories.length) { + throw new InvalidParameterError(`Category not found: ${category}`); + } + return categories[0].id as number; + }); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?categories=${categoryId}&per_page=${limit}&_embed`); return { title: `${SUB_NAME_PREFIX} - Category: ${category}`, link: categoryUrl, - item: await Promise.all( - itemRaw.map((e) => { - const item = $(e); - const link = item.find('h2.entry-title a').attr('href'); - return cache.tryGet(link!, () => loadArticle(link)); - }) - ), + item: posts.map((post) => loadArticle(post)), }; } diff --git a/lib/routes/everia/latest.ts b/lib/routes/everia/latest.ts index 6644328505a3..bcb5e9973944 100644 --- a/lib/routes/everia/latest.ts +++ b/lib/routes/everia/latest.ts @@ -1,7 +1,4 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; -import cache from '@/utils/cache'; import got from '@/utils/got'; import { loadArticle } from './article'; @@ -34,19 +31,11 @@ export const route: Route = { async function handler(ctx) { const limit = Number.parseInt(ctx.req.query('limit')) || 20; - const response = await got(SUB_URL); - const $ = load(response.body); - const itemRaw = $('article.blog-entry').slice(0, limit).toArray(); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?per_page=${limit}&_embed`); return { title: `${SUB_NAME_PREFIX} - Latest`, link: SUB_URL, - item: await Promise.all( - itemRaw.map((e) => { - const item = $(e); - const link = item.find('h2.entry-title a').attr('href'); - return cache.tryGet(link!, () => loadArticle(link)); - }) - ), + item: posts.map((post) => loadArticle(post)), }; } diff --git a/lib/routes/everia/search.ts b/lib/routes/everia/search.ts index 20e33bfbc6da..475a8302201e 100644 --- a/lib/routes/everia/search.ts +++ b/lib/routes/everia/search.ts @@ -1,7 +1,4 @@ -import { load } from 'cheerio'; - import type { Route } from '@/types'; -import cache from '@/utils/cache'; import got from '@/utils/got'; import { loadArticle } from './article'; @@ -27,22 +24,15 @@ export const route: Route = { }; async function handler(ctx) { + const limit = Number.parseInt(ctx.req.query('limit')) || 20; const keyword = ctx.req.param('keyword'); const url = `${SUB_URL}?s=${keyword}`; - const response = await got(url); - const $ = load(response.body); - const itemRaw = $('article.post').toArray(); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?search=${keyword}&per_page=${limit}&_embed`); return { title: `${SUB_NAME_PREFIX} - Search: ${keyword}`, link: url, - item: await Promise.all( - itemRaw.map((e) => { - const item = $(e); - const link = item.find('h2.entry-title a').attr('href'); - return cache.tryGet(link!, () => loadArticle(link)); - }) - ), + item: posts.map((post) => loadArticle(post)), }; } diff --git a/lib/routes/everia/tag.ts b/lib/routes/everia/tag.ts index 1b560b0f8569..3f12cdff0e82 100644 --- a/lib/routes/everia/tag.ts +++ b/lib/routes/everia/tag.ts @@ -1,5 +1,4 @@ -import { load } from 'cheerio'; - +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -39,19 +38,18 @@ async function handler(ctx) { const tag = ctx.req.param('tag'); const tagUrl = `${SUB_URL}tag/${tag}/`; - const response = await got(tagUrl); - const $ = load(response.body); - const itemRaw = $('article.blog-entry').slice(0, limit).toArray(); + const tagId = await cache.tryGet(`everia:tag:${tag}`, async () => { + const { data: tags } = await got(`${SUB_URL}wp-json/wp/v2/tags?slug=${tag}`); + if (!tags.length) { + throw new InvalidParameterError(`Tag not found: ${tag}`); + } + return tags[0].id as number; + }); + const { data: posts } = await got(`${SUB_URL}wp-json/wp/v2/posts?tags=${tagId}&per_page=${limit}&_embed`); return { title: `${SUB_NAME_PREFIX} - Tag: ${tag}`, link: tagUrl, - item: await Promise.all( - itemRaw.map((e) => { - const item = $(e); - const link = item.find('h2.entry-title a').attr('href'); - return cache.tryGet(link!, () => loadArticle(link)); - }) - ), + item: posts.map((post) => loadArticle(post)), }; } From 4378755d638cbe05ebfa192d0c74d54b534d0505 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:03:23 +0800 Subject: [PATCH 0746/1190] feat(route): add Namoc exhibition route (#23241) * feat(route): add Namoc exhibition route * fix: simplify imgUrl * fix: update scriptText Co-authored-by: Tony --------- --- lib/routes/namoc/namespace.ts | 10 +++ lib/routes/namoc/zhanlan.tsx | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 lib/routes/namoc/namespace.ts create mode 100644 lib/routes/namoc/zhanlan.tsx diff --git a/lib/routes/namoc/namespace.ts b/lib/routes/namoc/namespace.ts new file mode 100644 index 000000000000..06a07e0c4e01 --- /dev/null +++ b/lib/routes/namoc/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'National Art Museum of China', + url: 'www.namoc.cn', + + zh: { + name: '中国美术馆', + }, +}; diff --git a/lib/routes/namoc/zhanlan.tsx b/lib/routes/namoc/zhanlan.tsx new file mode 100644 index 000000000000..258981dbdf08 --- /dev/null +++ b/lib/routes/namoc/zhanlan.tsx @@ -0,0 +1,120 @@ +import { load } from 'cheerio'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { namespace } from './namespace'; + +const baseUrl = 'https://www.namoc.cn'; +const listUrl = `${baseUrl}/namoc/zhanlan/zl_list.shtml`; + +export const route: Route = { + path: '/zhanlan', + categories: ['travel'], + example: '/namoc/zhanlan', + name: 'Exhibitions', + maintainers: ['magazian'], + radar: [ + { + source: ['www.namoc.cn/namoc/zhanlan/zl_list.shtml'], + target: '/zhanlan', + }, + ], + handler: async (): Promise => { + const museumName = namespace.zh?.name || namespace.name; + + const response = await ofetch(listUrl); + const $ = load(response); + + const items = await Promise.all( + $('ul.zllist#zlhglb > li.clearfix') + .toArray() + .map((el) => { + const $li = $(el); + + const $titleAnchor = $li.find('div.text h3 > a'); + const title = $titleAnchor.attr('title') || ''; + + const rawLink = $titleAnchor.attr('href') || ''; + const link = new URL(rawLink, baseUrl).href; + + const fullDuration = $li + .find('div.text div p') + .filter((_, p) => $(p).text().startsWith('展览时间:')) + .text() + .replace('展览时间:', ''); + + const location = $li + .find('div.text div p') + .filter((_, p) => $(p).text().startsWith('展览场地:')) + .text() + .replace('展览场地:', ''); + + // Parse startDate and endDate from "YYYY-MM-DD至YYYY-MM-DD" + const dateParts = fullDuration.split('至'); + const startDate = dateParts[0] || undefined; + const endDate = dateParts[1] || undefined; + + const scriptText = $li.find('p.image script').text(); + const images = JSON.parse(scriptText.match(/var\s+jsonImageStr\s*=\s*'(\[.*?\])'/s)![1]) as Array<{ savepath: string }>; + const imgUrl = new URL(images[0].savepath, baseUrl).href; + + return cache.tryGet(link, async (): Promise => { + // Fetch detail page for pubDate + const detailHtml = await ofetch(link); + const $detail = load(detailHtml); + const pubDateStr = $detail('meta[name="PubDate"]').attr('content') || ''; + const pubDate = timezone(parseDate(pubDateStr), 8); + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + + return { + title, + link, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }; + }); + }) + ); + + return { + title: `${museumName} - 展览`, + link: listUrl, + language: 'zh-CN', + item: items, + }; + }, +}; From 85c7a8a288f38e23c0a368b052fd5a1358bc0fc3 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Tue, 8 Sep 2026 12:53:50 +0800 Subject: [PATCH 0747/1190] fix(bilibili): recover ranking requests blocked by risk control --- lib/routes/bilibili/ranking.ts | 68 ++++++- tests/bilibili-ranking.test.ts | 273 ++++++++++++++++++++++++++ tests/bilibili-ranking.worker.test.ts | 1 + 3 files changed, 335 insertions(+), 7 deletions(-) create mode 100644 tests/bilibili-ranking.test.ts create mode 100644 tests/bilibili-ranking.worker.test.ts diff --git a/lib/routes/bilibili/ranking.ts b/lib/routes/bilibili/ranking.ts index 7c9f5d3452ae..439570446a51 100644 --- a/lib/routes/bilibili/ranking.ts +++ b/lib/routes/bilibili/ranking.ts @@ -1,8 +1,11 @@ +import { FetchError } from 'ofetch'; + import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { getPlaywrightPage } from '@/utils/playwright'; import cache from './cache'; import utils, { getVideoUrl } from './utils'; @@ -180,6 +183,7 @@ function getAPI(isNumericRid: boolean, rid: string | number) { ridChinese: zone?.chinese ?? '', ridType: 'x/rid', link: 'https://www.bilibili.com/v/popular/rank/all', + browserLink: zone?.type === 'x/rid' ? `https://www.bilibili.com/v/popular/rank/${zone.english}` : undefined, }; } @@ -221,9 +225,64 @@ function getAPI(isNumericRid: boolean, rid: string | number) { ridChinese, ridType, link: `https://www.bilibili.com/v/popular/rank/${ridEnglish}`, + browserLink: `https://www.bilibili.com/v/popular/rank/${ridEnglish}`, }; } +const allowedBrowserRequestTypes = new Set(['document', 'script', 'xhr', 'fetch']); +const browserResponseTimeout = 30000; + +async function fetchRankingFromBrowser(apiUrl: string, browserLink: string) { + const expectedUrl = new URL(apiUrl); + const { page, destroy } = await getPlaywrightPage(browserLink, { noGoto: true, closeTimeout: 0 }); + try { + await page.route('**/*', (route) => (allowedBrowserRequestTypes.has(route.request().resourceType()) ? route.continue() : route.abort())); + // Let the official page initialize its anonymous session and sign the ranking request. + const [response] = await Promise.all([ + page.waitForResponse( + (response) => { + const url = new URL(response.url()); + return ( + url.origin === expectedUrl.origin && + url.pathname === expectedUrl.pathname && + url.searchParams.get('rid') === expectedUrl.searchParams.get('rid') && + url.searchParams.get('type') === expectedUrl.searchParams.get('type') + ); + }, + { timeout: browserResponseTimeout } + ), + page.goto(browserLink, { waitUntil: 'domcontentloaded', timeout: browserResponseTimeout }), + ]); + if (!response.ok()) { + throw new Error(`Bilibili ranking browser request failed with HTTP ${response.status()}`); + } + return await response.json(); + } finally { + await destroy(); + } +} + +async function fetchRanking(apiUrl: string, referer: string, browserLink?: string) { + let response; + try { + response = await ofetch(apiUrl, { + headers: { + Referer: referer, + origin: 'https://www.bilibili.com', + }, + }); + } catch (error) { + if (!(error instanceof FetchError) || error.status !== 412 || !browserLink) { + throw error; + } + return fetchRankingFromBrowser(apiUrl, browserLink); + } + if (response.code === -352 && browserLink) { + return fetchRankingFromBrowser(apiUrl, browserLink); + } + return response; +} + async function handler(ctx) { const isJsonFeed = ctx.req.query('format') === 'json'; const args = ctx.req.param(); @@ -238,17 +297,12 @@ async function handler(ctx) { const embed = !ctx.req.param('embed'); const isNumericRid = /^\d+$/.test(rid); - const { apiBase, apiParams, referer, ridChinese, link, ridType } = getAPI(isNumericRid, rid); + const { apiBase, apiParams, referer, ridChinese, link, ridType, browserLink } = getAPI(isNumericRid, rid); if (ridType.startsWith('pgc/')) { throw new Error('This type of ranking is not supported yet'); } - const response = await ofetch(`${apiBase}?${apiParams}`, { - headers: { - Referer: referer, - origin: 'https://www.bilibili.com', - }, - }); + const response = await fetchRanking(`${apiBase}?${apiParams}`, referer, browserLink); if (response.code !== 0) { throw new Error(response.message); diff --git a/tests/bilibili-ranking.test.ts b/tests/bilibili-ranking.test.ts new file mode 100644 index 000000000000..13b55b55a21c --- /dev/null +++ b/tests/bilibili-ranking.test.ts @@ -0,0 +1,273 @@ +import type { Context } from 'hono'; +import { createFetchError } from 'ofetch'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { route } from '../lib/routes/bilibili/ranking'; +import type { Data } from '../lib/types'; + +const mocks = vi.hoisted(() => ({ + config: { bilibili: { excludeSubtitles: false } }, + ofetch: vi.fn(), + getPlaywrightPage: vi.fn(), + getVideoSubtitleAttachment: vi.fn(), + renderUGCDescription: vi.fn(), + getVideoUrl: vi.fn(), +})); + +vi.mock('../lib/config', () => ({ config: mocks.config })); +vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); +vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); +vi.mock('../lib/routes/bilibili/cache', () => ({ default: { getVideoSubtitleAttachment: mocks.getVideoSubtitleAttachment } })); +vi.mock('../lib/routes/bilibili/utils', () => ({ default: { bvidTime: 1_585_108_800, renderUGCDescription: mocks.renderUGCDescription }, getVideoUrl: mocks.getVideoUrl })); + +const apiBase = 'https://api.bilibili.com/x/web-interface/ranking/v2'; +const allPage = 'https://www.bilibili.com/v/popular/rank/all'; +const gamePage = 'https://www.bilibili.com/v/popular/rank/game'; +const item = { + aid: 1234, + bvid: 'BV1rankingFixture', + title: 'Ranking video', + pic: 'https://example.com/cover.jpg', + desc: 'Video description', + ctime: 1_700_000_000, + owner: { name: 'Video author' }, + duration: 125, +}; +const ranking = { code: 0, data: { list: [item] } }; + +type BrowserResponse = { url: () => string; ok: () => boolean; status: () => number; json: () => Promise }; +type BrowserRoute = { request: () => { resourceType: () => string }; continue: () => Promise; abort: () => Promise }; + +function makeBrowser(rid = '0', body: unknown = ranking, status = 200) { + const response = { + url: () => `${apiBase}?rid=${rid}&type=all&w_rid=signed-fixture&wts=1700000000`, + ok: () => status >= 200 && status < 300, + status: () => status, + json: vi.fn(() => Promise.resolve(body)), + }; + const page = { + route: vi.fn((_pattern: string, _handler: (route: BrowserRoute) => Promise) => Promise.resolve()), + waitForResponse: vi.fn((_predicate: (response: BrowserResponse) => boolean, _options: { timeout: number }) => Promise.resolve(response)), + goto: vi.fn((_url: string, _options: { waitUntil: string; timeout: number }) => Promise.resolve(null)), + }; + const destroy = vi.fn(() => Promise.resolve()); + mocks.getPlaywrightPage.mockResolvedValue({ page, destroy }); + return { page, response, destroy }; +} + +function httpError(status: number) { + return createFetchError({ request: `${apiBase}?rid=0&type=all`, options: {}, response: new Response('Upstream error', { status }) }); +} + +const invoke = async (params: Record = {}, query: Record = {}) => + (await route.handler({ req: { param: (name?: string) => (name ? params[name] : params), query: (name: string) => query[name] } } as unknown as Context)) as Data; + +beforeEach(() => { + vi.resetAllMocks(); + mocks.config.bilibili.excludeSubtitles = false; + mocks.ofetch.mockResolvedValue(ranking); + mocks.getPlaywrightPage.mockRejectedValue(new Error('Unexpected browser request')); + mocks.getVideoSubtitleAttachment.mockResolvedValue([]); + mocks.renderUGCDescription.mockImplementation((_embed, _pic, description) => `

    ${description}

    `); + mocks.getVideoUrl.mockImplementation((bvid) => `https://example.com/player/${bvid}`); + vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('External network access is disabled'))); +}); + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe('Bilibili ranking browser recovery', () => { + it.each([{}, { rid: '0' }, { rid: 'all' }])('keeps the native fast path and feed data for %j', async (params) => { + const result = await invoke(params); + + expect(mocks.ofetch).toHaveBeenCalledExactlyOnceWith(`${apiBase}?rid=0&type=all&web_location=333.934`, { headers: { Referer: allPage, origin: 'https://www.bilibili.com' } }); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + expect(mocks.getVideoSubtitleAttachment).not.toHaveBeenCalled(); + expect(result).toMatchObject({ + title: 'bilibili 排行榜-全站', + link: allPage, + item: [ + { + title: item.title, + description: '

    Video description

    ', + author: item.owner.name, + link: `https://www.bilibili.com/video/${item.bvid}`, + image: item.pic, + pubDate: new Date('2023-11-14T22:13:20.000Z'), + attachments: [{ url: `https://example.com/player/${item.bvid}`, mime_type: 'text/html', duration_in_seconds: 125 }], + }, + ], + }); + expect(mocks.renderUGCDescription).toHaveBeenCalledExactlyOnceWith(true, item.pic, item.desc, item.aid, undefined, item.bvid); + }); + + it.each([ + { params: {}, rid: '0', browserLink: allPage, feedLink: allPage, title: '全站' }, + { params: { rid: '0' }, rid: '0', browserLink: allPage, feedLink: allPage, title: '全站' }, + { params: { rid: 'all' }, rid: '0', browserLink: allPage, feedLink: allPage, title: '全站' }, + { params: { rid: 'game' }, rid: '1008', browserLink: gamePage, feedLink: gamePage, title: '游戏' }, + { params: { rid: '1008' }, rid: '1008', browserLink: gamePage, feedLink: allPage, title: '游戏' }, + ])('recovers HTTP 412 on the correct official page for $params', async ({ params, rid, browserLink, feedLink, title }) => { + mocks.ofetch.mockRejectedValue(httpError(412)); + const { page, destroy } = makeBrowser(rid); + + const result = await invoke(params); + + expect(mocks.ofetch).toHaveBeenCalledTimes(1); + expect(mocks.ofetch.mock.calls[0][0]).toBe(`${apiBase}?rid=${rid}&type=all&web_location=333.934`); + expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(browserLink, { noGoto: true, closeTimeout: 0 }); + expect(page.goto).toHaveBeenCalledExactlyOnceWith(browserLink, { waitUntil: 'domcontentloaded', timeout: 30000 }); + expect(page.waitForResponse).toHaveBeenCalledExactlyOnceWith(expect.any(Function), { timeout: 30000 }); + expect(page.waitForResponse.mock.invocationCallOrder[0]).toBeLessThan(page.goto.mock.invocationCallOrder[0]); + expect(destroy).toHaveBeenCalledTimes(1); + expect(result).toMatchObject({ title: `bilibili 排行榜-${title}`, link: feedLink, item: [{ title: item.title, author: item.owner.name }] }); + }); + + it.each(['0', 'all', 'game', '1008'])('recovers application risk-control code -352 for %s', async (rid) => { + mocks.ofetch.mockResolvedValue({ code: -352, message: 'Risk control' }); + const { destroy } = makeBrowser(rid === '0' || rid === 'all' ? '0' : '1008'); + + expect((await invoke({ rid })).item).toHaveLength(1); + expect(mocks.ofetch).toHaveBeenCalledTimes(1); + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(1); + expect(destroy).toHaveBeenCalledTimes(1); + }); + + it.each([403, 404, 429, 500])('does not hide HTTP %s behind browser recovery', async (status) => { + const failure = httpError(status); + mocks.ofetch.mockRejectedValue(failure); + + await expect(invoke({ rid: '0' })).rejects.toBe(failure); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it('preserves native network failures and does not mistake an arbitrary status property for an HTTP failure', async () => { + const failure = Object.assign(new Error('Network unavailable'), { status: 412 }); + mocks.ofetch.mockRejectedValue(failure); + + await expect(invoke()).rejects.toBe(failure); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it.each([-400, -403, -404])('preserves non-risk application error %s', async (code) => { + mocks.ofetch.mockResolvedValue({ code, message: 'Upstream application error' }); + + await expect(invoke()).rejects.toThrow('Upstream application error'); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it.each(['9999', '1'])('does not substitute the all ranking for numeric rid %s without a supported official page', async (rid) => { + const failure = httpError(412); + mocks.ofetch.mockRejectedValue(failure); + await expect(invoke({ rid })).rejects.toBe(failure); + + mocks.ofetch.mockResolvedValue({ code: -352, message: 'Risk control' }); + await expect(invoke({ rid })).rejects.toThrow('Risk control'); + expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); + }); + + it('allows only document, script, XHR and fetch resources needed to initialize the official page', async () => { + mocks.ofetch.mockResolvedValue({ code: -352 }); + const { page } = makeBrowser(); + await invoke(); + expect(page.route).toHaveBeenCalledExactlyOnceWith('**/*', expect.any(Function)); + const handler = page.route.mock.calls[0][1]; + + await Promise.all( + ['document', 'script', 'xhr', 'fetch', 'image', 'media', 'font', 'stylesheet', 'websocket', 'other'].map(async (resourceType) => { + const request = { request: () => ({ resourceType: () => resourceType }), continue: vi.fn(() => Promise.resolve()), abort: vi.fn(() => Promise.resolve()) }; + await handler(request); + const allowed = ['document', 'script', 'xhr', 'fetch'].includes(resourceType); + expect(request.continue).toHaveBeenCalledTimes(allowed ? 1 : 0); + expect(request.abort).toHaveBeenCalledTimes(allowed ? 0 : 1); + }) + ); + }); + + it('matches only the requested ranking origin, path, rid and type while allowing page-generated signatures', async () => { + mocks.ofetch.mockResolvedValue({ code: -352 }); + const { page, response } = makeBrowser('1008'); + await invoke({ rid: 'game' }); + const matches = page.waitForResponse.mock.calls[0][0]; + + expect(matches(response)).toBe(true); + expect(matches({ ...response, url: () => `${apiBase}?w_rid=another-signature&type=all&web_location=333.934&rid=1008` })).toBe(true); + for (const url of [ + `${apiBase}?rid=0&type=all`, + `${apiBase}?rid=1008&type=origin`, + `${apiBase}?rid=1008`, + `${apiBase}?type=all`, + `${apiBase}/other?rid=1008&type=all`, + `${apiBase.replace('/ranking/v2', '/popular')}?rid=1008&type=all`, + `${apiBase.replace('https:', 'http:')}?rid=1008&type=all`, + `${apiBase.replace('api.bilibili.com', 'api.bilibili.com.evil.test')}?rid=1008&type=all`, + `${apiBase.replace('api.bilibili.com', 'api.bilibili.com:8443')}?rid=1008&type=all`, + ]) { + expect(matches({ ...response, url: () => url }), url).toBe(false); + } + }); + + it('propagates browser setup failures', async () => { + mocks.ofetch.mockRejectedValue(httpError(412)); + const failure = new Error('Browser unavailable'); + mocks.getPlaywrightPage.mockRejectedValue(failure); + + await expect(invoke()).rejects.toBe(failure); + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(1); + }); + + it.each(['route', 'waitForResponse', 'goto', 'json'] as const)('awaits browser cleanup when %s fails', async (stage) => { + mocks.ofetch.mockResolvedValue({ code: -352 }); + const { page, response, destroy } = makeBrowser(); + const failure = new Error(`${stage} failed`); + const operation = stage === 'json' ? response.json : page[stage]; + operation.mockRejectedValue(failure); + const cleanup = Promise.withResolvers(); + destroy.mockReturnValue(cleanup.promise); + const result = invoke(); + const assertion = expect(result).rejects.toBe(failure); + let finished = false; + void result + .then(() => { + finished = true; + }) + .catch(() => { + finished = true; + }); + + await vi.waitFor(() => expect(destroy).toHaveBeenCalledTimes(1)); + expect(finished).toBe(false); + cleanup.resolve(); + await assertion; + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(1); + }); + + it.each([403, 412, 500])('reports browser HTTP %s and closes the browser without parsing the error body', async (status) => { + mocks.ofetch.mockResolvedValue({ code: -352 }); + const { response, destroy } = makeBrowser('0', ranking, status); + + await expect(invoke()).rejects.toThrow(`Bilibili ranking browser request failed with HTTP ${status}`); + expect(response.json).not.toHaveBeenCalled(); + expect(destroy).toHaveBeenCalledTimes(1); + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(1); + }); + + it.each([-352, -400])('does not swallow or repeatedly retry browser application error %s', async (code) => { + mocks.ofetch.mockRejectedValue(httpError(412)); + const { destroy } = makeBrowser('0', { code, message: 'Browser application error' }); + + await expect(invoke()).rejects.toThrow('Browser application error'); + expect(destroy).toHaveBeenCalledTimes(1); + expect(mocks.getPlaywrightPage).toHaveBeenCalledTimes(1); + expect(mocks.ofetch).toHaveBeenCalledTimes(1); + }); + + it('does not turn a malformed browser payload into an empty successful feed', async () => { + mocks.ofetch.mockResolvedValue({ code: -352 }); + const { destroy } = makeBrowser('0', { code: 0 }); + + await expect(invoke()).rejects.toThrow(); + expect(destroy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/tests/bilibili-ranking.worker.test.ts b/tests/bilibili-ranking.worker.test.ts new file mode 100644 index 000000000000..3469f26097ea --- /dev/null +++ b/tests/bilibili-ranking.worker.test.ts @@ -0,0 +1 @@ +import './bilibili-ranking.test'; From e59689cb56af2b1f8ee0b380f1d17d4d02254673 Mon Sep 17 00:00:00 2001 From: JimenezLi <75196426+JimenezLi@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:21:01 +0800 Subject: [PATCH 0748/1190] feat(route): add 1lou search (#23239) * feat(route): add 1lou search * docs: add some comments * fix: remove timezone parse for unix timestamp --- lib/routes/1lou/index.ts | 48 ++--------------- lib/routes/1lou/search.ts | 106 ++++++++++++++++++++++++++++++++++++++ lib/routes/1lou/util.ts | 50 ++++++++++++++++++ 3 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 lib/routes/1lou/search.ts create mode 100644 lib/routes/1lou/util.ts diff --git a/lib/routes/1lou/index.ts b/lib/routes/1lou/index.ts index 2ccd839c8d99..ede610a7fdfe 100644 --- a/lib/routes/1lou/index.ts +++ b/lib/routes/1lou/index.ts @@ -2,11 +2,12 @@ import { load } from 'cheerio'; import type { Context } from 'hono'; import type { DataItem, Language, Route } from '@/types'; -import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { parseItems } from './util'; + const rootUrl = 'https://www.1lou.me'; export const handler = async (ctx: Context) => { @@ -49,48 +50,7 @@ export const handler = async (ctx: Context) => { }; }); - items = await Promise.all( - items.map((item) => - cache.tryGet(item.link, async () => { - const { data: detailResponse } = await got(item.link); - - const $$ = load(detailResponse); - - const title = $$('h4.break-all').contents().last().text(); - - if (title) { - const description = $$('div.message.break-all').html(); - const image = new URL($$('img').first().prop('src')!, rootUrl).href; - - item.title = title; - item.description = description; - item.pubDate = timezone(parseDate($$('span.date').text()), 8); - item.category = $$('a.badge') - .toArray() - .map((c) => $$(c).text()); - item.content = { - html: description, - text: $$('div.message.break-all').text(), - }; - item.image = image; - item.banner = image; - item.language = language; - - const torrents = $$('ul.attachlist li a'); - - if (torrents.length > 0) { - const torrent = torrents.first(); - - item.enclosure_url = new URL(torrent.prop('href')!, rootUrl).href; - item.enclosure_type = 'application/x-bittorrent'; - item.enclosure_title = torrent.text(); - } - } - - return item; - }) - ) - ); + items = await parseItems(items, language); const author = 'BT 之家 1LOU 站'; const image = new URL($('img.logo-2').prop('src')!, rootUrl).href; @@ -122,7 +82,7 @@ export const route: Route = { 若订阅 [最新发帖电视剧](https://www.1lou.me/forum-2-1.htm?orderby=tid\\&digest=0),网址为 \`https://www.1lou.me/forum-2-1.htm?orderby=tid&digest=0\`。截取 \`https://www.1lou.me/\` 到末尾 \`.htm\` 的部分 \`forum-2-1\` 作为参数,并补充 \`orderby\`,此时路由为 [\`/1lou/forum-2-1?orderby=tid\`](https://rsshub.app/1lou/forum-2-1?orderby=tid)。 -若订阅 [搜素繁花主题贴](https://www.1lou.me/search-_E7_B9_81_E8_8A_B1-1.htm),网址为 \`https://www.1lou.me/search-_E7_B9_81_E8_8A_B1-1.htm\`。截取 \`https://www.1lou.me/\` 到末尾 \`.htm\` 的部分 \`search-_E7_B9_81_E8_8A_B1-1\` 作为参数,此时路由为 [\`/1lou/search-_E7_B9_81_E8_8A_B1-1\`](https://rsshub.app/1lou/search-_E7_B9_81_E8_8A_B1-1)。 +搜索功能已移至 “搜索” 路由。 :::`, categories: ['multimedia'], diff --git a/lib/routes/1lou/search.ts b/lib/routes/1lou/search.ts new file mode 100644 index 000000000000..a51245ea91a1 --- /dev/null +++ b/lib/routes/1lou/search.ts @@ -0,0 +1,106 @@ +import type { Context } from 'hono'; + +import type { DataItem, Language, Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; + +import { parseItems } from './util'; + +const rootUrl = 'https://www.1lou.me'; +const apiUrl = 'https://www.1lou.me/search/api/search.php'; + +export const handler = async (ctx: Context) => { + const { params } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50; + + const searchQuery = params; + + const apiParams = new URLSearchParams({ + q: searchQuery, + fid: '0', + page: '1', + sort: 'newest', + scope: '全部', + type: '全部', + year: '全部', + quality: '全部', + source: '全部', + track: '1', + }); + + const apiUrlWithParams = `${apiUrl}?${apiParams.toString()}`; + + const { data: response } = await got(apiUrlWithParams); + + const hits: Array<{ + tid: number; + fid: number; + subject: string; + highlighted_subject: string; + username: string; + create_date: number; + views: number; + posts: number; + files: number; + thread_url: string; + }> = response?.data?.hits ?? []; + + const language: Language = 'zh'; + + const items: Array = hits.slice(0, limit).map((item) => ({ + title: item.highlighted_subject.replaceAll(/<\/?mark>/g, ''), + pubDate: parseDate(item.create_date, 'X'), + link: new URL(item.thread_url, rootUrl).href, + category: [item.username], + author: item.username, + language, + })); + + const refinedItems = await parseItems(items, language); + + const author = 'BT 之家 1LOU 站'; + + return { + title: `搜索 ${searchQuery} - ${author}`, + description: response?.data?.query ? `搜索关键词: ${response.data.query}` : '搜索结果', + link: `${rootUrl}/search?q=${encodeURIComponent(searchQuery)}`, + item: refinedItems, + allowEmpty: true, + author, + language, + }; +}; + +export const route: Route = { + path: '/search/:params', + name: '搜索', + url: '1lou.me/search', + maintainers: ['JimenezLi'], + handler, + example: '/1lou/search/繁花', + parameters: { params: '搜索关键词' }, + description: '搜索路由,支持关键词搜索。', + categories: ['multimedia'], + + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['1lou.me/search'], + target: (_, url) => { + const parsedUrl = new URL(url); + const searchParams = parsedUrl.searchParams; + const query = searchParams.get('q') || parsedUrl.pathname.match(/\/search-(.+)-\d+\.htm/)?.[1] || ''; + + return `/1lou/search/${query}`; + }, + }, + ], +}; diff --git a/lib/routes/1lou/util.ts b/lib/routes/1lou/util.ts new file mode 100644 index 000000000000..d96d75d6c23a --- /dev/null +++ b/lib/routes/1lou/util.ts @@ -0,0 +1,50 @@ +import { load } from 'cheerio'; + +import cache from '@/utils/cache'; +import got from '@/utils/got'; + +const rootUrl = 'https://www.1lou.me'; + +export const parseItems = async (items, language) => + await Promise.all( + items.map((item) => + cache.tryGet(item.link, async () => { + const { data: detailResponse } = await got(item.link); + + const $$ = load(detailResponse); + + const title = $$('h4.break-all').contents().last().text(); + + if (title) { + const description = $$('div.message.break-all').html(); + const image = new URL($$('img').first().prop('src')!, rootUrl).href; + + item.title = title; + item.description = description; + // No parsing item.pubDate because it may match the date in comments + item.category = $$('a.badge') + .toArray() + .map((c) => $$(c).text()); + item.content = { + html: description, + text: $$('div.message.break-all').text(), + }; + item.image = image; + item.banner = image; + item.language = language; + + const torrents = $$('ul.attachlist li a'); + + if (torrents.length > 0) { + const torrent = torrents.first(); + + item.enclosure_url = new URL(torrent.prop('href')!, rootUrl).href; + item.enclosure_type = 'application/x-bittorrent'; + item.enclosure_title = torrent.text(); + } + } + + return item; + }) + ) + ); From 4a00e668bfd932393959eaa1ca9cdb32ef4e826c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:18:33 +0800 Subject: [PATCH 0749/1190] chore(deps): bump the nix group with 2 updates (#23245) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `07666e4` to `8ea6b32` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/07666e44528d349ae352c33441fbf625df115370...8ea6b32683c7108e6705edc7817ccfa8d358a2d9) Updates `nixpkgs` from `3ed67ec` to `801bef6` - [Commits](https://github.com/NixOS/nixpkgs/compare/3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2...801bef6abd86b91e51083066b83fb354a11fc640) --- updated-dependencies: - dependency-name: devenv dependency-version: 8ea6b32683c7108e6705edc7817ccfa8d358a2d9 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: 801bef6abd86b91e51083066b83fb354a11fc640 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index dec4021b6426..2c129417e527 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1788494439, - "narHash": "sha256-vYpGge1n4Jd44/qopYhuJeAEIr+iCrx4KuAIoyw6VlE=", + "lastModified": 1788582287, + "narHash": "sha256-dg00rtwZFDmswg8Wp+GodoXHysrUsz4WMMWXtOKvvlU=", "owner": "cachix", "repo": "devenv", - "rev": "07666e44528d349ae352c33441fbf625df115370", + "rev": "8ea6b32683c7108e6705edc7817ccfa8d358a2d9", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1788316716, - "narHash": "sha256-bc7rSpXIdn9QWGNqfWcPZWOhEVF8NoeAZkWq0XWnf/k=", + "lastModified": 1788531059, + "narHash": "sha256-hLD4l3QOGBQhkVp3mQ2lJ/YbEi99qUgKapb40KovZ88=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2", + "rev": "801bef6abd86b91e51083066b83fb354a11fc640", "type": "github" }, "original": { From 865f1cf5af3973dffaa2cb8c2d73ee0538043c12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:19:02 +0800 Subject: [PATCH 0750/1190] chore(deps): bump pnpm/action-setup from 6.0.10 to 6.1.0 (#23244) Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 6.0.10 to 6.1.0. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](https://github.com/pnpm/action-setup/compare/0977fd99725f1db4007ccb2928dbb4e90d06cc86...ea17c68df8912ef543352723c149a84f56e3d413) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-assets.yml | 2 +- .github/workflows/comment-on-issue.yml | 4 ++-- .github/workflows/docker-test-cont.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/issue-command.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/npm-publish.yml | 2 +- .github/workflows/test-full-routes.yml | 2 +- .github/workflows/test.yml | 6 +++--- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 06e3b1d4840c..573edd0188ad 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - name: Use Node.js Active LTS uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 6bf569e6dc96..de6a197096c5 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -27,7 +27,7 @@ jobs: closed: ${{ steps.check.outputs.closed }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* @@ -61,7 +61,7 @@ jobs: needs.checkIssue.outputs.closed != 'true' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index 8aa9f6766c29..28bc3d5edfb1 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -43,7 +43,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index cac18de8d561..c6edde4a9d67 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 59b7cf7c083b..976f9eaa3659 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -111,7 +111,7 @@ jobs: ref: ${{ fromJson(steps.pr-data.outputs.data).head.ref }} - name: Install pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - name: Use Node.js Active LTS uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e29eae2ea3c8..f1387328df53 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: security-events: write steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index c2fae5ae6ef5..ec5899ad20ba 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -23,7 +23,7 @@ jobs: HUSKY: 0 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: lts/* diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index ab3903c0eba2..b453e35cce4c 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -16,7 +16,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - name: Use Node.js Active LTS uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97a7a5b089a5..f91322fc7c4e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: name: Vitest on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} @@ -78,7 +78,7 @@ jobs: name: Vitest Playwright on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} @@ -127,7 +127,7 @@ jobs: name: Build radar and maintainer on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} From e3d4200f58662d0608c02323ad6b98faf16deb1f Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 9 Sep 2026 01:03:35 +0800 Subject: [PATCH 0751/1190] fix(route/36kr): optional waf token (#23248) --- lib/routes/36kr/hot-list.ts | 8 +++++--- lib/routes/36kr/utils.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/routes/36kr/hot-list.ts b/lib/routes/36kr/hot-list.ts index e7c486c4fa62..1e151a55f123 100644 --- a/lib/routes/36kr/hot-list.ts +++ b/lib/routes/36kr/hot-list.ts @@ -73,9 +73,11 @@ async function handler(ctx) { const response = await got({ method: 'get', url: currentUrl, - headers: { - Cookie: `_waftokenid=${wafTokenId}`, - }, + headers: wafTokenId + ? { + Cookie: `_waftokenid=${wafTokenId}`, + } + : undefined, }); const data = getProperty(JSON.parse(response.data.match(/window.initialState=(\{.*\})/)[1]), categories[category].key); diff --git a/lib/routes/36kr/utils.ts b/lib/routes/36kr/utils.ts index 81eba1dac299..754ffa8104f3 100644 --- a/lib/routes/36kr/utils.ts +++ b/lib/routes/36kr/utils.ts @@ -42,7 +42,11 @@ export const getWafTokenId = () => const payload = $('script') .text() .match(/atob\('(.*?)'\)\),/)?.[1]; - const response = solveWafChallenge(payload!); + if (!payload) { + return; + } + + const response = solveWafChallenge(payload); const tokenIdResponse = await ofetch.raw(rootUrl, { headers: { @@ -57,7 +61,7 @@ export const getWafTokenId = () => ?.split(';', 1)[0] .split('=', 2)[1]; - return _wafTokenId as string; + return _wafTokenId; }, 300, // server-provided value false From ea01f7c161565b997216089227dfd386689f0db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E8=80=95=E6=AF=85=EF=BC=88Ryan=20Chou=EF=BC=89?= <88779759+ryanchou1994@users.noreply.github.com> Date: Wed, 9 Sep 2026 01:33:40 +0800 Subject: [PATCH 0752/1190] fix(core): parse Tuesday and Thursday as past weekdays (#23247) --- lib/utils/parse-date.test.ts | 17 +++++++++++++++++ lib/utils/parse-date.ts | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/utils/parse-date.test.ts b/lib/utils/parse-date.test.ts index 3298bfd0d7af..f01a27d2fbad 100644 --- a/lib/utils/parse-date.test.ts +++ b/lib/utils/parse-date.test.ts @@ -118,6 +118,23 @@ describe('parseRelativeDate', () => { expect(p('周三')).toBe(PREVIOUS_WEDNESDAY); }); + it.each([ + ['Tuesday', '2026-01-27T00:00:00'], + ['Tue', '2026-01-27T00:00:00'], + ['Thursday', '2026-01-29T00:00:00'], + ['Thu', '2026-01-29T00:00:00'], + ['Tuesday 3pm', '2026-01-27T15:00:00'], + ['Thu 10:00', '2026-01-29T10:00:00'], + ])('parses %s as a past weekday instead of tomorrow', (input, expected) => { + expect(p(input)).toBe(new Date(expected).getTime()); + }); + + it.each(['Tomorrow', 't'])('preserves %s as tomorrow with optional time', (input) => { + expect(p(input)).toBe(TOMORROW_START); + expect(p(`${input}3pm`)).toBe(TOMORROW_START + 15 * hour); + expect(p(`${input} 10:00`)).toBe(TOMORROW_START + 10 * hour); + }); + it('handles "Sunday" (Past: Sun is Yesterday -> Feb 01)', () => { // Sunday (Feb 01) is strictly before Monday (Feb 02). expect(p('Sunday')).toBe(LAST_SUNDAY); diff --git a/lib/utils/parse-date.ts b/lib/utils/parse-date.ts index 7a4063d87238..c77f93192801 100644 --- a/lib/utils/parse-date.ts +++ b/lib/utils/parse-date.ts @@ -114,7 +114,7 @@ const KEYWORDS: KeywordDefinition[] = [ calc: () => dayjs().subtract(2, 'days').startOf('day'), }, { - regExp: /^(?:明[天日早晨晚]|t(?:omorrow)?)/i, + regExp: /^(?:明[天日早晨晚]|t(?:omorrow|(?![a-z])))/i, calc: () => dayjs().add(1, 'days').startOf('day'), }, { From 5df73fc8f91d3e796a9e8e23fbf7470ab5c8644b Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 9 Sep 2026 02:03:30 +0800 Subject: [PATCH 0753/1190] chore(flake): bump nodejs from 22 to 24 --- .idx/dev.nix | 63 ---------------------------------------------------- flake.nix | 2 +- 2 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 .idx/dev.nix diff --git a/.idx/dev.nix b/.idx/dev.nix deleted file mode 100644 index f5fb581a466d..000000000000 --- a/.idx/dev.nix +++ /dev/null @@ -1,63 +0,0 @@ -# To learn more about how to use Nix to configure your environment -# see: https://firebase.google.com/docs/studio/customize-workspace -{ pkgs, ... }: { - # Which nixpkgs channel to use. - channel = "unstable"; # or "unstable" - - # Use https://search.nixos.org/packages to find packages - packages = [ - # pkgs.go - # pkgs.python311 - # pkgs.python311Packages.pip - pkgs.nodejs_22 - pkgs.pnpm_9 - # pkgs.nodePackages.nodemon - pkgs.cacert - pkgs.valkey - ]; - - # Sets environment variables in the workspace - env = {}; - idx = { - # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" - extensions = [ - "cweijan.vscode-database-client2" - "dbaeumer.vscode-eslint" - "eamodio.gitlens" - "EditorConfig.EditorConfig" - "oxc.oxc-vscode" - "sonarsource.sonarlint-vscode" - ]; - - # Enable previews - previews = { - enable = true; - previews = { - # web = { - # # Example: run "npm run dev" with PORT set to IDX's defined port for previews, - # # and show it in IDX's web preview panel - # command = ["npm" "run" "dev"]; - # manager = "web"; - # env = { - # # Environment variables to set for your server - # PORT = "$PORT"; - # }; - # }; - }; - }; - - # Workspace lifecycle hooks - workspace = { - # Runs when a workspace is first created - onCreate = { - # Example: install JS dependencies from NPM - pnpm-install = "pnpm i && pnpm rb && pnpx rebrowser-puppeteer browsers install chrome"; - }; - # Runs when the workspace is (re)started - onStart = { - # Example: start a background task to watch and re-build backend code - # watch-backend = "npm run watch-backend"; - }; - }; - }; -} diff --git a/flake.nix b/flake.nix index 8122f42ac7c8..2b7729426cb8 100644 --- a/flake.nix +++ b/flake.nix @@ -27,7 +27,7 @@ src = ./.; nativeBuildInputs = with pkgs; [ - nodejs_22 + nodejs_24 pnpm pnpmConfigHook git From f637d3a5a267d2548c82b2b61c24e0dca0a338cf Mon Sep 17 00:00:00 2001 From: chouj Date: Wed, 9 Sep 2026 11:18:37 +0800 Subject: [PATCH 0754/1190] feat(route): add iMuseum city exhibitions route (#23246) * feat(route): add iMuseum city exhibitions route * fix(route): address review comments --- lib/routes/icity/imuseum.ts | 96 +++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 lib/routes/icity/imuseum.ts diff --git a/lib/routes/icity/imuseum.ts b/lib/routes/icity/imuseum.ts new file mode 100644 index 000000000000..6e2532cd8999 --- /dev/null +++ b/lib/routes/icity/imuseum.ts @@ -0,0 +1,96 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const rootUrl = 'https://art.icity.ly'; + +export const route: Route = { + path: '/imuseum/:city/:type?', + categories: ['travel'], + example: '/icity/imuseum/guangzhou/latest', + parameters: { + city: '城市,取自站点 URL 中的城市路径,如 guangzhou、shanghai、beijing', + type: { + description: '展览列表类型', + default: 'latest', + options: [ + { value: 'all', label: '全部' }, + { value: 'latest', label: '最新' }, + { value: 'hot', label: '热门' }, + { value: 'end_soon', label: '即将结束' }, + { value: 'coming', label: '即将开始' }, + { value: 'outdated', label: '已结束' }, + ], + }, + }, + name: 'iMuseum 城市展览', + maintainers: ['chouj'], + radar: [ + { + source: ['art.icity.ly/:city'], + target: '/imuseum/:city', + }, + ], + description: 'iMuseum(每日环球展览)各城市正在进行与即将开始的展览。城市与类型均取自站点 URL 路径,例如 `guangzhou/latest`。', + handler: async (ctx) => { + const { city } = ctx.req.param(); + const type = ctx.req.param('type') ?? 'latest'; + const currentUrl = `${rootUrl}/${city}/${type}`; + + const response = await ofetch(currentUrl); + const $ = load(response); + + // The city switcher is the only element with a dropdown, the `all` page has an extra "world" section above it. + const cityName = $('a[data-toggle="dropdown"] h3').text(); + // Scoped to `ul.nav-pills` because the top navbar has another `li.active`. + const typeName = $('ul.nav-pills li.active a').text(); + + // The `all` page also has a featured list (`ul.imsm-entries.thumb`) with a different markup, + // whose items are shared across every city, so only the main list is used. + const list = $('ul.imsm-entries.list > li') + .toArray() + .map((item) => { + const $item = $(item); + const $info = $item.find('a.info'); + + return { + title: $info.find('div.title').text(), + link: new URL($info.attr('href')!, rootUrl).href, + }; + }); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link, async () => { + const detailResponse = await ofetch(item.link); + const $detail = load(detailResponse); + const $entry = $detail('div.imsm-entry'); + + const cover = $entry.find('img.fit-width').attr('src'); + + const $description = $detail('
    '); + if (cover) { + $description.append($detail('').attr('src', cover)); + } + $description.append($entry.find('table.info-fields'), $entry.find('div.content')); + + return { + title: item.title, + link: item.link, + description: $description.html(), + image: cover, + }; + }) + ) + ); + + return { + title: `${cityName}${typeName}展览 - iMuseum`, + link: currentUrl, + language: 'zh-CN', + item: items, + }; + }, +}; From b96278048e6ea12d01cbdc57a9539a9509bd3975 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 16:45:15 +0800 Subject: [PATCH 0755/1190] chore(deps): bump nixpkgs from `801bef6` to `c043004` in the nix group (#23250) Bumps the nix group with 1 update: [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `nixpkgs` from `801bef6` to `c043004` - [Commits](https://github.com/NixOS/nixpkgs/compare/801bef6abd86b91e51083066b83fb354a11fc640...c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2c129417e527..5b8e006a635c 100644 --- a/flake.lock +++ b/flake.lock @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1788531059, - "narHash": "sha256-hLD4l3QOGBQhkVp3mQ2lJ/YbEi99qUgKapb40KovZ88=", + "lastModified": 1788614874, + "narHash": "sha256-7QYjT2vHLuX9Z1pdxHXDKCbh1CR3D/2rywB9Tx0MPRg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "801bef6abd86b91e51083066b83fb354a11fc640", + "rev": "c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0", "type": "github" }, "original": { From 09da9011c9eef23350335db5df2d6514657fb0fe Mon Sep 17 00:00:00 2001 From: Syoius Date: Wed, 9 Sep 2026 22:14:39 +0800 Subject: [PATCH 0756/1190] fix(route/xiaohongshu): restore authenticated user note full text (#23252) * fix(route/xiaohongshu): restore authenticated user note full text * fix(route/xiaohongshu): scope token link selector to user posted feeds --- lib/routes/xiaohongshu/util.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/routes/xiaohongshu/util.ts b/lib/routes/xiaohongshu/util.ts index aaaa523d2ce3..585cef45fab4 100644 --- a/lib/routes/xiaohongshu/util.ts +++ b/lib/routes/xiaohongshu/util.ts @@ -300,16 +300,29 @@ async function getUserWithCookie(url: string) { const cookie = config.xiaohongshu.cookie; const res = await fetchWithProxy(url, cookie); const $ = load(res); - const paths = $('#userPostedFeeds > section > div > a.cover.ld.mask').map((i, item) => item.attributes[3].value); const script = extractInitialState($); const state = JSON.parse(script); - let index = 0; + + const tokenizedPaths = new Map(); + + $('#userPostedFeeds a[href*="xsec_token"]').each((_, item) => { + const href = $(item).attr('href'); + if (!href) { + return; + } + + const match = href.match(/\/([0-9a-f]{24})(?:\?|$)/i); + if (match && href.includes('?')) { + tokenizedPaths.set(match[1], href); + } + }); + for (const item of state.user.notes.flat()) { - const path = paths[index]; - if (path && path.includes('?')) { - item.id += path?.slice(path.indexOf('?')); + const path = tokenizedPaths.get(item.id); + + if (path) { + item.id += path.slice(path.indexOf('?')); } - index += 1; } return state.user; } @@ -317,8 +330,11 @@ async function getUserWithCookie(url: string) { // Add helper function to extract initial state function extractInitialState($: CheerioAPI) { let script = $('script:contains("window.__INITIAL_STATE__=")').text(); + script = script.slice(script.indexOf('window.__INITIAL_STATE__=') + 'window.__INITIAL_STATE__='.length); - script = script.replaceAll('undefined', 'null'); + + script = script.replaceAll('undefined', 'null').replaceAll(/new Map\(\s*\[\s*\]\s*\)/g, 'null'); + return script; } From 5b0956b1fe87c725bf8cf21efa4c49587169acca Mon Sep 17 00:00:00 2001 From: chouj Date: Wed, 9 Sep 2026 23:11:57 +0800 Subject: [PATCH 0757/1190] feat(route): add 98zhibo football highlights route (#23251) * feat(route): add 98zhibo football highlights route * fix(route): remove hardcoded User-Agent * fix(route): remove unnecessary trim --- lib/routes/98zhibo/namespace.ts | 7 +++ lib/routes/98zhibo/zuqiujijin.ts | 102 +++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 lib/routes/98zhibo/namespace.ts create mode 100644 lib/routes/98zhibo/zuqiujijin.ts diff --git a/lib/routes/98zhibo/namespace.ts b/lib/routes/98zhibo/namespace.ts new file mode 100644 index 000000000000..161cad308238 --- /dev/null +++ b/lib/routes/98zhibo/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '98直播吧', + url: 'www.98zhibo.com', + lang: 'zh-CN', +}; diff --git a/lib/routes/98zhibo/zuqiujijin.ts b/lib/routes/98zhibo/zuqiujijin.ts new file mode 100644 index 000000000000..af14b73467e9 --- /dev/null +++ b/lib/routes/98zhibo/zuqiujijin.ts @@ -0,0 +1,102 @@ +import { load } from 'cheerio'; +import iconv from 'iconv-lite'; + +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const baseUrl = 'https://www.98zhibo.com'; + +export const route: Route = { + path: '/zuqiujijin', + categories: ['sport'], + example: '/98zhibo/zuqiujijin', + name: '足球集锦', + maintainers: ['chouj'], + radar: [ + { + source: ['www.98zhibo.com/zuqiujijin/'], + target: '/zuqiujijin', + }, + ], + handler, +}; + +// Titles start with the publish date, e.g. 2026年9月9日 欧冠-贝蒂斯客场3-2逆转十人里尔 ... +const parsePubDateFromTitle = (title: string) => { + const match = title.match(/^(\d{4})年(\d{1,2})月(\d{1,2})日/); + return match ? timezone(parseDate(`${match[1]}-${match[2]}-${match[3]}`), 8) : undefined; +}; + +async function handler(): Promise { + const currentUrl = `${baseUrl}/zuqiujijin/`; + + const response = await got({ + method: 'get', + url: currentUrl, + responseType: 'buffer', + }); + + const $ = load(iconv.decode(response.data, 'gbk')); + + const list = $('.list_body_bd li') + .toArray() + .map((item) => { + const $item = $(item); + const $link = $item.find('a'); + const title = $link.text(); + + return { + title, + link: new URL($link.attr('href')!, baseUrl).href, + pubDate: parsePubDateFromTitle(title), + }; + }); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link, async () => { + const detailResponse = await got({ + method: 'get', + url: item.link, + responseType: 'buffer', + }); + const $detail = load(iconv.decode(detailResponse.data, 'gbk')); + + const $body = $detail('.Content-body'); + // The hidden block holds the raw match data, the kickoff time is in UTC+8, e.g. 比赛时间:2026-09-09 03:00:00 + const matchTime = $body + .find('div[style*="display:none"]') + .text() + .match(/比赛时间:(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/)?.[1]; + + // Drop inline scripts, HTML comments and the hidden raw data block, keep only the visible content + $body.find('script, div[style*="display:none"]').remove(); + $body + .contents() + .filter((_, node) => node.type === 'comment') + .remove(); + + const $description = $detail('
    '); + if (matchTime) { + $description.append($detail('

    ').text(`比赛时间:${matchTime}`)); + } + $description.append($body.html() ?? ''); + + return { + ...item, + description: $description.html(), + }; + }) + ) + ); + + return { + title: '足球集锦 - 98直播吧', + link: currentUrl, + language: 'zh-CN', + item: items, + }; +} From 282351b46c9454ffcba8057bf7188645defeeafb Mon Sep 17 00:00:00 2001 From: CanonNi Date: Wed, 9 Sep 2026 23:37:55 +0800 Subject: [PATCH 0758/1190] feat(route): add Maldita.es (#23148) * feat(foute): add Maldita.es * fix updates.ts * fix link selection * tweak selector --- lib/routes/maldita/namespace.ts | 6 +++ lib/routes/maldita/updates.ts | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 lib/routes/maldita/namespace.ts create mode 100644 lib/routes/maldita/updates.ts diff --git a/lib/routes/maldita/namespace.ts b/lib/routes/maldita/namespace.ts new file mode 100644 index 000000000000..89ae8fb1ec0c --- /dev/null +++ b/lib/routes/maldita/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Maldita.es', + url: 'maldita.es', +}; diff --git a/lib/routes/maldita/updates.ts b/lib/routes/maldita/updates.ts new file mode 100644 index 000000000000..d5963d3be2ec --- /dev/null +++ b/lib/routes/maldita/updates.ts @@ -0,0 +1,72 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +export const route: Route = { + path: '/updates/:category?', + categories: ['new-media'], + example: '/maldita/updates/desinfo', + parameters: { category: 'Category to fetch' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['maldita.es/:category/'], + target: '/updates/:category', + }, + ], + name: 'Latest Updates', + maintainers: ['canonnizq'], + description: 'Categories: all | desinfo | prebunking | investigaciones | control-del-poder | policy', + + handler: async (ctx) => { + const { category = 'all' } = ctx.req.param(); + const url = `https://maldita.es/${category === 'all' ? '' : `${category}/`}`; + + const response = await ofetch(url); + const $ = load(response); + + const name = $('h1').text() || 'All updates'; + + const links = $(category === 'all' ? 'a.ac-link' : '#content a[href*="maldita.es"]:has(div)') + .toArray() + .map((item) => $(item).attr('href')!); + + const items = await Promise.all( + links.map((link) => + cache.tryGet(link, async () => { + const response = await ofetch(link); + const $ = load(response); + + return { + title: $('h1').text(), + link, + category: $('#article-metadata .flex a') + .toArray() + .map((x) => $(x).text()), + pubDate: parseDate($('time').attr('datetime')!), + itunes_item_image: $('#featuredImage img').attr('src'), + content: { + html: $('#keys').html()! + $('#article-content').html()!, + }, + }; + }) + ) + ); + + return { + title: `Maldita.es - ${name}`, + link: url, + item: items, + }; + }, +}; From 75b823c9fba0f3a8ce8275490686650bb7c83ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:01:18 +0000 Subject: [PATCH 0759/1190] chore(deps): bump devenv from `8ea6b32` to `a7014c6` in the nix group (#23257) Bumps the nix group with 1 update: [devenv](https://github.com/cachix/devenv). Updates `devenv` from `8ea6b32` to `a7014c6` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/8ea6b32683c7108e6705edc7817ccfa8d358a2d9...a7014c60a08855bdc9a4fddba220c28087afc21a) --- updated-dependencies: - dependency-name: devenv dependency-version: a7014c60a08855bdc9a4fddba220c28087afc21a dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 5b8e006a635c..3cabca59e7c4 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1788582287, - "narHash": "sha256-dg00rtwZFDmswg8Wp+GodoXHysrUsz4WMMWXtOKvvlU=", + "lastModified": 1788757012, + "narHash": "sha256-ILhsVKEZdjjVbzbcM9lx4kX8Jz4JZ/LhZcPcKr/gFvE=", "owner": "cachix", "repo": "devenv", - "rev": "8ea6b32683c7108e6705edc7817ccfa8d358a2d9", + "rev": "a7014c60a08855bdc9a4fddba220c28087afc21a", "type": "github" }, "original": { From e74e65662ed034a273791a11df25a849260f9c93 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Thu, 10 Sep 2026 20:13:17 +0800 Subject: [PATCH 0760/1190] docs: add StarDesk advertisement alongside Folo --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c109d1507b24..b330764ec99d 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,16 @@ - +
    + Folo
    RSSHub pairs especially well with Folo, an AI RSS reader for feed discovery and modern reading workflows. The project is also open source on GitHub.
    +StarDesk +
    +RSSHub keeps me informed. StarDesk lets me respond wherever I am. If a route needs updating, a feed stops working, or I just need to check logs and restart the RSSHub container, I can remote into my PC from my phone with StarDesk anywhere and get it sorted in minutes. I can even run a few terminal commands straight from my phone instead of opening a full desktop session. And it's free, I figured it was worth trying. +
    From 7510a470b5f71ab8307728a856f46c42471132a9 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Thu, 10 Sep 2026 21:10:07 +0800 Subject: [PATCH 0761/1190] docs: shorten StarDesk advertisement copy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b330764ec99d..56ed1459fa91 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ RSSHub pairs especially well with Folo, an AI RSS

    StarDesk
    -RSSHub keeps me informed. StarDesk lets me respond wherever I am. If a route needs updating, a feed stops working, or I just need to check logs and restart the RSSHub container, I can remote into my PC from my phone with StarDesk anywhere and get it sorted in minutes. I can even run a few terminal commands straight from my phone instead of opening a full desktop session. And it's free, I figured it was worth trying. +RSSHub keeps me informed, and StarDesk lets me fix routes or feeds remotely, even run terminal commands from my phone. It’s fast, convenient, and free.
    From 62a9a2f1101a1e15b28caac7f52270600a953931 Mon Sep 17 00:00:00 2001 From: CanonNi Date: Fri, 11 Sep 2026 10:23:48 +0800 Subject: [PATCH 0762/1190] feat(route): add iRacing (#23254) * feat(route): add iRacing * tweak route --- lib/routes/iracing/namespace.ts | 7 ++++ lib/routes/iracing/news.ts | 68 +++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 lib/routes/iracing/namespace.ts create mode 100644 lib/routes/iracing/news.ts diff --git a/lib/routes/iracing/namespace.ts b/lib/routes/iracing/namespace.ts new file mode 100644 index 000000000000..dbbfdf56b906 --- /dev/null +++ b/lib/routes/iracing/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'iRacing', + url: 'iracing.com', + description: '', +}; diff --git a/lib/routes/iracing/news.ts b/lib/routes/iracing/news.ts new file mode 100644 index 000000000000..aaa8846fd3f9 --- /dev/null +++ b/lib/routes/iracing/news.ts @@ -0,0 +1,68 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +export const route: Route = { + path: '/news', + categories: ['game'], + example: '/iracing/news', + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.iracing.com/category/news/sim-racing-news'], + target: '/news', + }, + ], + name: 'News', + maintainers: ['canonnizq'], + + handler: async () => { + const url = 'https://www.iracing.com/category/news/sim-racing-news/'; + + const response = await ofetch(url); + const $ = load(response); + + const links = $('#page .clearfix h2 a') + .toArray() + .map((item) => $(item).attr('href')!); + + const items = await Promise.all( + links.map((link) => + cache.tryGet(link, async () => { + const response = await ofetch(link); + const $ = load(response); + + return { + title: $('h1 a').text(), + link, + author: $('.page-header a[rel="author"]').text(), + pubDate: parseDate($('.page-header small').text().trim().split(' by', 1)[0], 'MMMM D, YYYY'), + itunes_item_image: $('#page img').first().attr('src'), + content: { + html: $('#page > div > p') + .toArray() + .map((x) => $(x).prop('outerHTML')) + .join(''), + }, + }; + }) + ) + ); + + return { + title: 'iRacing News', + link: url, + item: items, + }; + }, +}; From 79e3c245d4d67a5e5f88a0e1bee9c7a19de5cc13 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 11 Sep 2026 11:22:15 +0800 Subject: [PATCH 0763/1190] docs(route/javdb): update session cookie requirement for tags Close #23249 --- lib/routes/javdb/tags.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/javdb/tags.ts b/lib/routes/javdb/tags.ts index e50be6f3114f..1512a89528b5 100644 --- a/lib/routes/javdb/tags.ts +++ b/lib/routes/javdb/tags.ts @@ -12,7 +12,7 @@ export const route: Route = { { name: 'JAVDB_SESSION', description: 'JavDB登陆后的session值,可在控制台的cookie下查找 `_jdb_session` 的值,即可获取', - optional: true, + optional: false, }, ], requirePuppeteer: false, From 1a5562bea2ed741d581c2506da3a6a747bbbaac4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 08:21:10 +0000 Subject: [PATCH 0764/1190] chore(deps): bump tldts from 7.4.11 to 7.4.12 (#23268) Bumps [tldts](https://github.com/remusao/tldts) from 7.4.11 to 7.4.12. - [Release notes](https://github.com/remusao/tldts/releases) - [Changelog](https://github.com/remusao/tldts/blob/master/CHANGELOG.md) - [Commits](https://github.com/remusao/tldts/compare/v7.4.11...v7.4.12) --- updated-dependencies: - dependency-name: tldts dependency-version: 7.4.12 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3f1b2b3c6f68..c6b4d2d5ea92 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "source-map": "0.8.0", "teleproto": "1.228.5", "title": "4.0.1", - "tldts": "7.4.11", + "tldts": "7.4.12", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", "tsx": "4.23.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b29ca80761e6..cb70b460c232 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -245,8 +245,8 @@ importers: specifier: 4.0.1 version: 4.0.1 tldts: - specifier: 7.4.11 - version: 7.4.11 + specifier: 7.4.12 + version: 7.4.12 tosource: specifier: 2.0.0-alpha.3 version: 2.0.0-alpha.3 @@ -5875,11 +5875,11 @@ packages: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true - tldts-core@7.4.11: - resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} + tldts-core@7.4.12: + resolution: {integrity: sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==} - tldts@7.4.11: - resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} + tldts@7.4.12: + resolution: {integrity: sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==} hasBin: true to-no-case@1.0.2: @@ -11650,11 +11650,11 @@ snapshots: tlds@1.261.0: {} - tldts-core@7.4.11: {} + tldts-core@7.4.12: {} - tldts@7.4.11: + tldts@7.4.12: dependencies: - tldts-core: 7.4.11 + tldts-core: 7.4.12 to-no-case@1.0.2: {} @@ -11675,7 +11675,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.11 + tldts: 7.4.12 tr46@0.0.3: {} From 54c3dd4a65f032a88515bdedf2ba7dea4cbd8de3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 08:22:13 +0000 Subject: [PATCH 0765/1190] chore(deps): bump the hono group with 3 updates (#23264) Bumps the hono group with 3 updates: [@hono/zod-openapi](https://github.com/honojs/middleware/tree/HEAD/packages/zod-openapi), [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono) and [hono](https://github.com/honojs/hono). Updates `@hono/zod-openapi` from 1.6.2 to 1.6.3 - [Release notes](https://github.com/honojs/middleware/releases) - [Changelog](https://github.com/honojs/middleware/blob/main/packages/zod-openapi/CHANGELOG.md) - [Commits](https://github.com/honojs/middleware/commits/@hono/zod-openapi@1.6.3/packages/zod-openapi) Updates `@scalar/hono-api-reference` from 0.12.0 to 0.12.1 - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) Updates `hono` from 4.13.5 to 4.13.7 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.13.5...v4.13.7) --- updated-dependencies: - dependency-name: "@hono/zod-openapi" dependency-version: 1.6.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.12.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono - dependency-name: hono dependency-version: 4.13.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 ++-- pnpm-lock.yaml | 92 +++++++++++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index c6b4d2d5ea92..99cfc088e017 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@googleapis/youtube": "35.0.0", "@honeybadger-io/js": "6.16.1", "@hono/node-server": "2.1.1", - "@hono/zod-openapi": "1.6.2", + "@hono/zod-openapi": "1.6.3", "@jocmp/mercury-parser": "3.1.0", "@notionhq/client": "5.26.0", "@opentelemetry/api": "1.9.1", @@ -77,7 +77,7 @@ "@opentelemetry/sdk-trace-base": "2.11.0", "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.12.0", + "@scalar/hono-api-reference": "0.12.1", "@sentry/node": "10.73.0", "acorn": "8.18.0", "cheerio": "1.2.0", @@ -93,7 +93,7 @@ "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", "header-generator": "2.1.88", - "hono": "4.13.5", + "hono": "4.13.7", "html-to-text": "10.0.1", "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb70b460c232..0e47aebbb28c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,10 +51,10 @@ importers: version: 6.16.1 '@hono/node-server': specifier: 2.1.1 - version: 2.1.1(hono@4.13.5) + version: 2.1.1(hono@4.13.7) '@hono/zod-openapi': - specifier: 1.6.2 - version: 1.6.2(hono@4.13.5)(zod@4.5.4) + specifier: 1.6.3 + version: 1.6.3(hono@4.13.7)(zod@4.5.4) '@jocmp/mercury-parser': specifier: 3.1.0 version: 3.1.0 @@ -86,8 +86,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.12.0 - version: 0.12.0(hono@4.13.5) + specifier: 0.12.1 + version: 0.12.1(hono@4.13.7) '@sentry/node': specifier: 10.73.0 version: 10.73.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) @@ -134,8 +134,8 @@ importers: specifier: 2.1.88 version: 2.1.88 hono: - specifier: 4.13.5 - version: 4.13.5 + specifier: 4.13.7 + version: 4.13.7 html-to-text: specifier: 10.0.1 version: 10.0.1 @@ -1280,8 +1280,8 @@ packages: peerDependencies: hono: ^4 - '@hono/zod-openapi@1.6.2': - resolution: {integrity: sha512-ktnBqVgvIlopiiB9ab6hqoYefhcb0DUZU5QJ1F5UtDu8w6+cIORENz93sTZT6G0mQNes0/TbbadfrOpdehioPA==} + '@hono/zod-openapi@1.6.3': + resolution: {integrity: sha512-VYR82Y60plu8g3pN60OUXMVKpmxh3R04Qs++3DY9vtwZ9/sA/BRY0PcRIR+Z37/3itWtM7pYwT9bKiH9JhqV6Q==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.10.0' @@ -2654,26 +2654,26 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.3.10': - resolution: {integrity: sha512-FlUByzYhsas54zIvsiTJ7UgAZiVUQmErWvrQm+CKTBnIvSmIb5eWowJ4/O0bbkUa8rsOIdmCcX+HtX+EDdf0CA==} + '@scalar/client-side-rendering@0.4.0': + resolution: {integrity: sha512-a8OUod1LZbNqkPv73ijRPL3MH2/3E92I5Awa9mGXtCaIVavY3JcF7+qZW4GOtffihTfuD/CxhZ+CnQhCZds2oQ==} engines: {node: '>=22'} - '@scalar/helpers@0.11.2': - resolution: {integrity: sha512-IgHW/hIj1XAvsPIBKiOgmK87qbyDjaQQg9S/auXU4MUtvnwKKpOeIOJc0NC71FkMdr4Mok0SSVI748cmlptoXQ==} + '@scalar/helpers@0.11.3': + resolution: {integrity: sha512-4zPzuNTXObDUtZS93xzAoK83ddHDgBGifv/vKFe6bHqEl5i+IMLTTtEHOaaOZK4dusPEXcXsU5TBSaY/I6Mi+A==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.12.0': - resolution: {integrity: sha512-K8WA0NTDnwCmTvh33AmkHuoolDuxEW7Jst/Am7+tcijPgFcsGE0l3O7Vk9RkNMMQT643Vy9nJDyhfnkMIWA5vQ==} + '@scalar/hono-api-reference@0.12.1': + resolution: {integrity: sha512-RbAMvXihbZLFn3aFLQ2cW/AVIyPILujCUbaBcFVBztL6mbcDk87H10xCa5Bl5xVKalmoJwnUIV+8zMxNizh07w==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/schemas@0.8.4': - resolution: {integrity: sha512-gbx/UjAswjJc+OZp/vcVRIopjqoJ62pOkrKLvVHa1MuMRoVxcPyNzW2/SQmnvdJku7C7a6PsKaPUQ6gZXGA4eg==} + '@scalar/schemas@0.9.0': + resolution: {integrity: sha512-yYRlIWzw+7HuIX4z7rk7tg4y1nERwvvWKbolZOm7LveSTrppllGKyjtnIqS5uXsmJddERxuurSgDW224gGJlFQ==} engines: {node: '>=22'} - '@scalar/types@0.18.3': - resolution: {integrity: sha512-hPLLxVt/ah4RpCVp5UrLEpnBEgTwf+RvPtRiSEty3QqfBJUfADMXHz+oWK6UAa/vALg2AWuJLCD8hRlyQ2ET9w==} + '@scalar/types@0.19.0': + resolution: {integrity: sha512-EKeoWgUlP+uepbM/zEHKbsdpBNyOmSrw6DdU/WC0p53gz5uRzI7d/IEecIP9SQMUkResPR9DmWeWfFQftG7vqg==} engines: {node: '>=22'} '@scalar/validation@0.6.3': @@ -3754,7 +3754,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -4307,8 +4307,8 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.13.5: - resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} + hono@4.13.7: + resolution: {integrity: sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==} engines: {node: '>=16.9.0'} hookable@6.1.1: @@ -6020,6 +6020,10 @@ packages: resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} engines: {node: '>=20'} + type-fest@5.9.0: + resolution: {integrity: sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==} + engines: {node: '>=20'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -7058,21 +7062,21 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.1.1(hono@4.13.5)': + '@hono/node-server@2.1.1(hono@4.13.7)': dependencies: - hono: 4.13.5 + hono: 4.13.7 - '@hono/zod-openapi@1.6.2(hono@4.13.5)(zod@4.5.4)': + '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.5.4)': dependencies: '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.5.4) - '@hono/zod-validator': 0.9.1(hono@4.13.5)(zod@4.5.4) - hono: 4.13.5 + '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.5.4) + hono: 4.13.7 openapi3-ts: 4.6.1 zod: 4.5.4 - '@hono/zod-validator@0.9.1(hono@4.13.5)(zod@4.5.4)': + '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.5.4)': dependencies: - hono: 4.13.5 + hono: 4.13.7 zod: 4.5.4 '@humanfs/core@0.19.2': @@ -8027,29 +8031,29 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.3.10': + '@scalar/client-side-rendering@0.4.0': dependencies: - '@scalar/schemas': 0.8.4 - '@scalar/types': 0.18.3 + '@scalar/schemas': 0.9.0 + '@scalar/types': 0.19.0 '@scalar/validation': 0.6.3 - '@scalar/helpers@0.11.2': {} + '@scalar/helpers@0.11.3': {} - '@scalar/hono-api-reference@0.12.0(hono@4.13.5)': + '@scalar/hono-api-reference@0.12.1(hono@4.13.7)': dependencies: - '@scalar/client-side-rendering': 0.3.10 - hono: 4.13.5 + '@scalar/client-side-rendering': 0.4.0 + hono: 4.13.7 - '@scalar/schemas@0.8.4': + '@scalar/schemas@0.9.0': dependencies: - '@scalar/helpers': 0.11.2 + '@scalar/helpers': 0.11.3 '@scalar/validation': 0.6.3 - '@scalar/types@0.18.3': + '@scalar/types@0.19.0': dependencies: - '@scalar/helpers': 0.11.2 + '@scalar/helpers': 0.11.3 nanoid: 5.1.16 - type-fest: 5.8.0 + type-fest: 5.9.0 zod: 4.5.4 '@scalar/validation@0.6.3': {} @@ -9736,7 +9740,7 @@ snapshots: hmacsha1@1.0.0: {} - hono@4.13.5: {} + hono@4.13.7: {} hookable@6.1.1: {} @@ -11763,6 +11767,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.9.0: + dependencies: + tagged-tag: 1.0.0 + type@2.7.3: {} typescript@6.0.3: {} From 1fb1763ea115a0a478ca5d86371eee8fc5907780 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 08:28:00 +0000 Subject: [PATCH 0766/1190] chore(deps): bump undici from 8.10.1 to 8.10.2 (#23269) Bumps [undici](https://github.com/nodejs/undici) from 8.10.1 to 8.10.2. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.10.1...v8.10.2) --- updated-dependencies: - dependency-name: undici dependency-version: 8.10.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 44 +++++++++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 99cfc088e017..1cce683e37b1 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "tsx": "4.23.13", "twitter-api-v2": "1.29.1", "ufo": "1.6.4", - "undici": "8.10.1", + "undici": "8.10.2", "uuid": "14.0.2", "vm2": "3.12.1", "winston": "3.19.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e47aebbb28c..b241fb168a56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -141,7 +141,7 @@ importers: version: 10.0.1 http-cookie-agent: specifier: 8.0.0 - version: 8.0.0(tough-cookie@6.0.2)(undici@8.10.1) + version: 8.0.0(tough-cookie@6.0.2)(undici@8.10.2) https-proxy-agent: specifier: 9.1.0 version: 9.1.0 @@ -263,8 +263,8 @@ importers: specifier: 1.6.4 version: 1.6.4 undici: - specifier: 8.10.1 - version: 8.10.1 + specifier: 8.10.2 + version: 8.10.2 uuid: specifier: 14.0.2 version: 14.0.2 @@ -418,7 +418,7 @@ importers: version: 2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 - version: 1.0.30(undici@8.10.1)(utf-8-validate@5.0.10) + version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) oxc-parser: specifier: 0.148.0 version: 0.148.0 @@ -6055,16 +6055,20 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@6.28.0: - resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} + undici@6.28.1: + resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} engines: {node: '>=18.17'} undici@7.29.0: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} - undici@8.10.1: - resolution: {integrity: sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==} + undici@7.29.1: + resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} + engines: {node: '>=20.18.1'} + + undici@8.10.2: + resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} engines: {node: '>=22.19.0'} unenv@2.0.0-rc.24: @@ -6528,17 +6532,17 @@ snapshots: '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 - undici: 6.28.0 + undici: 6.28.1 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.28.0 + undici: 6.28.1 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.28.0 + undici: 6.28.1 '@actions/io@3.0.2': {} @@ -8814,7 +8818,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.29.0 + undici: 7.29.1 whatwg-mimetype: 4.0.0 chownr@3.0.0: {} @@ -9780,12 +9784,12 @@ snapshots: http-cache-semantics@4.2.0: {} - http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.10.1): + http-cookie-agent@8.0.0(tough-cookie@6.0.2)(undici@8.10.2): dependencies: agent-base: 9.0.0 tough-cookie: 6.0.2 optionalDependencies: - undici: 8.10.1 + undici: 8.10.2 http-proxy-agent@9.1.0: dependencies: @@ -10051,7 +10055,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.2 - undici: 8.10.1 + undici: 8.10.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10715,13 +10719,13 @@ snapshots: dependencies: write-file-atomic: 1.3.4 - node-network-devtools@1.0.30(undici@8.10.1)(utf-8-validate@5.0.10): + node-network-devtools@1.0.30(undici@8.10.2)(utf-8-validate@5.0.10): dependencies: bufferutil: 4.1.0 iconv-lite: 0.7.3 inspector: 0.5.0 open: 8.4.2 - undici: 8.10.1 + undici: 8.10.2 ws: 8.21.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -11813,11 +11817,13 @@ snapshots: undici-types@8.3.0: {} - undici@6.28.0: {} + undici@6.28.1: {} undici@7.29.0: {} - undici@8.10.1: {} + undici@7.29.1: {} + + undici@8.10.2: {} unenv@2.0.0-rc.24: dependencies: From 51b0aeab4160a8b362c7cad341105b8152fa9f53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:02:49 +0800 Subject: [PATCH 0767/1190] chore(deps): bump the nix group with 2 updates (#23274) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `a7014c6` to `190959a` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/a7014c60a08855bdc9a4fddba220c28087afc21a...190959a9a4bb52d4802f076a90c3c4e3aa2e6fa2) Updates `nixpkgs` from `c043004` to `dc5d91f` - [Commits](https://github.com/NixOS/nixpkgs/compare/c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0...dc5d91f840324650bac8c379428c7037a416959a) --- updated-dependencies: - dependency-name: devenv dependency-version: 190959a9a4bb52d4802f076a90c3c4e3aa2e6fa2 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: dc5d91f840324650bac8c379428c7037a416959a dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 3cabca59e7c4..968723e169aa 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1788757012, - "narHash": "sha256-ILhsVKEZdjjVbzbcM9lx4kX8Jz4JZ/LhZcPcKr/gFvE=", + "lastModified": 1788819331, + "narHash": "sha256-PXClmncMTmWutJvFGrA6rKyvxWtUwaMUT615XrDHdB8=", "owner": "cachix", "repo": "devenv", - "rev": "a7014c60a08855bdc9a4fddba220c28087afc21a", + "rev": "190959a9a4bb52d4802f076a90c3c4e3aa2e6fa2", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1788614874, - "narHash": "sha256-7QYjT2vHLuX9Z1pdxHXDKCbh1CR3D/2rywB9Tx0MPRg=", + "lastModified": 1788752844, + "narHash": "sha256-VaWGJ6+cIYN2erfSecbRV+4ljI185Ty2wUrXyvQbgOw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0", + "rev": "dc5d91f840324650bac8c379428c7037a416959a", "type": "github" }, "original": { From 488b672316053fdfdfd810fd0cfb4ec2bdf464f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:05:35 +0800 Subject: [PATCH 0768/1190] chore(deps-dev): bump lint-staged from 17.4.1 to 17.5.0 (#23273) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 17.4.1 to 17.5.0. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.4.1...v17.5.0) --- updated-dependencies: - dependency-name: lint-staged dependency-version: 17.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 1cce683e37b1..fd29ef4d7aff 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,7 @@ "globals": "17.12.0", "husky": "9.1.7", "js-beautify": "2.0.3", - "lint-staged": "17.4.1", + "lint-staged": "17.5.0", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b241fb168a56..ef87d59f85ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,8 +408,8 @@ importers: specifier: 2.0.3 version: 2.0.3 lint-staged: - specifier: 17.4.1 - version: 17.4.1 + specifier: 17.5.0 + version: 17.5.0 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -4757,8 +4757,8 @@ packages: linkify-it@6.1.0: resolution: {integrity: sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==} - lint-staged@17.4.1: - resolution: {integrity: sha512-FmJeudcalbSfg1du+JCfvi5vS6Qt08KgbfLWiHinbef+2JJwUZwAWVoaO1AcJVUTWPfk0t30PMQNwPAeCzYQ+Q==} + lint-staged@17.5.0: + resolution: {integrity: sha512-ah2qsNtvKP1+Ak4rAvEEIvcXDLjjbr/xmxCvlequxqEOFYk0qINcZcRxD3Ic8wRn7/oQ8+wC9s0DfDrdMVCRFg==} engines: {node: '>=22.22.1'} hasBin: true @@ -10199,11 +10199,11 @@ snapshots: dependencies: uc.micro: 3.0.0 - lint-staged@17.4.1: + lint-staged@17.5.0: dependencies: picomatch: 4.0.7 string-argv: 0.3.2 - tinyexec: 1.3.0 + tinyexec: 1.3.1 optionalDependencies: yaml: 2.9.0 From 92a97b9d3f15afbd7054b4a3fc03388c06ec278a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:08:31 +0800 Subject: [PATCH 0769/1190] chore(deps-dev): bump the cloudflare group with 2 updates (#23263) Bumps the cloudflare group with 2 updates: [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/workers-types` from 5.20260907.1 to 5.20260908.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.129.0 to 4.129.1 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.129.1/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260908.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.129.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 131 +++++-------------------------------------------- 2 files changed, 14 insertions(+), 121 deletions(-) diff --git a/package.json b/package.json index fd29ef4d7aff..2fb07d657d65 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", "@cloudflare/vitest-plugin": "1.1.5", - "@cloudflare/workers-types": "5.20260907.1", + "@cloudflare/workers-types": "5.20260908.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.81.0", "@stylistic/eslint-plugin": "5.10.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.3", "vitest": "4.1.11", - "wrangler": "4.129.0", + "wrangler": "4.129.1", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef87d59f85ba..2a2cf8d942c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,10 +304,10 @@ importers: version: 1.3.6 '@cloudflare/vitest-plugin': specifier: 1.1.5 - version: 1.1.5(@cloudflare/workers-types@5.20260907.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + version: 1.1.5(@cloudflare/workers-types@5.20260908.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260907.1 - version: 5.20260907.1 + specifier: 5.20260908.1 + version: 5.20260908.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) @@ -471,8 +471,8 @@ importers: specifier: 4.1.11 version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: - specifier: 4.129.0 - version: 4.129.0(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.129.1 + version: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -604,68 +604,38 @@ packages: '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260903.1': - resolution: {integrity: sha512-FG+4mGxAXhKiL/1temH42alevIkumtYXNidhTa//3yULpzux6APw5UNVocI/vCQ1yG1YOEZRfbVy8lyuipM9MQ==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - '@cloudflare/workerd-darwin-64@1.20260907.1': resolution: {integrity: sha512-Xaum46kviN8xixl0axXm7mfLTf2GAG+baotnEIv8M5EbvyHxHagvS5Z8bU5B/WJlTWwwvGzItFfKZvga78o3ew==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260903.1': - resolution: {integrity: sha512-o241VefnjG8eG+kGap5CjgV4zOT5UaAmS3OR1VZCpNj7vkXGxvp9KftKvtQgcCsIqJKaKx+7Xd7xq0L1DjkfPQ==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260907.1': resolution: {integrity: sha512-P2o7xpZbxvoES9xY5uHqJPXvsWBWi0w1STCGoYLqxI1rh1gSUdlr4HruMiWq/TA94PPoQCg0eUfoshyQxMdGpA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260903.1': - resolution: {integrity: sha512-/VEvvtQ/XKf6HlBbg6FbvpwcpfYUcx6Fv6RkASY8DyEmUyuJ8rc7Qxil83ClRFoBzz/GY0BV94UZ6F+wers/hg==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - '@cloudflare/workerd-linux-64@1.20260907.1': resolution: {integrity: sha512-4aXjFMtD76FgVmDm7vUtl96LWNmFScfrYZ9lA6JpP5+svRfUQOYaJKcYK4g9IVmAxemItJizTTIA8jhEZaw0vg==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260903.1': - resolution: {integrity: sha512-OWhihGC6KoTXF4u2C1AonfpgXeM4/7p/1IXuALqXESmFUpLLP5gZhRzjSk/gWW+mrCZDfSrvnjifl+lRqselbA==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260907.1': resolution: {integrity: sha512-nex+emDfyOYnQlhfPoCfLYQN6L+Ng0fVg5f3+8Ot4XRZaKJUzpiBFiRkgpoUuEN47U8Pm8BwhhOK2dQwtKwivQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260903.1': - resolution: {integrity: sha512-soPMF9/aMHlHKK7M0vq5HrRRicPbnZO1F6ZZ7JWN8EltxWJU5L7CEq7CxjO878DzyPx7gYvqBFy3SVgdZKZjMQ==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - '@cloudflare/workerd-windows-64@1.20260907.1': resolution: {integrity: sha512-/ZHtEmpdSUKZyQCBUSqmGp+I7GwWVR2eUaGE5NzFJIu31HBsoQEakXaDpTrbwXydpBzpI9N0DC83W7qekkGPMQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260907.1': - resolution: {integrity: sha512-HYI7eFb/MOcNzvbUW7ZwHVZL5YFXb4r6yHN3vx8gJCqde7yhn3bhuPKiQPw90UtGWWp7uOI+Que3kf8Svjn5cw==} + '@cloudflare/workers-types@5.20260908.1': + resolution: {integrity: sha512-cILmYEd/YtL+Hlwytc5n+QVV8F+E5doQOtc6QiBtPb51kK+5fkk909db+G8dxeUsMd2ytJgdpAt/lyciUEobcw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4994,10 +4964,6 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@5.20260903.0-alpha: - resolution: {integrity: sha512-VCZIFxOqFXeibRBJWwTlppqbv2lkeO10IX3QBRGK9j1QiMAfH/OSsCvbjp1MslBMhVZmy2VTRlVaVnsKnbDp6A==} - engines: {node: '>=22.0.0'} - miniflare@5.20260907.0-alpha: resolution: {integrity: sha512-56F13BeJQuDbTxcoGGN4MGxN8oNNIH3pK48VyJ1JGmjbzE9MMu6S2kxoON3nOBHlLdNjXyzg1Rz3jdHx+JpuZA==} engines: {node: '>=22.0.0'} @@ -6348,26 +6314,11 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260903.1: - resolution: {integrity: sha512-xJzt2RnCy7ulOULmZy/4JLbEPg1uisp9lVoOUsEz+UVhDsTmrSQ0rBXZMGcXuhr2HCGKs89bg8nnbbzvBSX1Ig==} - engines: {node: '>=16'} - hasBin: true - workerd@1.20260907.1: resolution: {integrity: sha512-MN/jgZkg2y9ZOOXdN9ecYynRFaqTCRkmpt3jS6LQNPoSx8HxTgcG947SduxJEB38YisB7RJ3Gu/y7fpd7fZ5bA==} engines: {node: '>=16'} hasBin: true - wrangler@4.129.0: - resolution: {integrity: sha512-PGPvs9UPoFrwxT0VogpESSZGvZIctAuTK3wGsLLPHtHsSgS85kNdvtpa2d14UzG8gwLWD64XUGFPGG9tOXG9VQ==} - engines: {node: '>=22.0.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^5.20260903.1 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true - wrangler@4.129.1: resolution: {integrity: sha512-u1Q8dMAczlmj41K0xk5LkfcHHmbwq5xZRctlyrErVqpcoGsSIW07i74phv3B9xt6mTEO1dX3U5d9WVxR3GFRfA==} engines: {node: '>=22.0.0'} @@ -6649,19 +6600,13 @@ snapshots: '@cloudflare/playwright@1.3.6': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260903.1 - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: workerd: 1.20260907.1 - '@cloudflare/vitest-plugin@1.1.5(@cloudflare/workers-types@5.20260907.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.5(@cloudflare/workers-types@5.20260908.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -6669,44 +6614,29 @@ snapshots: esbuild: 0.28.1 miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) - wrangler: 4.129.1(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260903.1': - optional: true - '@cloudflare/workerd-darwin-64@1.20260907.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260903.1': - optional: true - '@cloudflare/workerd-darwin-arm64@1.20260907.1': optional: true - '@cloudflare/workerd-linux-64@1.20260903.1': - optional: true - '@cloudflare/workerd-linux-64@1.20260907.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260903.1': - optional: true - '@cloudflare/workerd-linux-arm64@1.20260907.1': optional: true - '@cloudflare/workerd-windows-64@1.20260903.1': - optional: true - '@cloudflare/workerd-windows-64@1.20260907.1': optional: true - '@cloudflare/workers-types@5.20260907.1': {} + '@cloudflare/workers-types@5.20260908.1': {} '@colors/colors@1.6.0': {} @@ -10607,18 +10537,6 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260903.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.2 - undici: 7.29.0 - workerd: 1.20260903.1 - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - youch: 4.1.0-beta.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - miniflare@5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -12078,14 +11996,6 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260903.1: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260903.1 - '@cloudflare/workerd-darwin-arm64': 1.20260903.1 - '@cloudflare/workerd-linux-64': 1.20260903.1 - '@cloudflare/workerd-linux-arm64': 1.20260903.1 - '@cloudflare/workerd-windows-64': 1.20260903.1 - workerd@1.20260907.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260907.1 @@ -12094,24 +12004,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260907.1 '@cloudflare/workerd-windows-64': 1.20260907.1 - wrangler@4.129.0(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): - dependencies: - '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260903.1) - blake3-wasm: 2.1.5 - esbuild: 0.28.1 - miniflare: 5.20260903.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20260903.1 - optionalDependencies: - '@cloudflare/workers-types': 5.20260907.1 - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - wrangler@4.129.1(@cloudflare/workers-types@5.20260907.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1) @@ -12122,7 +12015,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260907.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260907.1 + '@cloudflare/workers-types': 5.20260908.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil From 15baccc98cbc143861fc9bd045185d6a6824c5d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:26:47 +0000 Subject: [PATCH 0770/1190] chore(deps-dev): bump @types/node from 26.4.1 to 26.5.0 (#23272) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.4.1 to 26.5.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 144 ++++++++++++++++++++++++------------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index 2fb07d657d65..15261dd47cc7 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", - "@types/node": "26.4.1", + "@types/node": "26.5.0", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.69.0", "@typescript-eslint/parser": "8.69.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a2cf8d942c0..f2f8e4646f8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,8 +348,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.4.1 - version: 26.4.1 + specifier: 26.5.0 + version: 26.5.0 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -376,7 +376,7 @@ importers: version: 10.10.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.4.1)(eslint@10.10.0(jiti@2.7.0)) + version: 9.1.1(@types/node@26.5.0)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) @@ -415,7 +415,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) @@ -466,10 +466,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: specifier: 4.129.1 version: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -2819,8 +2819,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.4.1': - resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} + '@types/node@26.5.0': + resolution: {integrity: sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -3724,7 +3724,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -6018,8 +6018,8 @@ packages: undici-types@8.10.2: resolution: {integrity: sha512-7/+aSjzkUoLc92hV22bTW4aGanXf800zbwguhcICs0OAoCF9wDOE4wkopQ+SqfhXZm8mCK8gHpdTs7pZUWzK3w==} - undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} undici@6.28.1: resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} @@ -6613,7 +6613,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -7139,76 +7139,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.4.1)': + '@inquirer/checkbox@4.3.2(@types/node@26.5.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.4.1) + '@inquirer/core': 10.3.2(@types/node@26.5.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.1) + '@inquirer/type': 3.0.10(@types/node@26.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/confirm@5.1.21(@types/node@26.4.1)': + '@inquirer/confirm@5.1.21(@types/node@26.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.4.1) - '@inquirer/type': 3.0.10(@types/node@26.4.1) + '@inquirer/core': 10.3.2(@types/node@26.5.0) + '@inquirer/type': 3.0.10(@types/node@26.5.0) optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/confirm@6.1.1(@types/node@26.4.1)': + '@inquirer/confirm@6.1.1(@types/node@26.5.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.4.1) - '@inquirer/type': 4.0.7(@types/node@26.4.1) + '@inquirer/core': 11.2.1(@types/node@26.5.0) + '@inquirer/type': 4.0.7(@types/node@26.5.0) optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/core@10.3.2(@types/node@26.4.1)': + '@inquirer/core@10.3.2(@types/node@26.5.0)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.1) + '@inquirer/type': 3.0.10(@types/node@26.5.0) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/core@11.2.1(@types/node@26.4.1)': + '@inquirer/core@11.2.1(@types/node@26.5.0)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.4.1) + '@inquirer/type': 4.0.7(@types/node@26.5.0) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.4.1)': + '@inquirer/select@4.4.2(@types/node@26.5.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.4.1) + '@inquirer/core': 10.3.2(@types/node@26.5.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.4.1) + '@inquirer/type': 3.0.10(@types/node@26.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/type@3.0.10(@types/node@26.4.1)': + '@inquirer/type@3.0.10(@types/node@26.5.0)': optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 - '@inquirer/type@4.0.7(@types/node@26.4.1)': + '@inquirer/type@4.0.7(@types/node@26.5.0)': optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@ioredis/commands@2.0.0': {} @@ -8085,7 +8085,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/caseless@0.12.5': {} @@ -8098,7 +8098,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/crypto-js@4.2.2': {} @@ -8119,11 +8119,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/express-serve-static-core@5.1.3': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8137,7 +8137,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/html-to-text@9.0.4': {} @@ -8149,7 +8149,7 @@ snapshots: '@types/jsdom@30.0.0': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/tough-cookie': 4.0.5 parse5: 8.0.1 undici-types: 8.10.2 @@ -8160,7 +8160,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/jsrsasign@10.5.15': {} @@ -8172,9 +8172,9 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.4.1': + '@types/node@26.5.0': dependencies: - undici-types: 8.3.0 + undici-types: 8.9.0 '@types/qs@6.15.1': {} @@ -8188,7 +8188,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8198,16 +8198,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@types/statuses@2.0.6': {} @@ -8407,7 +8407,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/expect@4.1.11': dependencies: @@ -8418,14 +8418,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + msw: 2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) '@vitest/pretty-format@4.1.11': dependencies: @@ -9193,12 +9193,12 @@ snapshots: eslint: 10.10.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.4.1)(eslint@10.10.0(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.5.0)(eslint@10.10.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.4.1) - '@inquirer/confirm': 5.1.21(@types/node@26.4.1) - '@inquirer/select': 4.4.2(@types/node@26.4.1) + '@inquirer/checkbox': 4.3.2(@types/node@26.5.0) + '@inquirer/confirm': 5.1.21(@types/node@26.5.0) + '@inquirer/select': 4.4.2(@types/node@26.5.0) eslint: 10.10.0(jiti@2.7.0) eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) optionator: 0.9.4 @@ -9966,7 +9966,7 @@ snapshots: jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 - '@types/node': 26.4.1 + '@types/node': 26.5.0 jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: @@ -10573,9 +10573,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.4.1) + '@inquirer/confirm': 6.1.1(@types/node@26.5.0) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -11029,7 +11029,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.4.1 + '@types/node': 26.5.0 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -11733,7 +11733,7 @@ snapshots: undici-types@8.10.2: {} - undici-types@8.3.0: {} + undici-types@8.9.0: {} undici@6.28.1: {} @@ -11864,16 +11864,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): + vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11882,17 +11882,17 @@ snapshots: rollup: 4.63.1 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.4.1 + '@types/node': 26.5.0 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 yaml: 2.9.0 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.4.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.4.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11909,11 +11909,11 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.4.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.4.1 + '@types/node': 26.5.0 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: From f70d7171ec2a710e647e9a02424637b5c173a33e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:47:54 +0800 Subject: [PATCH 0771/1190] chore(deps-dev): bump the typescript-eslint group across 1 directory with 2 updates (#23266) Bumps the typescript-eslint group with 2 updates in the / directory: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 8.69.0 to 8.70.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.69.0 to 8.70.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.70.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint - dependency-name: "@typescript-eslint/parser" dependency-version: 8.70.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: typescript-eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 120 ++++++++++++++++++++++++------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 15261dd47cc7..3b28dea757bc 100644 --- a/package.json +++ b/package.json @@ -168,8 +168,8 @@ "@types/module-alias": "2.0.4", "@types/node": "26.5.0", "@types/sanitize-html": "2.16.1", - "@typescript-eslint/eslint-plugin": "8.69.0", - "@typescript-eslint/parser": "8.69.0", + "@typescript-eslint/eslint-plugin": "8.70.0", + "@typescript-eslint/parser": "8.70.0", "@vercel/nft": "1.11.0", "@vitest/coverage-v8": "4.1.11", "discord-api-types": "0.38.55", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2f8e4646f8c..0686ad150bab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -354,11 +354,11 @@ importers: specifier: 2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: 8.69.0 - version: 8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + specifier: 8.70.0 + version: 8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@typescript-eslint/parser': - specifier: 8.69.0 - version: 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + specifier: 8.70.0 + version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 version: 1.11.0(rollup@4.63.1) @@ -2861,39 +2861,39 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.69.0': - resolution: {integrity: sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==} + '@typescript-eslint/eslint-plugin@8.70.0': + resolution: {integrity: sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.69.0 + '@typescript-eslint/parser': ^8.70.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.69.0': - resolution: {integrity: sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==} + '@typescript-eslint/parser@8.70.0': + resolution: {integrity: sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.69.0': - resolution: {integrity: sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==} + '@typescript-eslint/project-service@8.70.0': + resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.69.0': - resolution: {integrity: sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==} + '@typescript-eslint/scope-manager@8.70.0': + resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.69.0': - resolution: {integrity: sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==} + '@typescript-eslint/tsconfig-utils@8.70.0': + resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.69.0': - resolution: {integrity: sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==} + '@typescript-eslint/type-utils@8.70.0': + resolution: {integrity: sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2903,25 +2903,25 @@ packages: resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.69.0': - resolution: {integrity: sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==} + '@typescript-eslint/types@8.70.0': + resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.69.0': - resolution: {integrity: sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==} + '@typescript-eslint/typescript-estree@8.70.0': + resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.69.0': - resolution: {integrity: sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==} + '@typescript-eslint/utils@8.70.0': + resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.69.0': - resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} + '@typescript-eslint/visitor-keys@8.70.0': + resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': @@ -3724,7 +3724,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -8219,14 +8219,14 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/type-utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) - '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/parser': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/type-utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/visitor-keys': 8.70.0 eslint: 10.10.0(jiti@2.7.0) ignore: 7.0.8 natural-compare: 1.4.0 @@ -8235,41 +8235,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/visitor-keys': 8.70.0 debug: 4.4.3 eslint: 10.10.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.69.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.70.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.0 debug: 4.4.3 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.69.0': + '@typescript-eslint/scope-manager@8.70.0': dependencies: - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 - '@typescript-eslint/tsconfig-utils@8.69.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/tsconfig-utils@8.70.0(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/type-utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) debug: 4.4.3 eslint: 10.10.0(jiti@2.7.0) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8279,14 +8279,14 @@ snapshots: '@typescript-eslint/types@8.65.0': {} - '@typescript-eslint/types@8.69.0': {} + '@typescript-eslint/types@8.70.0': {} - '@typescript-eslint/typescript-estree@8.69.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@8.70.0(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/project-service': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/tsconfig-utils': 8.69.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/visitor-keys': 8.69.0 + '@typescript-eslint/project-service': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 debug: 4.4.3 minimatch: 10.2.6 semver: 7.8.5 @@ -8296,20 +8296,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.69.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) eslint: 10.10.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.69.0': + '@typescript-eslint/visitor-keys@8.70.0': dependencies: - '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/types': 8.70.0 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': From 6a04152bff6773984bdec5a8faec8e7f5f226382 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:57:08 +0000 Subject: [PATCH 0772/1190] chore(deps): bump entities from 8.0.0 to 8.1.0 (#23270) Bumps [entities](https://github.com/fb55/entities) from 8.0.0 to 8.1.0. - [Release notes](https://github.com/fb55/entities/releases) - [Commits](https://github.com/fb55/entities/compare/v8.0.0...v8.1.0) --- updated-dependencies: - dependency-name: entities dependency-version: 8.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3b28dea757bc..fd1648cba0a0 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "dayjs": "1.11.23", "destr": "2.0.5", "dotenv": "17.4.2", - "entities": "8.0.0", + "entities": "8.1.0", "etag": "1.8.1", "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0686ad150bab..d665b0aaeb48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,8 +119,8 @@ importers: specifier: 17.4.2 version: 17.4.2 entities: - specifier: 8.0.0 - version: 8.0.0 + specifier: 8.1.0 + version: 8.1.0 etag: specifier: 1.8.1 version: 1.8.1 @@ -3832,8 +3832,8 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} - entities@8.0.0: - resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} engines: {node: '>=20.19.0'} error-stack-parser-es@1.0.5: @@ -8963,7 +8963,7 @@ snapshots: dependencies: domelementtype: 3.0.0 domhandler: 6.0.1 - entities: 8.0.0 + entities: 8.1.0 domelementtype@2.3.0: {} @@ -9049,7 +9049,7 @@ snapshots: entities@7.0.1: {} - entities@8.0.0: {} + entities@8.1.0: {} error-stack-parser-es@1.0.5: {} @@ -9252,7 +9252,7 @@ snapshots: ci-info: 4.4.0 core-js-compat: 3.50.0 detect-indent: 7.0.2 - entities: 8.0.0 + entities: 8.1.0 eslint: 10.10.0(jiti@2.7.0) find-up-simple: 1.0.1 globals: 17.12.0 @@ -9710,7 +9710,7 @@ snapshots: domelementtype: 3.0.0 domhandler: 6.0.1 domutils: 4.0.2 - entities: 8.0.0 + entities: 8.1.0 http-cache-semantics@4.2.0: {} @@ -10199,7 +10199,7 @@ snapshots: markdown-it@15.0.1: dependencies: argparse: 3.0.0 - entities: 8.0.0 + entities: 8.1.0 linkify-it: 6.1.0 mdurl: 2.1.0 punycode.js: 2.3.1 @@ -10906,7 +10906,7 @@ snapshots: parse5@8.0.1: dependencies: - entities: 8.0.0 + entities: 8.1.0 parseley@0.13.1: dependencies: From 0a4ecdb02390fb047bb675a1764e1dedbf5cf9bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Sep 2026 19:21:12 +0800 Subject: [PATCH 0773/1190] chore(deps-dev): bump the oxc group across 1 directory with 5 updates (#23265) Bumps the oxc group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.81.0` | `1.82.0` | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.148.0` | `0.149.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.66.0` | `0.67.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.81.0` | `1.82.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.81.0` | `1.82.0` | Updates `@oxlint/plugins` from 1.81.0 to 1.82.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.82.0/npm/oxlint-plugins) Updates `oxc-parser` from 0.148.0 to 0.149.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.149.0/napi/parser) Updates `oxfmt` from 0.66.0 to 0.67.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.67.0/npm/oxfmt) Updates `oxlint` from 1.81.0 to 1.82.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.82.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.81.0 to 1.82.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.82.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.82.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.149.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.67.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.82.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.82.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +- pnpm-lock.yaml | 523 +++++++++++++++++++++++++------------------------ 2 files changed, 269 insertions(+), 264 deletions(-) diff --git a/package.json b/package.json index fd1648cba0a0..86ee66c8bb1a 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@cloudflare/vitest-plugin": "1.1.5", "@cloudflare/workers-types": "5.20260908.1", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.81.0", + "@oxlint/plugins": "1.82.0", "@stylistic/eslint-plugin": "5.10.0", "@types/crypto-js": "4.2.2", "@types/eslint": "9.6.1", @@ -190,10 +190,10 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.148.0", - "oxfmt": "0.66.0", - "oxlint": "1.81.0", - "oxlint-plugin-eslint": "1.81.0", + "oxc-parser": "0.149.0", + "oxfmt": "0.67.0", + "oxlint": "1.82.0", + "oxlint-plugin-eslint": "1.82.0", "oxlint-tsgolint": "7.0.2001", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d665b0aaeb48..42b98c3cea11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,8 +312,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) '@oxlint/plugins': - specifier: 1.81.0 - version: 1.81.0 + specifier: 1.82.0 + version: 1.82.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.10.0(jiti@2.7.0)) @@ -420,17 +420,17 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) oxc-parser: - specifier: 0.148.0 - version: 0.148.0 + specifier: 0.149.0 + version: 0.149.0 oxfmt: - specifier: 0.66.0 - version: 0.66.0 + specifier: 0.67.0 + version: 0.67.0 oxlint: - specifier: 1.81.0 - version: 1.81.0(oxlint-tsgolint@7.0.2001) + specifier: 1.82.0 + version: 1.82.0(oxlint-tsgolint@7.0.2001) oxlint-plugin-eslint: - specifier: 1.81.0 - version: 1.81.0 + specifier: 1.82.0 + version: 1.82.0 oxlint-tsgolint: specifier: 7.0.2001 version: 7.0.2001 @@ -1806,124 +1806,124 @@ packages: '@otplib/uri@13.5.0': resolution: {integrity: sha512-LsL1hqTEgJHY40U2eb/Qp6OR1tKkNGwHQTrqqjIuJj0cawGWkLEmeUFK1MHzWBQ4bgy5IdeB5Et7gIDSAH/CRw==} - '@oxc-parser/binding-android-arm-eabi@0.148.0': - resolution: {integrity: sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==} + '@oxc-parser/binding-android-arm-eabi@0.149.0': + resolution: {integrity: sha512-OQmN5XYQ/GNRkbU0Ksb2QSaHOgKWhFw7Bz6lJvANBdwVkLzTlcHXDLj62XrNPtUf3pDxRBr7OoHJrKhHYOb9iA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.148.0': - resolution: {integrity: sha512-sg/6Ez0KdAygsu0POELux9wN1Po2CP93WY8eNl4DBKIGprsd4QSHBXOb471Pu9i2OCD5sLkISSb2agZEhVn2Zw==} + '@oxc-parser/binding-android-arm64@0.149.0': + resolution: {integrity: sha512-M7nHwfHY2Xv5Rm1iygvyOTPXrD4d5WRVw+C8eItLwoipzQBnkUMmwh3jvuGLBio2BGetfIH0F7J3Afb1Qn9dJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.148.0': - resolution: {integrity: sha512-yiSJmzGUvCUaJT8X3j40gVcX+ckuHQMuiOtF8DvzTs5+JtB/7XuHFPp4M+vv5u+HlBtDUd4Ks5pyHpWz8mfnkg==} + '@oxc-parser/binding-darwin-arm64@0.149.0': + resolution: {integrity: sha512-h6NrhqRAVKbgzfShjHoBgjeJ1vQR2o6iNM6/t8JCSej1XRKJyraV08Zz7ey6SsjPzof4ecp48Upg2vidqoDgtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.148.0': - resolution: {integrity: sha512-6ZeklaamrMy4H2JmhvcJg6iip59tYILtuLaILxyAHT3l5FDxnI5ihVievAft5ZmAbqtlWHErOi1OpJK8gy1wcA==} + '@oxc-parser/binding-darwin-x64@0.149.0': + resolution: {integrity: sha512-+WRRZeFYqVznJEEttiWNcI43Ij2iD0yphmMFkAuKNOQTyXQ+lLCAVW6aVLOD6nPO6FOymW7HtDh5KmnxZPi/2A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.148.0': - resolution: {integrity: sha512-vFsPx+a/qFECPnz/H8nC6x6MDvnWscLTCo/5muojEF54ERUq1kdgbvnWo95YnkhjF9sTIcG/uDxQBh1gffaufQ==} + '@oxc-parser/binding-freebsd-x64@0.149.0': + resolution: {integrity: sha512-kG0YUZj/4hdHBQDtIr2UZ7Nyxy3M5Et+w3A0V0eefRgtQWE4wJrNflBJMJfwFPiwMLPlPL6qJR+5JdeIqhzW/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.148.0': - resolution: {integrity: sha512-eOr3M+6iGbbxNL4PSS0VtsyQ2eOUxSBh00BqO22SbolDimPSYsBuLr/LCrZBkiqW2BoabhR6V4R8jrRAay7hjg==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.149.0': + resolution: {integrity: sha512-gOD5NhD8DG7aJ8CLZ+ve5xoDq0y0J0zKZSfkfMGfk96XLhu21IzSFvjpEvVUGodD7Yyy3Tr9rNdiPEdZjkuRqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.148.0': - resolution: {integrity: sha512-58ZKDw0mQRbCNfrd2IDyV4o8T7enzGERJn41BH2tjrZVGyiKiFzcfDicuB7Zcpb/1xIOrObovr8Dja6lZi8dLw==} + '@oxc-parser/binding-linux-arm-musleabihf@0.149.0': + resolution: {integrity: sha512-UtPYQkciY1NzYRtfKBopYfOjSxPFFVyprs0iH+Z5KZKNv9at/71pGd2KNrlmALhRcNocnxEsc+KuhY3nUlhIxg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.148.0': - resolution: {integrity: sha512-Fnu95O4eZ5i++GPvIzBEZ8y4ddTLR+D9paYa8JRaRk6ZK7nHQiWP5xtrhcPQsXqgat1d7sU/d5rbbI0p1FTHSQ==} + '@oxc-parser/binding-linux-arm64-gnu@0.149.0': + resolution: {integrity: sha512-3jWkwykjJJegMD+5dU9n30mzwvTLi99fkUqHU9Pqdzw3fIVfyn6ZnjOl8l053jogr0BGQPxagTjIKBksp288eQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.148.0': - resolution: {integrity: sha512-3CQy/BMdx7N7H3qrcPxUL+a2CwUZodUcf6oq8iJuNZ9C6Ol1aq3mcWzsgySJ7CHFLvpX21ZDPp1r1X0QLbu/AQ==} + '@oxc-parser/binding-linux-arm64-musl@0.149.0': + resolution: {integrity: sha512-H7n8zPxnLVI0pkutAxyXJNE/IaCuCJS6ghgv2cl8IDIgXCDZWjG/Bs8rF8BHKQ9gsOpA4m+T4lFaFAdnGuk/cw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.148.0': - resolution: {integrity: sha512-9LkaYvfiF8hMOw900csAvkf1oxE8XlmMeGowu5BcastSSwV8mKvKRMNU7HsV+ycyj1dQD8pX5qgOw8ja6SJacg==} + '@oxc-parser/binding-linux-ppc64-gnu@0.149.0': + resolution: {integrity: sha512-aByOTCdLYp8FjwAUUmN7YYjAmPJ/9t0I1ACuhAkYun87fajIHbwuZ/opaBk0Pnfxzp2vYyD8hdG8OQWEglP5oA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.148.0': - resolution: {integrity: sha512-2GBiM9h26dR4WJfhoMvnFMnFLf7m/kYs4UMqjvrOfQG4BV1nuTJDH22Zc2MQr3INZF7nSKYQ6xlhD3hQ7A6gug==} + '@oxc-parser/binding-linux-riscv64-gnu@0.149.0': + resolution: {integrity: sha512-SAdNs+uxMRBLPM6QuLlOxzE4duWia4EMhqGknsJS2EGnWni7i53gSZhaW3VYPxaP8nk/UsMQxMODIFwBHbRWcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.148.0': - resolution: {integrity: sha512-uPqZexvKJmEgq4mAu36qe2xTfXZE7oyik1R7KtZ5tl8qKlq1U1fIqTFRUEBZqRGvforoTrGIpatRzcoPKO66RA==} + '@oxc-parser/binding-linux-riscv64-musl@0.149.0': + resolution: {integrity: sha512-RhmzD62QNtMjuBAeKJ15gzyNRv06Yz1ZFi6lgTiA5S3ixkrSMPJWIPGyqdlC4XiWxwAW9W4jeQNV4/Om9V27gA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.148.0': - resolution: {integrity: sha512-9oUHvnTbp7ZraFsTC8PN6XhdhPSSxZumYvixWl7Smi353gEULvK6yV0sXNVrdFMHQeaDKFCi8TgDhNK7/A+Y+Q==} + '@oxc-parser/binding-linux-s390x-gnu@0.149.0': + resolution: {integrity: sha512-HqEExoeKhlfUacdhj7KEe1m93B1SzCUkF88c1qLQR5Km6GPSoMHMIjK7qmRXLzjsO+ipSTWVsnYMVG0j2/mxNA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.148.0': - resolution: {integrity: sha512-2qhDSJwKzbSZzF7lDqqk8sr/yXsmwr3PeUa4/nazIF+zFAYz1gVPEfC34GQtGxzJUUmklaYAL63368LEfrMeyw==} + '@oxc-parser/binding-linux-x64-gnu@0.149.0': + resolution: {integrity: sha512-FuAv1J7GoSkmxg5glvzlHA88Ld7dOIg23VdAsVLngPZN/ZjZPtCaBZDby1GPn63Yj+7h47/wbcd7i1p2UJTeNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.148.0': - resolution: {integrity: sha512-qQoPDZUFV0bh9xA09XydmkjMBpgc1ukJuhMvzQ9QeVmFaHTS9W5TE5CoLmSl3QQyUP9OuHO3x/WPZTIIZPWR3Q==} + '@oxc-parser/binding-linux-x64-musl@0.149.0': + resolution: {integrity: sha512-KDFOVgjMIOiMiRUw+jZlAIEZZFG0WgGk+adcldjc+/qz0/kvobnYJqDqNX0+REJbSpSsM1Knj0zrEGTQ6WTIng==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.148.0': - resolution: {integrity: sha512-1UGbaQWEXUCLqAmaR5kwRDjx/R4S5LQKZkM9CHmaHkuKhriOF32aRLfS0jCRNE2yGQJLMEA1z9UucbBVqjXnDw==} + '@oxc-parser/binding-openharmony-arm64@0.149.0': + resolution: {integrity: sha512-O3O+ZBhGEUhtbDXFWRMTuOCBg+fC2hf1wrtZBrd3VJUtV/MDd1LCwZXIHY2nMCnh9Akgjay965pBok2h5AXN0A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-win32-arm64-msvc@0.148.0': - resolution: {integrity: sha512-pWKdzRDNG2+NK4h/V6U/CYERcfYD6u28h5IB/VJVsrZaD3muvE58tUj22lieL5vLZ+XFi1GPv9YXckZbJZ9BLA==} + '@oxc-parser/binding-win32-arm64-msvc@0.149.0': + resolution: {integrity: sha512-9hMvs3GZESAcbUGU3mqXFWRLgBd8TLeosNYfHusHp/hrBnk5lIP2nJtark3hZ6qT+lqSdS79sH8NoWHj1x9iUw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.148.0': - resolution: {integrity: sha512-i3p4x+mvwtjcE1J5HM6V7ggsbXiznExN/4MkNyOy3dfXrVV3bnkSfmZxvo6/84qCVX4ShkpNE1SKt9biIF31GQ==} + '@oxc-parser/binding-win32-ia32-msvc@0.149.0': + resolution: {integrity: sha512-8hQaS1Urc1NnlutDfhQ+DwijRALGLY+AmqJSBHV4OmyPZDoqgtkmjTHl2tsTLK6PETe6PbIJLKmXcpJuqkf8Lw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.148.0': - resolution: {integrity: sha512-Ye6vB7VQulghWYkYkECOBYFRVEizz4XyRTUAv+t8BuyurhKU7uD0P9eowL+mKG5Mf8MSYx+DI3Cm8SKZvYG7bQ==} + '@oxc-parser/binding-win32-x64-msvc@0.149.0': + resolution: {integrity: sha512-xFqDgKSKdY0m2EtHQL7uMuknuEGnvbtxkWllGle5hmhVe5xn5XZ0Zb9iViAsItFAf0CjSiZ5er3XqqAlr5oARA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1931,6 +1931,9 @@ packages: '@oxc-project/types@0.148.0': resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} + '@oxc-project/types@0.149.0': + resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] @@ -2034,124 +2037,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.66.0': - resolution: {integrity: sha512-2Me9eoptv6ERdEuI2P8AOlYdHHraXebJaM6SC0kc2Dfb+mLrep2db+fedBPKaYn673h/vBgvP4tkOdAbaudX6w==} + '@oxfmt/binding-android-arm-eabi@0.67.0': + resolution: {integrity: sha512-2olh3ioEmc4gRzQm7jxyB1b/PFBoFvTq8KdgYySeNpysDtA6DEg2Mvya4/I6flhL7G0eOrE8RD7JCNCIMhE16Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.66.0': - resolution: {integrity: sha512-u7O+bSSF0HGsDKkQQxBqvLGVepu93RA+JKu+ONqvfh4sCnCEbj31wZj4iG5gk3XfRwrmYj0/8catkO2LcblQKQ==} + '@oxfmt/binding-android-arm64@0.67.0': + resolution: {integrity: sha512-ulfw8EHN1MBq/MFFDXw2/M1VAFu5mRUcnuZ8Hqbv9viAnFzO9t1jKSAsDqKYYDGMlytF/uj6Z5z5n/tHupnKhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.66.0': - resolution: {integrity: sha512-/ikyMIVjX/sdo7KtjxoEsSUosfPzveVhT9RWMx9yGqFDKFJ89JAEKuEeLBmurDjrkb4w8tOnAdSO3SBaplY3bw==} + '@oxfmt/binding-darwin-arm64@0.67.0': + resolution: {integrity: sha512-MfONZx/O2o9M5v2jDFol556G9+A+P9xCuJ4DZ+qhE+RnaCdoscy6Eu5nq1dbuNxhwdJyZ6kLI7fnG9mwEeOeGg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.66.0': - resolution: {integrity: sha512-q5xUsKeFqawa9NXa6ZGXWimFV19m8MogKPdTaSVDAAk2EQKBmBZRDeluwcl1p8ty/OFc9s9888OKEh3xfPVH0g==} + '@oxfmt/binding-darwin-x64@0.67.0': + resolution: {integrity: sha512-CYnIx5LvFVJnyJcCqwH2jxMKjFjqo5678MPjdmNFoSGMhlOvZ/xRZqvhDcolKrXc8fezW3AKh+C4wyoFuWOSSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.66.0': - resolution: {integrity: sha512-CR+x4VzMY0pRXLK/xFQ/RzsSFkP5t2Z2mef0QY6OP/rTRcMUoMLCOM62/3Fp/t0K+UDoBKxvMyeb6D0zPMjleA==} + '@oxfmt/binding-freebsd-x64@0.67.0': + resolution: {integrity: sha512-7/iF1orvIS9mxhKUqnmtMgm+OrSQ5acPwuvdQrm6ECgqbwPmC+Pw9cdke3sNfVN6pT2hbJ58+jP8BCThl5HXOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.66.0': - resolution: {integrity: sha512-ZEYmO/LbH9tTQCADILHGZE4GeOXOAj2VzedHkASNwjmwlwtutJCLpCJbIs37wRGTFgWRoEcD72jpMX+IBJUGjQ==} + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + resolution: {integrity: sha512-yy+OGys07IZOpOmYPZoObKyUQLkfxeQqeCypk+1jaZd8HGo77hzvU1Jg8X3+W75o+9lszOjBfg0nkGtlwYywXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.66.0': - resolution: {integrity: sha512-hNtR9/oU0CeTkq7JnRkmBQwqe17v2ZaAMLC4VcN7IIOWeRyWDk0knSPWS9iiLmtbZ2RRBBtsG01jQgkZmKCJeQ==} + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + resolution: {integrity: sha512-wPIeeigXgJpwNw3wydYRt3U9iN9Y/ejpOZuYL9IA7igxWs7LIQMOkhKxTumRvy6dIv0iXKk3RTw3Vmjg0i+2sg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.66.0': - resolution: {integrity: sha512-uwOVQ8i6I1LT/+eDzfsgrrcZp8Fn6NPVUPn8fF5gdFGekFf0PddF+LEuwsD0/pbNUcKZhDj2rQ5UpITh9gF4iQ==} + '@oxfmt/binding-linux-arm64-gnu@0.67.0': + resolution: {integrity: sha512-0+XNxcdbkTfxdcD4qW6Ci9n+mBNJ8xTBumnxKvKBmRFOdx0Wf8/KiHjCJayooXmYkqRpRVd98Q5egvzx5BLSgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.66.0': - resolution: {integrity: sha512-tTkF2Dmx4nGAjmBlZb+UtTGqR/EK4ZrW9qBfzte07a9XWqzoGGKzpFFlyNDhQe+Uwql94+ReCTeNbhOXscw1Dg==} + '@oxfmt/binding-linux-arm64-musl@0.67.0': + resolution: {integrity: sha512-I75LKPJyNOYUzkqAiAMIE31+Ye7xtQXZdoty1IXn4B+bw5Zpmez5wfG19ejGpNnS/BzQ7LFS+7jxuTPb+vHiZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.66.0': - resolution: {integrity: sha512-F3cKHUav4yXOHn6GFnwpBhSYsJOYKKf9eqO/9jlEuqPxNw9zb98E9ZFct79gcg8pibUGkbveEu9WDlmXJpDzKw==} + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + resolution: {integrity: sha512-c2M5iRpe1QMZSRE/UvZoPdXBWb5Ic/ycvOyNiKCqPwQ/OyOKIMiJs02ynlNnjb7ZZJnRXYLmGcohoINOcwDK3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.66.0': - resolution: {integrity: sha512-K5fDaNZfDyQMYA/3qL21bqyN0X9T15LLwwbFPt2aHc94+ZG7bh0vZEsy2y7NlRnjjHFSwN+Hzg6ldJtbOriH4Q==} + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + resolution: {integrity: sha512-dQzzYlV24Udhfm5ECuSdgqRvFJU/CGHzcYYEO3dLM6W6+CHiBFrq9OjIllkdCcPhsoSQ8o223Dja84MOSzed9A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.66.0': - resolution: {integrity: sha512-44Yc+I+qOmTElRcEhm5hUKIUJEQIOugymz4ua4tB0Wox7tGAfIbjzmXz/HDAtw1Ij6gmBwZlzh4hc9679RhWeA==} + '@oxfmt/binding-linux-riscv64-musl@0.67.0': + resolution: {integrity: sha512-rFNq1CgX4qMJANOq42LkAs90JE80GpiaEohAV2qn/gT2hGjQTW1zBO5zQBxArI4926pM1OSzo3CN0tBszGBIaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.66.0': - resolution: {integrity: sha512-1e29Eg9hEj2kRBB19M0seIehPbbXHCk35GvImjDvb79rjjYjXCRmtbUNHJcgoktZAMIzXrTbxDBKmTc1V4bg3A==} + '@oxfmt/binding-linux-s390x-gnu@0.67.0': + resolution: {integrity: sha512-Sky6rEdz2o5IGq01lPhS12yEvDdChVEcaYrcLHkveh4Fx0qPjljE/Iul6SX/bRMl6lNc8J7J/mDQdzgBdA++Pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.66.0': - resolution: {integrity: sha512-vODY1UQo10gngn0+D4xHKU84F1Twm1LqrzV4SqPXvmQKSd87paehvZ6jqA5wKs6XQrlWul9clYMDVHcoW9CPMA==} + '@oxfmt/binding-linux-x64-gnu@0.67.0': + resolution: {integrity: sha512-vPXmlNORV8AZq2Ocxh07pxwMjfENUWCV/eZArnao0qC3NO/hDeTVkQvee7SJJUbIiF5PZbBa4kYmaXnu7Rk58w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.66.0': - resolution: {integrity: sha512-YDzXx2JsT4+HL4MdkVrYjO55NS5lUKNm8rLC4ZPou8+seu0v0jhecSh+ufoO6+xEa8gccEezMlI2WHJi4ApUgw==} + '@oxfmt/binding-linux-x64-musl@0.67.0': + resolution: {integrity: sha512-x/WAtFqYtVr3vZ9ni8nr4kn9whSitg8fOljq/pZzBpxopRdY1BMLZCZkrbIbaBcYkm46qGbqVea2FCWmtQ2P9w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.66.0': - resolution: {integrity: sha512-mJjUYd8lj0+j4JkYyEM+5qKBf1Rnrpgjn/SVYKJhicVDqLz566ooa7Fs8zflPqt+dnZDV7X054rVIQX6ZcQNlQ==} + '@oxfmt/binding-openharmony-arm64@0.67.0': + resolution: {integrity: sha512-eRw9Neh4/aA6i+q/R3WU1gGQINhVM0J4fXIm6t27caOamkr/37uAkp1IdBx4zlJH97hmXR63z/q9n5c5dN7MzA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.66.0': - resolution: {integrity: sha512-soV+0vESv7e5ntCHWC61x4gg8OSak6IHHnWsZmHrJFlvMj2AK+kmldErCNkVkrvc1Ts2/++rJXn+IuAb2WMXhw==} + '@oxfmt/binding-win32-arm64-msvc@0.67.0': + resolution: {integrity: sha512-YIMvb+sGNYN2uc6+QK2HLPeEKM2vl7QZ5onQzpAJRb6pnf0DwUFP5R8tdS9R0l8hdUil2gu4Uxd0Yxrop0iT4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.66.0': - resolution: {integrity: sha512-YCPi23uRIEYuIKTZohAkKbPFpujQ5QBuUM5iDv+UqbCmTPAkaFsxjsSuB8xlBpRT0G7eP/4HMF+cPDSqHtOD9A==} + '@oxfmt/binding-win32-ia32-msvc@0.67.0': + resolution: {integrity: sha512-LzmU9MyACPzwNDIK0ItMedHPz735Ug7ELWguxo4/kuy6zWuDoeglOAEFCY8jLg0PzRpFO3hDyLFe2Gu2eFDeGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.66.0': - resolution: {integrity: sha512-bwTQcv/JVRPkOqQtMF0X7vpvpncDQiBcXHxZ9S2hR12Hlo8bvBdUR5x5XnxzDZ3kM0qoZw1rv7KaD66Ly+pFWA==} + '@oxfmt/binding-win32-x64-msvc@0.67.0': + resolution: {integrity: sha512-sbQOIDNLUEeVZcAJcSL5VURn7kfjvilPviody4Yl5n8lQCDtUm+C9oHTTwZS/m4d/Z6Vv3jNEiAofH932NPPCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2186,130 +2189,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.81.0': - resolution: {integrity: sha512-IcCRsXiedJoJopY6mpZUBEeVFsUrutmrG7dZ87zMuKJlhg70Ora9bBl1WcCxZQtyI10YpnVdEso5oCg7YcfSHw==} + '@oxlint/binding-android-arm-eabi@1.82.0': + resolution: {integrity: sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.81.0': - resolution: {integrity: sha512-GRrIPyTGVhx3L3h+0T5xT2A0jFAcdPv4+IfuXpGDLIdl6XeYhgg/zw72A5ILZoUgRqZuM8F1y+V/gfDriXSxzQ==} + '@oxlint/binding-android-arm64@1.82.0': + resolution: {integrity: sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.81.0': - resolution: {integrity: sha512-qNQ9tXRgLuKbqSV1S2h9h4KPHjbovO7RRR2/enUOtHzTkFZ7B9X5zqqHJua8dRyc7dBy7Aoyq5pqTSLFVcAzGQ==} + '@oxlint/binding-darwin-arm64@1.82.0': + resolution: {integrity: sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.81.0': - resolution: {integrity: sha512-q0QTm32jWga2Gv4j7IaVZN0jYMi9UV73sWVgFtDA4iIfqwMCLLZ3ve+9KwfYtsaKZSgQhmPaogeZWqDZpcY1Pw==} + '@oxlint/binding-darwin-x64@1.82.0': + resolution: {integrity: sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.81.0': - resolution: {integrity: sha512-/+8wVWDXEC7wHVAhOc59Fw/SkMc1arLkFD8iQCaSsmzenK1X4doFqquL9H1wrtGUzaiycVqkf/sSpcILK6W1UA==} + '@oxlint/binding-freebsd-x64@1.82.0': + resolution: {integrity: sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.81.0': - resolution: {integrity: sha512-4xt422FEgioRq9hAL4Tq7fujGUWnc8z1BJ+Oi8RN8vB8axaP+sdK6a2xdlcQCCYnJg9QMuMFS0AucuIFx/EacA==} + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + resolution: {integrity: sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.81.0': - resolution: {integrity: sha512-u3vna8KdGplH4DRCW9K54D68fcMo7IxVrkCJWwXnIhwtBdnDnYrmzOUA/XjmBlPpcLsgw9Z5BNdY4za9+Dj+MQ==} + '@oxlint/binding-linux-arm-musleabihf@1.82.0': + resolution: {integrity: sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.81.0': - resolution: {integrity: sha512-3j9k+gsYsE7nv71GWotXsqsa2l9/aJenD7dVHNt/CBvsb0SgRjSMnHFeP59IXUAl1wvVFhqGl2wJNMwWU3UBlA==} + '@oxlint/binding-linux-arm64-gnu@1.82.0': + resolution: {integrity: sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.81.0': - resolution: {integrity: sha512-k5iAp3dNxW0/uDCBY+WSm8jKB2szu7SkEQZdgRRpDXvuDd69vvDcqhB3A/pWCfCwXyenjNjFn9Td1fVoyAc+Yg==} + '@oxlint/binding-linux-arm64-musl@1.82.0': + resolution: {integrity: sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.81.0': - resolution: {integrity: sha512-TFqLja3uYmVSte6nof9GWrex9Z8WgdZrNiLC6Te5rXGDqXB2y4j/26iFhwosXiAFqDhE9JJVuuCkDKLwptTn1g==} + '@oxlint/binding-linux-ppc64-gnu@1.82.0': + resolution: {integrity: sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.81.0': - resolution: {integrity: sha512-UEcySvGS0NOVo7h7n7CYyJL9+6gFAh7Zc/ToDXVScFvzHSTIxtzkMVU30rmQ6+nQ1LF+UdiRDdJajpDu+OylLg==} + '@oxlint/binding-linux-riscv64-gnu@1.82.0': + resolution: {integrity: sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.81.0': - resolution: {integrity: sha512-H+diDbhD00+wI1IRP8Kz88x/lat+DgtoBJzoTthS16xkTJGNaEkfb8gzmd1rzc/2uDQQMl7GNl+JFUacVeWxIA==} + '@oxlint/binding-linux-riscv64-musl@1.82.0': + resolution: {integrity: sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.81.0': - resolution: {integrity: sha512-8znJ/5TekjOKg1j1Acho4PJMdiAHLtlcXuWEiipOhAMV6rQcXdmDdXCbheyDczN6TjBwiNfjcP81k4AthrKRzw==} + '@oxlint/binding-linux-s390x-gnu@1.82.0': + resolution: {integrity: sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.81.0': - resolution: {integrity: sha512-Q2Wj70yFsvn5QjlmifFzbj4H+kJy53bwqc41o1fzoM7MpLV1NIbhg/LpWXRfC6KOkSAdUx1Wd8VJsdPmhp/HRA==} + '@oxlint/binding-linux-x64-gnu@1.82.0': + resolution: {integrity: sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.81.0': - resolution: {integrity: sha512-cPInHp/ddEe5qkyK2IiyQ8Q3Mp2oLLEhhsGgTK2oZx4L6+llGam1H1yBvJZ7qHfOXj8N3hxBS8sj4tO+gtFlIg==} + '@oxlint/binding-linux-x64-musl@1.82.0': + resolution: {integrity: sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.81.0': - resolution: {integrity: sha512-0CQxSX4ajqm07AHBf5U33qQzXKdd7wtq/oTL/7vpY6RNNuxrRi8W4bqUV1Jyu/vj+9KmxQyDhxfeVX1nQL6kfg==} + '@oxlint/binding-openharmony-arm64@1.82.0': + resolution: {integrity: sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.81.0': - resolution: {integrity: sha512-l0hbeISm9673hVrrQU8j/p2M7YH9Ouoj7p7E/QM55NTrKVLP+P3PF8hLu+OY+x0VtGRW+ggiQKZqmdYps9H+TA==} + '@oxlint/binding-win32-arm64-msvc@1.82.0': + resolution: {integrity: sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.81.0': - resolution: {integrity: sha512-ksqPP5jbFXcYreEQ7zdJh06rJQBymCTyGRCdaXjfcf2aG4f8KxUWY5wcgYHmaTK+FJ4bPG5sUAdOX+6trnH1JA==} + '@oxlint/binding-win32-ia32-msvc@1.82.0': + resolution: {integrity: sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.81.0': - resolution: {integrity: sha512-IZuUCwGw9emG5JtCp+fYGB+Z4OWEoeEcM8R5BA1pYw63/ieYFVdcU2ylxTpHbVHSenZnsYE+ZZ20uHAJszQ4cA==} + '@oxlint/binding-win32-x64-msvc@1.82.0': + resolution: {integrity: sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.81.0': - resolution: {integrity: sha512-HhD8kd3r6XpelZkhxhRty/po8V6yK1VV63Eq4kSsOS2IoHUywG3DV2EX+z/ZHw46aLI74Y6aA604NRiAqLKW9w==} + '@oxlint/plugins@1.82.0': + resolution: {integrity: sha512-IDF4UXBNOaCeLoEpVxf5Bg85KOWdeuBEkbo1rGzS2vXcbD+GT1asBSeeTC+5BgW6NFTHVByd6N5giHsFPGtwAA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -5159,15 +5162,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.148.0: - resolution: {integrity: sha512-syxUKHeUll89RIABQADcI7sikYrwyssvA6gj4phSSIPezKVM8yMaLAiLLSc7fmzVvrwybfFGFbW5zme9sX87rg==} + oxc-parser@0.149.0: + resolution: {integrity: sha512-iuirONeHJBp6dFuuZKvkCfFvBiF0uFmojIQooGtazR9+GvcwoayGMtal1+cvDUnEw2k435GBish01HOn5iyVqQ==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.66.0: - resolution: {integrity: sha512-FfvqR8RFtV6JJpRrpkfqyVCQ7HDvZ/VriWFx7veftCgL1B5ZO9qNr+1rvPieycMQnNfVG0PWyJQiy7p0hq1I5w==} + oxfmt@0.67.0: + resolution: {integrity: sha512-vV7sSiPsaO0mSxdoUdayipVDFPzW/UQ+hrezEHa20+Tx1dnMdZLSRHMT0PdS67FFbhd74M1n08asW21aLGeCrA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5179,16 +5182,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.81.0: - resolution: {integrity: sha512-rP192UkflmY1cAubbqlGccVRGpyK3MMSzZhN2V5njKzbNJW+KmJE/9JjJaVl9mQPq686xZ2Zc0xFcUAiP8oIMw==} + oxlint-plugin-eslint@1.82.0: + resolution: {integrity: sha512-u86jGed4TZWbar5rXdxROcU+zaRXhKuzdvNwmmFPuhlhxv8LH5ioyjeuE/QgSUjzjFVPU0q5UokWHnkknvnw3w==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@7.0.2001: resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.81.0: - resolution: {integrity: sha512-HyrJYqeoOCL0iqaLEzGewGT48ZX99P3hxYh8udAF9RGGIghSamkXE4ClUyBpEDNqasamThgmlPbuMOe7SAZmHg==} + oxlint@1.82.0: + resolution: {integrity: sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5825,8 +5828,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@2.1.0: - resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} engines: {node: ^20.0.0 || >=22.0.0} tinyrainbow@3.1.1: @@ -7508,65 +7511,67 @@ snapshots: dependencies: '@otplib/core': 13.5.0 - '@oxc-parser/binding-android-arm-eabi@0.148.0': + '@oxc-parser/binding-android-arm-eabi@0.149.0': optional: true - '@oxc-parser/binding-android-arm64@0.148.0': + '@oxc-parser/binding-android-arm64@0.149.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.148.0': + '@oxc-parser/binding-darwin-arm64@0.149.0': optional: true - '@oxc-parser/binding-darwin-x64@0.148.0': + '@oxc-parser/binding-darwin-x64@0.149.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.148.0': + '@oxc-parser/binding-freebsd-x64@0.149.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.148.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.149.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.148.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.149.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.148.0': + '@oxc-parser/binding-linux-arm64-gnu@0.149.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.148.0': + '@oxc-parser/binding-linux-arm64-musl@0.149.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.148.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.149.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.148.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.149.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.148.0': + '@oxc-parser/binding-linux-riscv64-musl@0.149.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.148.0': + '@oxc-parser/binding-linux-s390x-gnu@0.149.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.148.0': + '@oxc-parser/binding-linux-x64-gnu@0.149.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.148.0': + '@oxc-parser/binding-linux-x64-musl@0.149.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.148.0': + '@oxc-parser/binding-openharmony-arm64@0.149.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.148.0': + '@oxc-parser/binding-win32-arm64-msvc@0.149.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.148.0': + '@oxc-parser/binding-win32-ia32-msvc@0.149.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.148.0': + '@oxc-parser/binding-win32-x64-msvc@0.149.0': optional: true '@oxc-project/types@0.148.0': {} + '@oxc-project/types@0.149.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7628,61 +7633,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.66.0': + '@oxfmt/binding-android-arm-eabi@0.67.0': optional: true - '@oxfmt/binding-android-arm64@0.66.0': + '@oxfmt/binding-android-arm64@0.67.0': optional: true - '@oxfmt/binding-darwin-arm64@0.66.0': + '@oxfmt/binding-darwin-arm64@0.67.0': optional: true - '@oxfmt/binding-darwin-x64@0.66.0': + '@oxfmt/binding-darwin-x64@0.67.0': optional: true - '@oxfmt/binding-freebsd-x64@0.66.0': + '@oxfmt/binding-freebsd-x64@0.67.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.66.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.66.0': + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.66.0': + '@oxfmt/binding-linux-arm64-gnu@0.67.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.66.0': + '@oxfmt/binding-linux-arm64-musl@0.67.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.66.0': + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.66.0': + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.66.0': + '@oxfmt/binding-linux-riscv64-musl@0.67.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.66.0': + '@oxfmt/binding-linux-s390x-gnu@0.67.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.66.0': + '@oxfmt/binding-linux-x64-gnu@0.67.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.66.0': + '@oxfmt/binding-linux-x64-musl@0.67.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.66.0': + '@oxfmt/binding-openharmony-arm64@0.67.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.66.0': + '@oxfmt/binding-win32-arm64-msvc@0.67.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.66.0': + '@oxfmt/binding-win32-ia32-msvc@0.67.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.66.0': + '@oxfmt/binding-win32-x64-msvc@0.67.0': optional: true '@oxlint-tsgolint/darwin-arm64@7.0.2001': @@ -7703,64 +7708,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.81.0': + '@oxlint/binding-android-arm-eabi@1.82.0': optional: true - '@oxlint/binding-android-arm64@1.81.0': + '@oxlint/binding-android-arm64@1.82.0': optional: true - '@oxlint/binding-darwin-arm64@1.81.0': + '@oxlint/binding-darwin-arm64@1.82.0': optional: true - '@oxlint/binding-darwin-x64@1.81.0': + '@oxlint/binding-darwin-x64@1.82.0': optional: true - '@oxlint/binding-freebsd-x64@1.81.0': + '@oxlint/binding-freebsd-x64@1.82.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.81.0': + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.81.0': + '@oxlint/binding-linux-arm-musleabihf@1.82.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.81.0': + '@oxlint/binding-linux-arm64-gnu@1.82.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.81.0': + '@oxlint/binding-linux-arm64-musl@1.82.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.81.0': + '@oxlint/binding-linux-ppc64-gnu@1.82.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.81.0': + '@oxlint/binding-linux-riscv64-gnu@1.82.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.81.0': + '@oxlint/binding-linux-riscv64-musl@1.82.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.81.0': + '@oxlint/binding-linux-s390x-gnu@1.82.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.81.0': + '@oxlint/binding-linux-x64-gnu@1.82.0': optional: true - '@oxlint/binding-linux-x64-musl@1.81.0': + '@oxlint/binding-linux-x64-musl@1.82.0': optional: true - '@oxlint/binding-openharmony-arm64@1.81.0': + '@oxlint/binding-openharmony-arm64@1.82.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.81.0': + '@oxlint/binding-win32-arm64-msvc@1.82.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.81.0': + '@oxlint/binding-win32-ia32-msvc@1.82.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.81.0': + '@oxlint/binding-win32-x64-msvc@1.82.0': optional: true - '@oxlint/plugins@1.81.0': {} + '@oxlint/plugins@1.82.0': {} '@pinojs/redact@0.4.0': {} @@ -10743,29 +10748,29 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.148.0: + oxc-parser@0.149.0: dependencies: - '@oxc-project/types': 0.148.0 + '@oxc-project/types': 0.149.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.148.0 - '@oxc-parser/binding-android-arm64': 0.148.0 - '@oxc-parser/binding-darwin-arm64': 0.148.0 - '@oxc-parser/binding-darwin-x64': 0.148.0 - '@oxc-parser/binding-freebsd-x64': 0.148.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.148.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.148.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.148.0 - '@oxc-parser/binding-linux-arm64-musl': 0.148.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.148.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.148.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.148.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.148.0 - '@oxc-parser/binding-linux-x64-gnu': 0.148.0 - '@oxc-parser/binding-linux-x64-musl': 0.148.0 - '@oxc-parser/binding-openharmony-arm64': 0.148.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.148.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.148.0 - '@oxc-parser/binding-win32-x64-msvc': 0.148.0 + '@oxc-parser/binding-android-arm-eabi': 0.149.0 + '@oxc-parser/binding-android-arm64': 0.149.0 + '@oxc-parser/binding-darwin-arm64': 0.149.0 + '@oxc-parser/binding-darwin-x64': 0.149.0 + '@oxc-parser/binding-freebsd-x64': 0.149.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.149.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.149.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.149.0 + '@oxc-parser/binding-linux-arm64-musl': 0.149.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.149.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.149.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.149.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.149.0 + '@oxc-parser/binding-linux-x64-gnu': 0.149.0 + '@oxc-parser/binding-linux-x64-musl': 0.149.0 + '@oxc-parser/binding-openharmony-arm64': 0.149.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.149.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.149.0 + '@oxc-parser/binding-win32-x64-msvc': 0.149.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10789,31 +10794,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.66.0: + oxfmt@0.67.0: dependencies: - tinypool: 2.1.0 + tinypool: 2.1.2 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.66.0 - '@oxfmt/binding-android-arm64': 0.66.0 - '@oxfmt/binding-darwin-arm64': 0.66.0 - '@oxfmt/binding-darwin-x64': 0.66.0 - '@oxfmt/binding-freebsd-x64': 0.66.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.66.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.66.0 - '@oxfmt/binding-linux-arm64-gnu': 0.66.0 - '@oxfmt/binding-linux-arm64-musl': 0.66.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.66.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.66.0 - '@oxfmt/binding-linux-riscv64-musl': 0.66.0 - '@oxfmt/binding-linux-s390x-gnu': 0.66.0 - '@oxfmt/binding-linux-x64-gnu': 0.66.0 - '@oxfmt/binding-linux-x64-musl': 0.66.0 - '@oxfmt/binding-openharmony-arm64': 0.66.0 - '@oxfmt/binding-win32-arm64-msvc': 0.66.0 - '@oxfmt/binding-win32-ia32-msvc': 0.66.0 - '@oxfmt/binding-win32-x64-msvc': 0.66.0 - - oxlint-plugin-eslint@1.81.0: {} + '@oxfmt/binding-android-arm-eabi': 0.67.0 + '@oxfmt/binding-android-arm64': 0.67.0 + '@oxfmt/binding-darwin-arm64': 0.67.0 + '@oxfmt/binding-darwin-x64': 0.67.0 + '@oxfmt/binding-freebsd-x64': 0.67.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.67.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.67.0 + '@oxfmt/binding-linux-arm64-gnu': 0.67.0 + '@oxfmt/binding-linux-arm64-musl': 0.67.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-musl': 0.67.0 + '@oxfmt/binding-linux-s390x-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-musl': 0.67.0 + '@oxfmt/binding-openharmony-arm64': 0.67.0 + '@oxfmt/binding-win32-arm64-msvc': 0.67.0 + '@oxfmt/binding-win32-ia32-msvc': 0.67.0 + '@oxfmt/binding-win32-x64-msvc': 0.67.0 + + oxlint-plugin-eslint@1.82.0: {} oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -10824,27 +10829,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.81.0(oxlint-tsgolint@7.0.2001): + oxlint@1.82.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.81.0 - '@oxlint/binding-android-arm64': 1.81.0 - '@oxlint/binding-darwin-arm64': 1.81.0 - '@oxlint/binding-darwin-x64': 1.81.0 - '@oxlint/binding-freebsd-x64': 1.81.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.81.0 - '@oxlint/binding-linux-arm-musleabihf': 1.81.0 - '@oxlint/binding-linux-arm64-gnu': 1.81.0 - '@oxlint/binding-linux-arm64-musl': 1.81.0 - '@oxlint/binding-linux-ppc64-gnu': 1.81.0 - '@oxlint/binding-linux-riscv64-gnu': 1.81.0 - '@oxlint/binding-linux-riscv64-musl': 1.81.0 - '@oxlint/binding-linux-s390x-gnu': 1.81.0 - '@oxlint/binding-linux-x64-gnu': 1.81.0 - '@oxlint/binding-linux-x64-musl': 1.81.0 - '@oxlint/binding-openharmony-arm64': 1.81.0 - '@oxlint/binding-win32-arm64-msvc': 1.81.0 - '@oxlint/binding-win32-ia32-msvc': 1.81.0 - '@oxlint/binding-win32-x64-msvc': 1.81.0 + '@oxlint/binding-android-arm-eabi': 1.82.0 + '@oxlint/binding-android-arm64': 1.82.0 + '@oxlint/binding-darwin-arm64': 1.82.0 + '@oxlint/binding-darwin-x64': 1.82.0 + '@oxlint/binding-freebsd-x64': 1.82.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.82.0 + '@oxlint/binding-linux-arm-musleabihf': 1.82.0 + '@oxlint/binding-linux-arm64-gnu': 1.82.0 + '@oxlint/binding-linux-arm64-musl': 1.82.0 + '@oxlint/binding-linux-ppc64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-musl': 1.82.0 + '@oxlint/binding-linux-s390x-gnu': 1.82.0 + '@oxlint/binding-linux-x64-gnu': 1.82.0 + '@oxlint/binding-linux-x64-musl': 1.82.0 + '@oxlint/binding-openharmony-arm64': 1.82.0 + '@oxlint/binding-win32-arm64-msvc': 1.82.0 + '@oxlint/binding-win32-ia32-msvc': 1.82.0 + '@oxlint/binding-win32-x64-msvc': 1.82.0 oxlint-tsgolint: 7.0.2001 p-cancelable@4.0.1: {} @@ -11564,7 +11569,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - tinypool@2.1.0: {} + tinypool@2.1.2: {} tinyrainbow@3.1.1: {} From 67305a500b0c90fba23a9155440ed28d2b011a6b Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sat, 12 Sep 2026 17:27:01 +0800 Subject: [PATCH 0774/1190] feat(route): Add Yinxu Museum news and exhibition route (#23253) * feat(route): Add Yinxu Museum news and exhibition route * fix: update parameters --- lib/routes/yinxubwg/exhibition.tsx | 104 +++++++++++++++++++++++++++++ lib/routes/yinxubwg/namespace.ts | 10 +++ lib/routes/yinxubwg/notice.ts | 53 +++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 lib/routes/yinxubwg/exhibition.tsx create mode 100644 lib/routes/yinxubwg/namespace.ts create mode 100644 lib/routes/yinxubwg/notice.ts diff --git a/lib/routes/yinxubwg/exhibition.tsx b/lib/routes/yinxubwg/exhibition.tsx new file mode 100644 index 000000000000..b26454f0bdf4 --- /dev/null +++ b/lib/routes/yinxubwg/exhibition.tsx @@ -0,0 +1,104 @@ +import { renderToString } from 'hono/jsx/dom/server'; + +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +export const route = { + path: '/exhibitionIndex/:type', + categories: ['travel'], + example: '/yinxubwg/exhibitionIndex/2', + parameters: { + type: 'Exhibition type. Supported values: `1` (Permanent Exhibition), `2` (Temporary Exhibition), `3` (Past Exhibitions).', + }, + name: 'Exhibition Information', + maintainers: ['magazian'], + radar: [ + { + source: ['www.yinxubwg.cn/yxgw/exhibitionIndex'], + target: '/exhibitionIndex/:type', + }, + ], + handler: async (ctx) => { + const typeParam = ctx.req.param('type'); + const exhibitionType = Number(typeParam); + + const baseUrl = 'https://www.yinxubwg.cn'; + const apiBaseUrl = 'https://guc.yinxubwg.cn'; + const museumName = namespace.zh?.name || namespace.name; + + const typeName: Record = { + 1: '常设展览', + 2: '临时展览', + 3: '展览回眸', + }; + const feedTitle = typeName[exhibitionType]; + + const response = await ofetch(`${apiBaseUrl}/gwebapi/exhibition/list`, { + query: { + p: 'w', + exhibition_type: exhibitionType, + language: 1, + page: 1, + limit: 10, + }, + }); + + const list: Array<{ + exhibition_id: number; + exhibition_name: string; + list_img: string; + place: string; + start_date_format: string; + end_date_format: string; + }> = response.data?.list ?? []; + + const items = list.map((item) => { + const link = `${baseUrl}/yxgw/exhibitionIndex/exhibitionDetail?exhibitionId=${item.exhibition_id}`; + const imgUrl = item.list_img; + const startDate = item.start_date_format; + const endDate = item.end_date_format; + const location = item.place; + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    +
    + ); + + return { + title: item.exhibition_name, + link, + pubDate: startDate ? parseDate(startDate) : undefined, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }; + }); + + return { + title: `${museumName} - ${feedTitle}`, + link: `${baseUrl}/yxgw/exhibitionIndex/exhibitionList?type=${exhibitionType}`, + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/yinxubwg/namespace.ts b/lib/routes/yinxubwg/namespace.ts new file mode 100644 index 000000000000..c4c526b2f7a1 --- /dev/null +++ b/lib/routes/yinxubwg/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Yinxu Museum', + url: 'www.yinxubwg.cn', + + zh: { + name: '殷墟博物馆', + }, +}; diff --git a/lib/routes/yinxubwg/notice.ts b/lib/routes/yinxubwg/notice.ts new file mode 100644 index 000000000000..40bc71f822ed --- /dev/null +++ b/lib/routes/yinxubwg/notice.ts @@ -0,0 +1,53 @@ +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { namespace } from './namespace'; + +export const route = { + path: '/notice', + categories: ['travel'], + example: '/yinxubwg/notice', + name: 'Notice', + maintainers: ['magazian'], + radar: [ + { + source: ['www.yinxubwg.cn/yxgw/notice'], + target: '/notice', + }, + ], + handler: async () => { + const cate = 1; + const baseUrl = 'https://www.yinxubwg.cn'; + const apiBaseUrl = 'https://guc.yinxubwg.cn'; + const museumName = namespace.zh?.name || namespace.name; + + const response = await ofetch(`${apiBaseUrl}/gwebapi/article/list`, { + query: { + p: 'w', + cate_id: cate, + page: 1, + limit: 9, + }, + }); + + const list: Array<{ + article_id: number; + title: string; + push_time_new: string; + out_url: string; + }> = response.data?.list ?? []; + + const items = list.map((item) => ({ + title: item.title, + link: item.out_url || `${baseUrl}/yxgw/notice/detail?id=${item.article_id}&cate=${cate}&pos=news`, + pubDate: parseDate(item.push_time_new), + })); + + return { + title: `${museumName} - 最新公告`, + link: `${baseUrl}/yxgw/notice?cate=${cate}`, + language: 'zh-CN', + item: items, + }; + }, +}; From 2eb6d1320d06a7f26b4ec2fa7f3343b33173a19d Mon Sep 17 00:00:00 2001 From: wongJG <76810856+wongJG@users.noreply.github.com> Date: Sun, 13 Sep 2026 14:28:46 +0000 Subject: [PATCH 0775/1190] fix(route/embassy): migrate links to .gov.cn and refresh selectors (#23258) The MFA migrated all websites to .china-embassy.gov.cn and .china-consulate.gov.cn on 2022-04. --- lib/routes/embassy/supported-list.ts | 212 +++++++++++++-------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/lib/routes/embassy/supported-list.ts b/lib/routes/embassy/supported-list.ts index ba84db54fa33..02aecf4d89d4 100644 --- a/lib/routes/embassy/supported-list.ts +++ b/lib/routes/embassy/supported-list.ts @@ -2,16 +2,16 @@ export default { ca: { country: 'ca', countryCN: '加拿大', - link: 'http://ca.china-embassy.org/chn/zytz/', - list: 'div[class=nav_title_list] > ul a', + link: 'https://ca.china-embassy.gov.cn/zytz/', + list: 'ul.news-content > li.item > a', title: '#News_Body_Title', - description: '#News_Body_Txt_A', - pubDate: '#News_Body_Time', + description: '.view-content .view_default', + pubDate: 'div.date', consulates: { montreal: { cityCN: '蒙特利尔', - link: 'http://montreal.chineseconsulate.org/chn/zytz/', - list: '#docMore > tbody > tr > td:nth-child(3) > div[class=Text_Center] > ul a', + link: 'https://montreal.china-consulate.gov.cn/zjfw/zytz_1/', + list: 'div.Text_Center > ul > li > a', title: '#News_Body_Title', description: '#News_Body_Txt_A', pubDate: '#News_Body_Time', @@ -21,34 +21,34 @@ export default { de: { country: 'de', countryCN: '德国', - link: 'http://de.china-embassy.gov.cn/chn/lsfw/jqtz/', - list: '#docMore a', - title: '#article > table > tbody > tr:nth-child(2) b', - description: '#article > table > tbody > tr:nth-child(10) > td > div', - pubDate: '#article > table > tbody > tr:nth-child(6) > td', + link: 'https://de.china-embassy.gov.cn/chn/lsfw/jqtz/', + list: '.article-list a', + title: '.article-title', + description: '.article-content', + pubDate: '.publish-date', consulates: { munich: { cityCN: '慕尼黑', - link: 'http://munich.chineseconsulate.org/chn/jqtz/', - list: '#docMore > tbody > tr > td:nth-child(2) > table:nth-child(2) > tbody > tr:nth-child(2) > td > table > tbody a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(2) > td > div', - pubDate: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(4) > td', + link: 'https://munich.china-consulate.gov.cn/zytz/', + list: 'ul.list li a', + title: '#News_Body_Title', + description: '#News_Body_Txt_A', + pubDate: '#News_Body_Time', }, }, }, fr: { country: 'fr', countryCN: '法国', - link: 'http://fr.chineseembassy.org/chn/zgzfg/zgsg/lsb/', - list: '#docMore > tbody > tr > td > table > tbody a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(3) > td', - pubDate: '#article > tbody > tr:nth-child(2) > td', + link: 'https://fr.china-embassy.gov.cn/zgzfg/zgsg/lsqwc/zytz/', + list: 'td.title > a', + title: 'td.bigtitle', + description: '.TRS_UEDITOR', + pubDate: 'td.time', consulates: { marseille: { cityCN: '马赛', - link: 'http://marseille.china-consulate.org/chn/lsfwgg/', + link: 'https://marseille.china-consulate.gov.cn/chn/lsfwgg/', list: '.newslist a', title: '#News_Body_Title', description: '#News_Body_Txt_A', @@ -56,34 +56,34 @@ export default { }, strasbourg: { cityCN: '斯特拉斯堡', - link: 'http://strasbourg.china-consulate.org/chn/lsfw/', - list: '#docMore > tbody a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(3) > td', - pubDate: '#article > tbody > tr:nth-child(2) > td', + link: 'https://strasbourg.china-consulate.gov.cn/lsfw/', + list: 'div.do-section > ul > li > a.news-link', + title: '.view-title', + description: '.view-content .content', + pubDate: '.view-date', }, lyon: { cityCN: '里昂', - link: 'http://lyon.china-consulate.org/chn/tztg/', - list: '#manytitle > tbody > tr > td > table > tbody > tr:nth-child(1) > td > table > tbody a', - title: '#article > table:nth-child(1) > tbody > tr:nth-child(1) > td > font', - description: '#article > table:nth-child(2) > tbody > tr:nth-child(2) > td > div > div', - pubDate: '#article > table:nth-child(1) > tbody > tr:nth-child(4) > td > font', + link: 'https://lyon.china-consulate.gov.cn/lsfw/zytz/', + list: 'ul.news-list > li.news-item > a.item-link', + title: '.view-title', + description: '.view-content .view_default', + pubDate: '.view-date', }, }, }, jp: { country: 'jp', countryCN: '日本', - link: 'http://jp.china-embassy.org/chn/lsfws/LSB/', - list: '#docMore > tbody > tr > td:nth-child(2) > table > tbody > tr:nth-child(2) > td > table:nth-child(3) a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(2) > td', - pubDate: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(4) > td', + link: 'https://jp.china-embassy.gov.cn/lsfw_0/lsbtz_138411/', + list: 'a.news-link', + title: 'h2.title', + description: '.view-content .view_default', + pubDate: 'div.view-date', consulates: { nagasaki: { cityCN: '长崎', - link: 'http://nagasaki.china-consulate.org/chn/tzgg/', + link: 'https://nagasaki.china-consulate.gov.cn/chn/tzgg/', list: '.newsList a', title: '#News_Body_Title', description: '#News_Body_Txt_A', @@ -91,15 +91,15 @@ export default { }, osaka: { cityCN: '大阪', - link: 'http://osaka.china-consulate.org/chn/tzgg/', - list: '#liebiao > tbody > tr > td:nth-child(1) > table:nth-child(3) > tbody a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(3) > td', - pubDate: '#article > tbody > tr:nth-child(2) > td', + link: 'https://osaka.china-consulate.gov.cn/lsfw/ZYTZ/', + list: 'td.title > a', + title: 'td.bigtitle', + description: '.TRS_UEDITOR', + pubDate: 'td[height="19"]', }, fukuoka: { cityCN: '福冈', - link: 'http://fukuoka.china-consulate.org/chn/tztg/', + link: 'https://fukuoka.china-consulate.gov.cn/chn/tztg/', list: '#docMore > tbody > tr > td:nth-child(2) > table > tbody > tr:nth-child(2) > td > table:nth-child(2) > tbody a', title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', description: '#article > tbody > tr:nth-child(3) > td', @@ -107,7 +107,7 @@ export default { }, nagoya: { cityCN: '名古屋', - link: 'http://nagoya.chineseconsulate.org/chn/gzgg/', + link: 'https://nagoya.china-consulate.gov.cn/chn/gzgg/', list: '#docMore > div.mbox.fl > div > div.cbox_ul > ul a', title: '#News_Body_Title', description: '#News_Body_Txt_A', @@ -115,42 +115,42 @@ export default { }, sapporo: { cityCN: '札幌', - link: 'http://sapporo.china-consulate.org/chn/tzbg/', - list: '#docMore > tbody > tr > td:nth-child(2) > table > tbody > tr:nth-child(2) > td > table:nth-child(3) > tbody > tr:nth-child(1) a', - title: '#article > tbody > tr > td > table:nth-child(1) > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr > td > table:nth-child(2) > tbody > tr:nth-child(1) > td', - pubDate: '#article > tbody > tr > td > table:nth-child(1) > tbody > tr:nth-child(4) > td', + link: 'https://sapporo.china-consulate.gov.cn/chn/tzbg/', + list: 'td[width="585"] a', + title: 'td.bigtitle', + description: '.TRS_UEDITOR', + pubDate: 'td.time', }, niigata: { cityCN: '新潟', - link: 'http://niigata.china-consulate.org/chn/lsbh/tztg/', - list: '#docMore a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(4) > td', - description: '#article > tbody > tr:nth-child(2) > td', - pubDate: '#article > tbody > tr > td > table:nth-child(1) > tbody > tr:nth-child(4) > td', + link: 'https://niigata.china-consulate.gov.cn/wybs/tztg_123188/', + list: 'div.b-top > ul.news > li > a', + title: '#News_Body_Title', + description: '#News_Body_Txt_A', + pubDate: '#News_Body_Time', }, }, }, kr: { country: 'kr', countryCN: '韩国', - link: 'http://kr.china-embassy.org/chn/lsqz/ls_tz/', - list: '#TRS a', - title: '#article > table > tbody > tr:nth-child(1) > td > font > b', - description: '#article > table > tbody > tr:nth-child(3) > td > div > div:nth-child(2)', - pubDate: '#article > table > tbody > tr:nth-child(1) > td > font > b', + link: 'https://kr.china-embassy.gov.cn/lstz/', + list: 'div.news-wrapper > ul.news-list > li.news-item > a.news-link', + title: '.view-title', + description: '.view-content .view_default', + pubDate: 'div.view-date', consulates: { busan: { cityCN: '釜山', - link: 'http://busan.china-consulate.org/chn/lsfw/tzgg101/', - list: 'body > table:nth-child(6) > tbody > tr > td:nth-child(2) > table:nth-child(2) > tbody > tr:nth-child(3) > td > table > tbody a', - title: '#article > tbody > tr:nth-child(1) > td > table > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(3) > td', - pubDate: '#article > tbody > tr:nth-child(2) > td', + link: 'https://busan.china-consulate.gov.cn/chn/lsfw/tzgg101/', + list: 'td.title > a', + title: 'td.bigtitle', + description: '.TRS_UEDITOR', + pubDate: 'td[height="19"]', }, jeju: { cityCN: '济州', - link: 'http://jeju.chineseconsulate.org/chn/ggl/', + link: 'https://jeju.china-consulate.gov.cn/chn/ggl/', list: '#docMore > tbody > tr > td:nth-child(3) > div > ul:nth-child(5) a', title: '#News_Body_Title', description: '#News_Body_Txt_A', @@ -158,18 +158,18 @@ export default { }, gwangju: { cityCN: '光州', - link: 'http://gwangju.china-consulate.org/chn/lbxx/ggtz/', + link: 'https://gwangju.china-consulate.gov.cn/chn/lsfwx/zytz/', list: '#docNum a', - title: '#article > table > tbody > tr:nth-child(2) > td', - description: '#article > table > tbody > tr:nth-child(10) > td > div', - pubDate: '#article > table > tbody > tr:nth-child(6) > td > font', + title: 'td.bigtitle', + description: '.TRS_UEDITOR', + pubDate: 'td font[color="666666"]', }, }, }, my: { country: 'my', countryCN: '马来西亚', - link: 'http://my.china-embassy.org/chn/zytz/', + link: 'https://my.china-embassy.gov.cn/chn/zytz/', list: 'div[class=nbox_ul] > ul a', title: '#News_Body_Title', description: '#News_Body_Txt_A', @@ -178,75 +178,75 @@ export default { sg: { country: 'sg', countryCN: '新加坡', - link: 'http://sg.china-embassy.gov.cn/lsfw_0/zytz/', + link: 'https://sg.china-embassy.gov.cn/lsfw_0/zytz/', list: 'div.news-list a[target=_blank]', title: '#News_Body_Title', - description: 'div.trs_editor_view', + description: '.view-content .content', pubDate: 'div.view-date', }, uk: { country: 'uk', countryCN: '英国', - link: 'http://www.chinese-embassy.org.uk/chn/lsfw/lsxz/', - list: '#docMore > tbody a', - title: '#article > tbody > tr:nth-child(2) > td', - description: '#article .content', - pubDate: '#article > tbody > tr:nth-child(5) > td', + link: 'https://gb.china-embassy.gov.cn/lsfw_137163/zytz_137164/', + list: 'ul.news-card:not(.chnllist) a[href$=".htm"]', + title: '#News_Body_Title', + description: '#News_Body_Txt_A', + pubDate: 'div.txt2', consulates: { belfast: { cityCN: '贝尔法斯特', - link: 'http://belfast.chineseconsulate.org/chn/tztg/', - list: 'div.newslist a', - title: '#News_Body_Title', - description: '#News_Body_Txt_A > div', - pubDate: '#News_Body_Time', + link: 'https://belfast.china-consulate.gov.cn/lsfw/zytz/', + list: 'ul.news-list > li > a.news-link', + title: '.view-title', + description: '.view-content .view_default', + pubDate: '.view-date', }, edinburgh: { cityCN: '爱丁堡', - link: 'http://edinburgh.china-consulate.org/chn/zytz/', - list: '#TRS_OUTLINE > tbody > tr:nth-child(3) a', - title: '#article > tbody > tr:nth-child(1) > td', - description: '#article > tbody > tr:nth-child(3) > td', - pubDate: '#article > tbody > tr:nth-child(2) > td', + link: 'https://edinburgh.china-consulate.gov.cn/zytz/', + list: 'div.section ul > li > a.news-link', + title: '.view-title', + description: '.view-content .content', + pubDate: '.view-date', }, manchester: { cityCN: '曼彻斯特', - link: 'http://manchester.chineseconsulate.org/chn/tzgg/', - list: 'div#TRS a', - title: '#article > table > tbody > tr:nth-child(1) > td > font > b', - description: '#article > table > tbody > tr:nth-child(6) > td > div > div', - pubDate: '#article > table > tbody > tr:nth-child(4) > td > font', + link: 'https://manchester.china-consulate.gov.cn/zytz/', + list: 'div.nbox_ul > ul > li > a', + title: '#News_Body_Title', + description: '#News_Body_Txt_A', + pubDate: '#News_Body_Time', }, }, }, us: { country: 'us', countryCN: '美国', - link: 'http://us.china-embassy.gov.cn/zytz/', + link: 'https://us.china-embassy.gov.cn/zytz/', list: '.list-content ul li a', title: 'div.title', - description: 'div.trs_editor_view', + description: '.view-content .TRS_UEDITOR', pubDate: 'div.date', consulates: { chicago: { cityCN: '芝加哥', - link: 'http://www.chinaconsulatechicago.org/chn/zytz/', - list: 'div#TRS a', - title: '#article > table > tbody > tr:nth-child(1) > td', - description: '#article > table > tbody > tr:nth-child(6) > td > div', - pubDate: '#article > table > tbody > tr:nth-child(3) > td > font', + link: 'https://chicago.china-consulate.gov.cn/zytz_0/', + list: 'ul > li.aside-newsitem > a.news-link', + title: '.view-title', + description: '.view-content .view_default', + pubDate: '.view-date', }, newyork: { cityCN: '纽约', - link: 'http://newyork.china-consulate.org/chn/fwzc/zxtz/', - list: 'div.cbox_ul a', - title: '#News_Body_Title', - description: '#News_Body_Txt_A', - pubDate: '#News_Body_Time', + link: 'https://newyork.china-consulate.gov.cn/tzgg/', + list: 'ul.news-list > li > a.news-link', + title: '.view-title', + description: '.view-content .TRS_UEDITOR', + pubDate: '.view-date', }, sanfrancisco: { cityCN: '旧金山', - link: 'http://www.chinaconsulatesf.org/chn/zytz/', + link: 'https://sanfrancisco.china-consulate.gov.cn/chn/zytz/', list: 'div#docMore a', title: '#News_Body_Title', description: '#article > .News_Body_Txt_A', From b05e6a2ffd55375bbfd62dccec91abbee3087e52 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:14:35 +0900 Subject: [PATCH 0776/1190] feat(route): add temposmart new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /temposmart/estates/:pref? for テンポスマート (www.temposmart.jp) new restaurant-property listings by prefecture (東京, 神奈川, 埼玉, 千葉, 大阪, 京都, 兵庫). Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/temposmart/estates.ts | 278 +++++++++++++++++++++++++++++ lib/routes/temposmart/namespace.ts | 12 ++ lib/routes/temposmart/utils.ts | 211 ++++++++++++++++++++++ 3 files changed, 501 insertions(+) create mode 100644 lib/routes/temposmart/estates.ts create mode 100644 lib/routes/temposmart/namespace.ts create mode 100644 lib/routes/temposmart/utils.ts diff --git a/lib/routes/temposmart/estates.ts b/lib/routes/temposmart/estates.ts new file mode 100644 index 000000000000..b10587792c5e --- /dev/null +++ b/lib/routes/temposmart/estates.ts @@ -0,0 +1,278 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import type { ListingExtra } from './utils'; +import { bracketNotes, clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseMonthsSum, parseWalkMin, parseWard, parseYmd, sumKnown, summarize, tsuboUnit } from './utils'; + +const HOST = 'https://www.temposmart.jp'; +const DETAIL_CONCURRENCY = 2; +const DEFAULT_LIMIT = 30; +const PAGE_SIZE = 50; + +/** Prefectures the site lists (sitemap `/estates/pref/{JIS X 0401 code}`). */ +const PREFECTURES = [ + { slug: 'tokyo', code: '13', label: '東京都' }, + { slug: 'kanagawa', code: '14', label: '神奈川県' }, + { slug: 'saitama', code: '11', label: '埼玉県' }, + { slug: 'chiba', code: '12', label: '千葉県' }, + { slug: 'osaka', code: '27', label: '大阪府' }, + { slug: 'kyoto', code: '26', label: '京都府' }, + { slug: 'hyogo', code: '28', label: '兵庫県' }, +]; + +interface ListCard { + title: string; + link: string; + extra: ListingExtra; +} + +interface DetailFields { + listed_at: string | null; + updated_at: string | null; + rent: string | null; + tsubo_unit: string | null; + fixtures: string | null; + deposit: string | null; // 保証金 + security: string | null; // 敷金 + key_money: string | null; // 礼金 + contract_kind: string | null; + contract_term: string | null; + purpose: string | null; // 現況(建物詳細) + available_purpose: string | null; // 可能用途 + note: string | null; // 備考 +} + +/** + * List page card selectors (`/estates/pref/13?sort=new`): + * card .estateItem, id .estateItem__estateId--value, title/link .estateItem__estateTitle a, + * 賃料 .estateItem__estatePrice--value ('330,000' | 'ご相談'), 坪単価 .estateItem__estateSubPrice--value, + * 最寄駅 .stationInfo__name / .stationInfo__near--value / .stationInfo__route, + * 所在地 .estateItem__estateAddress--link, 所在階 .estateItem__estateFloor, 保証金/敷金 .estateItem__estateDeposit, + * 面積 .estateItem__estateArea, 現況 .estateItem__estatePurpose, tags .estateItem__estateProperties__tag + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('.estateItem') + .toArray() + .map((el) => { + const $el = $(el); + const a = $el.find('.estateItem__estateTitle a').first(); + const href = a.attr('href'); + const title = clean(a.text()); + const id = clean($el.find('.estateItem__estateId--value').text()); + if (!href || !title || !id) { + return null; + } + + const raw: ListingExtra['raw'] = { + rent: clean($el.find('.estateItem__estatePrice--value').text()), + tsubo_unit: clean($el.find('.estateItem__estateSubPrice--value').text()), + station: clean($el.find('.stationInfo__name').text()), + walk: clean($el.find('.stationInfo__near--value').text()), + line: clean($el.find('.stationInfo__route').text()), + address: clean($el.find('.estateItem__estateAddress--link').first().text()), + floor: clean($el.find('.estateItem__estateFloor').text()), + deposit: clean($el.find('.estateItem__estateDeposit').text()), + area: clean($el.find('.estateItem__estateArea').text()), + purpose: clean($el.find('.estateItem__estatePurpose').text()), + }; + const tags = $el + .find('.estateItem__estateProperties__tag') + .toArray() + .map((t) => clean($(t).text())) + .filter((t): t is string => t !== null); + if ($el.find('.imageLabel--new').length > 0) { + tags.unshift('NEW'); + } + + const notes = bracketNotes(title); + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + const purpose = raw.purpose; + + const extra: ListingExtra = { + source: 'temposmart', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(raw.tsubo_unit) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor), + station: raw.station?.replace(/駅$/, '') ?? null, + line: raw.line, + walk_min: parseWalkMin(raw.walk), + deposit_months: parseMonthsSum(raw.deposit), + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: parseCondition(purpose, title), + prev_business: purpose !== null && purpose !== 'スケルトン' && purpose !== 'その他' ? purpose : null, + heavy_food_ok: parseHeavyFood(title), + business_limit: notes.length > 0 ? notes.join(' / ') : null, + listed_at: null, + ward: parseWard(raw.address), + address_hint: raw.address, + tags, + raw, + }; + return { title, link: new URL(href, HOST).href, extra }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** + * Detail page selectors (`/estates/{id}`): + * 登録日 / 最終変更日 .overview__date span, fee / contract / building tables table.estateTable th → td, + * 可能用途 .availablePurpose__text, 備考 .estateTable__note--content + */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const th = $('table.estateTable th') + .toArray() + .find((el) => clean($(el).text()) === label); + return th ? clean($(th).next('td').text()) : null; + }; + const dates = $('.overview__date span') + .toArray() + .map((el) => clean($(el).text())) + .filter((t): t is string => t !== null); + const dateOf = (label: string): string | null => dates.find((d) => d.startsWith(label))?.replace(/^[^::]+[::]\s*/, '') ?? null; + + return { + listed_at: dateOf('登録日'), + updated_at: dateOf('最終変更日'), + rent: cell('賃料'), + tsubo_unit: cell('坪単価'), + fixtures: cell('造作価格'), + deposit: cell('保証金'), + security: cell('敷金'), + key_money: cell('礼金'), + contract_kind: cell('契約種別'), + contract_term: cell('契約期間'), + purpose: cell('現況'), + available_purpose: clean($('.availablePurpose__text').first().text()), + note: clean($('.estateTable__note--content').text()), + }; +}; + +/** List fields + detail fields; values already known from the list are never overwritten with null. */ +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { + const depositMonths = d.deposit === null && d.security === null ? null : sumKnown(parseMonths(d.deposit), parseMonths(d.security)); + const limitParts = [base.business_limit, d.available_purpose === null ? null : `可能用途: ${d.available_purpose}`].filter((p): p is string => p !== null); + + return { + ...base, + rent_jpy: base.rent_jpy ?? parseJpy(d.rent), + tsubo_unit_jpy: base.tsubo_unit_jpy ?? parseJpy(d.tsubo_unit) ?? tsuboUnit(base.rent_jpy, base.tsubo), + deposit_months: depositMonths ?? base.deposit_months, + key_money_months: parseMonths(d.key_money), + fixtures_transfer_jpy: parseJpy(d.fixtures), + condition: base.condition ?? parseCondition(d.purpose, d.note), + heavy_food_ok: base.heavy_food_ok ?? parseHeavyFood(d.available_purpose), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, + listed_at: parseYmd(d.listed_at), + raw: { + ...base.raw, + listed_at: d.listed_at, + updated_at: d.updated_at, + rent_detail: d.rent, + fixtures: d.fixtures, + deposit_detail: d.deposit, + security: d.security, + key_money: d.key_money, + contract_kind: d.contract_kind, + contract_term: d.contract_term, + purpose_detail: d.purpose, + available_purpose: d.available_purpose, + note: d.note, + }, + }; +}; + +/** A failed detail page (e.g. delisted 404) keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`temposmart: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string = ctx.req.param('pref') ?? 'tokyo'; + const prefecture = PREFECTURES.find((p) => p.slug === pref || p.code === pref); + if (!prefecture) { + throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, PAGE_SIZE); + const listUrl = `${HOST}/estates/pref/${prefecture.code}?sort=new`; + + const cards = parseList(await ofetch(listUrl)).slice(0, limit); + + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: summarize(extra), + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `テンポスマート 新着物件 (${prefecture.label})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/estates/:pref?', + name: '新着物件', + url: 'www.temposmart.jp', + maintainers: ['pseudoyu'], + handler, + example: '/temposmart/estates/tokyo', + parameters: { + pref: { + description: '都道府県 slug or JIS X 0401 code', + default: 'tokyo', + options: PREFECTURES.map((p) => ({ value: p.slug, label: `${p.label} (${p.code})` })), + }, + }, + description: `New listings on テンポスマート for one prefecture, sorted by 新着順 (first page, 50 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,保証金,礼金,造作譲渡料,現況,業種制限,登録日,…) parsed from the list and detail pages; unknown values are \`null\`. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 50 | 30 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.temposmart.jp/estates/pref/:pref'], + target: '/estates/:pref', + }, + ], +}; diff --git a/lib/routes/temposmart/namespace.ts b/lib/routes/temposmart/namespace.ts new file mode 100644 index 000000000000..4cb82bdac771 --- /dev/null +++ b/lib/routes/temposmart/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Temposmart', + url: 'www.temposmart.jp', + description: 'テンポスマート — 飲食店向け居抜き・貸店舗物件情報サイト', + lang: 'ja', + + ja: { + name: 'テンポスマート', + }, +}; diff --git a/lib/routes/temposmart/utils.ts b/lib/routes/temposmart/utils.ts new file mode 100644 index 000000000000..ee45efccbda7 --- /dev/null +++ b/lib/routes/temposmart/utils.ts @@ -0,0 +1,211 @@ +/** + * Text → structure helpers for テンポスマート listing pages. + * Unknown values are `null`, never `0`; site text is kept verbatim in `raw`. + */ + +export type ListingCondition = 'inuki' | 'skeleton'; + +export interface ListingExtra { + source: 'temposmart'; + listing_id: string; + rent_jpy: number | null; // 賃料(税込、円/月) + tsubo: number | null; // 坪 + area_m2: number | null; // ㎡ + tsubo_unit_jpy: number | null; // 坪単価 + floor: string | null; // 1F / B1F / 1F〜2F + station: string | null; // 最寄駅(without 駅) + line: string | null; // 路線 + walk_min: number | null; // 徒歩(分) + deposit_months: number | null; // 保証金 + 敷金(賃料の何ヶ月分) + deposit_jpy: number | null; // 保証金・敷金(金額) + key_money_months: number | null; // 礼金 + fixtures_transfer_jpy: number | null; // 造作譲渡料;無償 = 0,相談 / 未記載 = null + condition: ListingCondition | null; // 現況 + prev_business: string | null; // 前業態 / 現業態 + heavy_food_ok: boolean | null; // 重飲食可否 + business_limit: string | null; // 業種制限(原文) + listed_at: string | null; // 登録日 YYYY-MM-DD + ward: string | null; // 区 / 市 + address_hint: string | null; // 所在地(町丁目まで) + tags: string[]; + raw: Record; +} + +const TSUBO_M2 = 3.30579; + +/** Collapse whitespace from cheerio `.text()`; empty → null. */ +export const clean = (text: string | undefined | null): string | null => { + const s = (text ?? '').replaceAll(/\s+/g, ' ').trim(); + return s === '' ? null : s; +}; + +const isUnknown = (text: string): boolean => /^(?:[--―—]|ご?相談|要相談|応相談|未定|非公開)?$/.test(text); + +/** + * '352,000円(税込)' → 352000;'160万円' → 1600000;'330,000' → 330000; + * 'なし' / '無償譲渡' → 0;'ご相談' / '-' / '' → null。 + */ +export const parseJpy = (text: string | null): number | null => { + if (text === null) { + return null; + } + const s = text.replaceAll(/[,,\s]/g, ''); + if (/無償|なし|無し/.test(s)) { + return 0; + } + if (isUnknown(s)) { + return null; + } + // Without 円 / 万 only a bare number is accepted ('3ヶ月' is not an amount). + if (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s)) { + return null; + } + const oku = s.match(/^(\d+(?:\.\d+)?)億(?:(\d+(?:\.\d+)?)万)?/); + if (oku) { + return Math.round(Number(oku[1]) * 1e8 + (oku[2] ? Number(oku[2]) * 1e4 : 0)); + } + const m = s.match(/\d+(?:\.\d+)?/); + if (!m) { + return null; + } + const n = Number(m[0]); + return Math.round(s.includes('万') ? n * 1e4 : n); +}; + +const round2 = (n: number): number => Math.round(n * 100) / 100; + +/** '21.42坪(70.84㎡)' / '70㎡' → { tsubo, area_m2 }, the missing side converted at 3.30579. */ +export const parseArea = (text: string | null): { tsubo: number | null; area_m2: number | null } => { + if (text === null) { + return { tsubo: null, area_m2: null }; + } + const s = text.replaceAll(/[,,]/g, ''); + const tsubo = s.match(/(\d+(?:\.\d+)?)\s*坪/)?.[1]; + const m2 = s.match(/(\d+(?:\.\d+)?)\s*(?:㎡|m²|m2|平米)/)?.[1]; + const t = tsubo ? Number(tsubo) : m2 ? round2(Number(m2) / TSUBO_M2) : null; + const a = m2 ? Number(m2) : tsubo ? round2(Number(tsubo) * TSUBO_M2) : null; + return { tsubo: t, area_m2: a }; +}; + +/** 'N ヶ月 / ヵ月 / か月 / カ月' → N;'なし' → 0;'ご相談' / '' → null。 */ +export const parseMonths = (text: string | null): number | null => { + if (text === null) { + return null; + } + const s = text.replaceAll(/\s/g, ''); + if (/なし|無し/.test(s)) { + return 0; + } + const m = s.match(/(\d+(?:\.\d+)?)[ヶヵかカケ]月/); + return m?.[1] ? Number(m[1]) : null; +}; + +/** '10ヶ月 / なし' (保証金 / 敷金) → sum of the known parts; all unknown → null. */ +export const parseMonthsSum = (text: string | null): number | null => { + if (text === null) { + return null; + } + const known = text + .split(/[//]/) + .map((p) => parseMonths(clean(p))) + .filter((p): p is number => p !== null); + return known.length === 0 ? null : known.reduce((a, b) => a + b, 0); +}; + +/** '徒歩6分' / '3分' → 6 / 3。 */ +export const parseWalkMin = (text: string | null): number | null => { + const m = text?.match(/(\d+)\s*分/); + return m?.[1] ? Number(m[1]) : null; +}; + +/** '3階' → '3F','B1階' → 'B1F','1〜2階' → '1F〜2F'。 */ +export const normalizeFloor = (text: string | null): string | null => { + const tokens = text?.match(/(?:B|-)?\d+/gi); + if (!tokens) { + return null; + } + return tokens.map((t) => (/^(?:B|-)/i.test(t) ? `B${t.slice(1)}F` : `${t}F`)).join('〜'); +}; + +/** '2026年09月10日' / '2026/09/11' → '2026-09-10'。 */ +export const parseYmd = (text: string | null): string | null => { + const m = text?.match(/(\d{4})[./年-](\d{1,2})[./月-](\d{1,2})/); + if (!m?.[1] || !m[2] || !m[3]) { + return null; + } + return `${m[1]}-${m[2].padStart(2, '0')}-${m[3].padStart(2, '0')}`; +}; + +/** '東京都港区麻布十番二丁目' → '港区';'藤沢市鵠沼石上1丁目' → '藤沢市'。 */ +export const parseWard = (address: string | null): string | null => { + if (address === null) { + return null; + } + const s = address.replace(/^(?:東京都|北海道|(?:京都|大阪)府|\S{2,3}県)/, ''); + // 区 first, then 市 / 郡, then 町 / 村, so 東村山市 does not become 東村. + for (const re of [/^(.+?区)/, /^(.+?市)/, /^(.+?郡)/, /^(.+?(?:町|村))/]) { + const m = s.match(re); + if (m?.[1]) { + return m[1]; + } + } + return null; +}; + +/** 現況:居抜き → inuki;スケルトン → skeleton;otherwise null. */ +export const parseCondition = (...texts: Array): ListingCondition | null => { + const s = texts.filter((t): t is string => t !== null).join(' '); + if (/居抜き|居抜/.test(s)) { + return 'inuki'; + } + if (s.includes('スケルトン')) { + return 'skeleton'; + } + return null; +}; + +/** '重飲食可' → true;'重飲食不可' / '飲食不可' → false;plain '飲食可' → null. */ +export const parseHeavyFood = (...texts: Array): boolean | null => { + const s = texts.filter((t): t is string => t !== null).join(' '); + // Negation first: '重飲食不可' also contains '重飲食'; '重飲食・ライブハウス不可' counts too. + if (/重飲食(?:・[^\s【】/]+)?\s*(?:不可|NG|×)|飲食\s*不可/.test(s)) { + return false; + } + if (/重飲食\s*(?:可|OK|○)/.test(s)) { + return true; + } + return null; +}; + +/** '… 【何業も可】【重飲食不可】' → ['何業も可', '重飲食不可']。 */ +export const bracketNotes = (text: string | null): string[] => + (text ?? '') + .matchAll(/【([^】]+)】/g) + .toArray() + .map((m) => m[1].trim()) + .filter(Boolean); + +/** 賃料 ÷ 坪 when the site does not give 坪単価. */ +export const tsuboUnit = (rentJpy: number | null, tsubo: number | null): number | null => (rentJpy !== null && tsubo !== null && tsubo > 0 ? Math.round(rentJpy / tsubo) : null); + +export const sumKnown = (a: number | null, b: number | null): number | null => { + if (a === null && b === null) { + return null; + } + return (a ?? 0) + (b ?? 0); +}; + +/** Structured one-line summary used as the item description. */ +export const summarize = (x: ListingExtra): string => + [ + x.rent_jpy === null ? null : `賃料 ${x.rent_jpy.toLocaleString('ja-JP')}円`, + x.tsubo === null ? null : `${x.tsubo}坪`, + x.floor, + x.station === null ? null : `${x.station}駅 徒歩${x.walk_min ?? '?'}分`, + x.condition === 'inuki' ? '居抜き' : x.condition === 'skeleton' ? 'スケルトン' : null, + x.prev_business === null ? null : `現況 ${x.prev_business}`, + x.fixtures_transfer_jpy === null ? null : `造作 ${x.fixtures_transfer_jpy === 0 ? '無償' : `${x.fixtures_transfer_jpy.toLocaleString('ja-JP')}円`}`, + x.ward, + ] + .filter((p): p is string => p !== null) + .join(' / '); From 56f659c3c21cce207f08763d30d1bd7047e77c87 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:14:41 +0900 Subject: [PATCH 0777/1190] feat(route): add inuki-ichiba new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /inuki-ichiba/rent/:pref? for 居抜き市場 (inuki-ichiba.jp) new listings, site-wide or per prefecture (一都三県). Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/inuki-ichiba/namespace.ts | 12 ++ lib/routes/inuki-ichiba/rent.ts | 231 +++++++++++++++++++++++++++ lib/routes/inuki-ichiba/utils.ts | 176 ++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 lib/routes/inuki-ichiba/namespace.ts create mode 100644 lib/routes/inuki-ichiba/rent.ts create mode 100644 lib/routes/inuki-ichiba/utils.ts diff --git a/lib/routes/inuki-ichiba/namespace.ts b/lib/routes/inuki-ichiba/namespace.ts new file mode 100644 index 000000000000..07bb23b209a5 --- /dev/null +++ b/lib/routes/inuki-ichiba/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Inuki Ichiba', + url: 'inuki-ichiba.jp', + description: '居抜き市場 — 一都三県の居抜き店舗・貸店舗物件情報サイト', + lang: 'ja', + + ja: { + name: '居抜き市場', + }, +}; diff --git a/lib/routes/inuki-ichiba/rent.ts b/lib/routes/inuki-ichiba/rent.ts new file mode 100644 index 000000000000..7423773ed9a0 --- /dev/null +++ b/lib/routes/inuki-ichiba/rent.ts @@ -0,0 +1,231 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; + +import type { ListingExtra } from './utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from './utils'; + +// `www.` 301s to the bare domain. +const HOST = 'https://inuki-ichiba.jp'; +const DETAIL_CONCURRENCY = 2; +const PAGE_SIZE = 20; + +/** 一都三県 — the only prefectures in the site's search form (`location[][prefecture]`). */ +const PREFECTURES = [ + { slug: 'tokyo', code: '13', label: '東京都' }, + { slug: 'kanagawa', code: '14', label: '神奈川県' }, + { slug: 'saitama', code: '11', label: '埼玉県' }, + { slug: 'chiba', code: '12', label: '千葉県' }, +]; + +interface ListCard { + title: string; + link: string; + extra: ListingExtra; +} + +interface DetailFields { + deposit: string | null; // 敷金・保証金 '160万円' | '3ヶ月' + fixtures: string | null; // 造作代金 '無償譲渡' + food_condition: string | null; // 飲食条件 '重飲食可' + type: string | null; // 物件タイプ/現業態 +} + +/** + * List page (`/sp_rent/1`, `/rent/search-results`) renders a PC and an SP copy; only `.property_list.pc_only` is read. + * card .property_box, title/link h3.title a ('浅草橋駅 | 徒歩6分 | 台東区浅草橋5丁目', href '/rent/21712'), + * rows .detail_box .detail → .head label + .item value: + * 駅 a(路線) a(駅) '徒歩6分' | 賃料 '352,000円(税込)' | 造作価格 '無償譲渡' | '相談' (row absent when none) + * 物件タイプ/現業態 '居抜き / 居酒屋' | 'スケルトン' | エリア a(区/市) a(町) | 階数/面積 '1F / 16.55坪 (54.74㎡)' + * tags .tag_list li (NEW, 値下げ), .picto_list .picto (路面店) + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('.property_list.pc_only .property_box') + .toArray() + .map((el) => { + const $el = $(el); + const a = $el.find('h3.title a').first(); + const href = a.attr('href'); + const title = clean(a.text()); + const id = href?.match(/\/rent\/(\d+)/)?.[1]; + if (!href || !title || !id) { + return null; + } + + const rows = new Map>(); + for (const row of $el.find('.detail_box .detail').toArray()) { + const head = clean($(row).find('.head').first().text()); + if (head) { + rows.set(head, $(row).find('.item').first()); + } + } + const text = (label: string): string | null => clean(rows.get(label)?.text()); + const links = (label: string): string[] => (rows.get(label)?.find('a').toArray() ?? []).map((x) => clean($(x).text())).filter((x): x is string => x !== null); + + const [line, station] = links('駅'); + const [ward, town] = links('エリア'); + const [floorText, areaText] = (text('階数/面積') ?? '').split(/\s*\/\s*/, 2); + const typeText = text('物件タイプ/現業態'); + const [typeKind, typeBusiness] = (typeText ?? '').split(/\s*\/\s*/, 2); + + const raw: ListingExtra['raw'] = { + station: text('駅'), + rent: text('賃料'), + fixtures: text('造作価格'), + type: typeText, + area_row: text('エリア'), + floor_area: text('階数/面積'), + }; + const tags = [...$el.find('.tag_list li').toArray(), ...$el.find('.picto_list .picto').toArray()].map((t) => clean($(t).text())).filter((t): t is string => t !== null); + + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(clean(areaText)); + + const extra: ListingExtra = { + source: 'inuki-ichiba', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(floorText)), + station: station?.replace(/駅$/, '') ?? null, + line: line ?? null, + walk_min: parseWalkMin(raw.station), + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: parseJpy(raw.fixtures), + condition: parseCondition(typeKind ?? null), + prev_business: clean(typeBusiness), + heavy_food_ok: null, + business_limit: null, + listed_at: null, + ward: ward ?? parseWard(title.split('|').pop()?.trim() ?? null), + address_hint: ward && town ? `${ward}${town}` : (ward ?? null), + tags, + raw, + }; + return { title, link: new URL(href, HOST).href, extra }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page (non-member view): `.detail .head` label → sibling `.item`. 物件概要 and the map are members-only. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const item = (label: string): string | null => { + const head = $('.detail .head') + .toArray() + .find((el) => clean($(el).text()) === label); + return head ? clean($(head).siblings('.item').first().text()) : null; + }; + return { + deposit: item('敷金・保証金'), + fixtures: item('造作代金'), + food_condition: item('飲食条件'), + type: item('物件タイプ/現業態'), + }; +}; + +/** 敷金・保証金 is either an amount ('160万円') or months ('3ヶ月'); they land in deposit_jpy / deposit_months respectively. */ +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ + ...base, + deposit_months: parseMonths(d.deposit), + deposit_jpy: parseJpy(d.deposit), + fixtures_transfer_jpy: base.fixtures_transfer_jpy ?? parseJpy(d.fixtures), + heavy_food_ok: parseHeavyFood(d.food_condition), + business_limit: d.food_condition, + raw: { + ...base.raw, + deposit: d.deposit, + fixtures_detail: d.fixtures, + food_condition: d.food_condition, + type_detail: d.type, + }, +}); + +/** A failed detail page (e.g. delisted 404) keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`inuki-ichiba: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string | undefined = ctx.req.param('pref'); + const prefecture = pref === undefined ? undefined : PREFECTURES.find((p) => p.slug === pref || p.code === pref); + if (pref !== undefined && !prefecture) { + throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : PAGE_SIZE, PAGE_SIZE); + // `sort=id` is the 新着順 option of the search results page. + const listUrl = prefecture ? `${HOST}/rent/search-results?location%5B${prefecture.code}%5D%5Bprefecture%5D=${prefecture.code}&sort=id` : `${HOST}/sp_rent/1`; + + const cards = parseList(await ofetch(listUrl)).slice(0, limit); + + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + // The site does not publish a listing date; no pubDate is fabricated. + description: summarize(extra), + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `居抜き市場 新着物件${prefecture ? ` (${prefecture.label})` : ''}`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/rent/:pref?', + name: '新着物件', + url: 'inuki-ichiba.jp', + maintainers: ['pseudoyu'], + handler, + example: '/inuki-ichiba/rent/tokyo', + parameters: { + pref: { + description: '都道府県 slug or JIS X 0401 code; omit for the site-wide 新着物件 page (一都三県 mixed)', + options: PREFECTURES.map((p) => ({ value: p.slug, label: `${p.label} (${p.code})` })), + }, + }, + description: `New listings on 居抜き市場,20 per page (first page only). With a prefecture the search results are sorted by 新着順;without one the site's 新着物件 page is used. Each item's \`_extra\` carries the structured listing fields (賃料,坪,階,最寄駅,敷金・保証金,造作価格,物件タイプ,現業態,飲食条件,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 20 | 20 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['inuki-ichiba.jp/sp_rent/1', 'inuki-ichiba.jp/'], + target: '/rent', + }, + ], +}; diff --git a/lib/routes/inuki-ichiba/utils.ts b/lib/routes/inuki-ichiba/utils.ts new file mode 100644 index 000000000000..8be3570da9f9 --- /dev/null +++ b/lib/routes/inuki-ichiba/utils.ts @@ -0,0 +1,176 @@ +/** + * Text → structure helpers for 居抜き市場 listing pages. + * Unknown values are `null`, never `0`; site text is kept verbatim in `raw`. + */ + +export type ListingCondition = 'inuki' | 'skeleton'; + +export interface ListingExtra { + source: 'inuki-ichiba'; + listing_id: string; + rent_jpy: number | null; // 賃料(税込、円/月) + tsubo: number | null; // 坪 + area_m2: number | null; // ㎡ + tsubo_unit_jpy: number | null; // 坪単価(賃料 ÷ 坪) + floor: string | null; // 1F / B1F / 1F〜2F + station: string | null; // 最寄駅(without 駅) + line: string | null; // 路線 + walk_min: number | null; // 徒歩(分) + deposit_months: number | null; // 敷金・保証金(賃料の何ヶ月分) + deposit_jpy: number | null; // 敷金・保証金(金額) + key_money_months: number | null; // 礼金(site does not publish; always null) + fixtures_transfer_jpy: number | null; // 造作価格;無償譲渡 = 0,相談 = null + condition: ListingCondition | null; // 物件タイプ + prev_business: string | null; // 現業態 + heavy_food_ok: boolean | null; // 飲食条件 → 重飲食可否 + business_limit: string | null; // 飲食条件(原文) + listed_at: string | null; // site does not publish; always null + ward: string | null; // 区 / 市 + address_hint: string | null; // 区 + 町 + tags: string[]; + raw: Record; +} + +const TSUBO_M2 = 3.30579; + +/** Collapse whitespace from cheerio `.text()`; empty → null. */ +export const clean = (text: string | undefined | null): string | null => { + const s = (text ?? '').replaceAll(/\s+/g, ' ').trim(); + return s === '' ? null : s; +}; + +const isUnknown = (text: string): boolean => /^(?:[--―—]|ご?相談|要相談|応相談|未定|非公開)?$/.test(text); + +/** '352,000円(税込)' → 352000;'160万円' → 1600000;'無償譲渡' → 0;'相談' / '' → null。 */ +export const parseJpy = (text: string | null): number | null => { + if (text === null) { + return null; + } + const s = text.replaceAll(/[,,\s]/g, ''); + if (/無償|なし|無し/.test(s)) { + return 0; + } + if (isUnknown(s)) { + return null; + } + // Without 円 / 万 only a bare number is accepted ('3ヶ月' is not an amount). + if (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s)) { + return null; + } + const oku = s.match(/^(\d+(?:\.\d+)?)億(?:(\d+(?:\.\d+)?)万)?/); + if (oku) { + return Math.round(Number(oku[1]) * 1e8 + (oku[2] ? Number(oku[2]) * 1e4 : 0)); + } + const m = s.match(/\d+(?:\.\d+)?/); + if (!m) { + return null; + } + const n = Number(m[0]); + return Math.round(s.includes('万') ? n * 1e4 : n); +}; + +const round2 = (n: number): number => Math.round(n * 100) / 100; + +/** '16.55坪 (54.74㎡)' / '70㎡' → { tsubo, area_m2 }, the missing side converted at 3.30579. */ +export const parseArea = (text: string | null): { tsubo: number | null; area_m2: number | null } => { + if (text === null) { + return { tsubo: null, area_m2: null }; + } + const s = text.replaceAll(/[,,]/g, ''); + const tsubo = s.match(/(\d+(?:\.\d+)?)\s*坪/)?.[1]; + const m2 = s.match(/(\d+(?:\.\d+)?)\s*(?:㎡|m²|m2|平米)/)?.[1]; + const t = tsubo ? Number(tsubo) : m2 ? round2(Number(m2) / TSUBO_M2) : null; + const a = m2 ? Number(m2) : tsubo ? round2(Number(tsubo) * TSUBO_M2) : null; + return { tsubo: t, area_m2: a }; +}; + +/** 'N ヶ月 / ヵ月 / か月 / カ月' → N;'なし' → 0;'相談' / '' → null。 */ +export const parseMonths = (text: string | null): number | null => { + if (text === null) { + return null; + } + const s = text.replaceAll(/\s/g, ''); + if (/なし|無し/.test(s)) { + return 0; + } + const m = s.match(/(\d+(?:\.\d+)?)[ヶヵかカケ]月/); + return m?.[1] ? Number(m[1]) : null; +}; + +/** '徒歩6分' → 6。 */ +export const parseWalkMin = (text: string | null): number | null => { + const m = text?.match(/(\d+)\s*分/); + return m?.[1] ? Number(m[1]) : null; +}; + +/** '1F' → '1F','-1F' → 'B1F','1F~2F' → '1F〜2F'。 */ +export const normalizeFloor = (text: string | null): string | null => { + const tokens = text?.match(/(?:B|-)?\d+/gi); + if (!tokens) { + return null; + } + return tokens.map((t) => (/^(?:B|-)/i.test(t) ? `B${t.slice(1)}F` : `${t}F`)).join('〜'); +}; + +/** '台東区浅草橋5丁目' → '台東区';'藤沢市鵠沼石上1丁目' → '藤沢市'。 */ +export const parseWard = (address: string | null): string | null => { + if (address === null) { + return null; + } + const s = address.replace(/^(?:東京都|北海道|(?:京都|大阪)府|\S{2,3}県)/, ''); + // 区 first, then 市 / 郡, then 町 / 村, so 東村山市 does not become 東村. + for (const re of [/^(.+?区)/, /^(.+?市)/, /^(.+?郡)/, /^(.+?(?:町|村))/]) { + const m = s.match(re); + if (m?.[1]) { + return m[1]; + } + } + return null; +}; + +/** 物件タイプ:居抜き → inuki;スケルトン → skeleton;otherwise null. */ +export const parseCondition = (text: string | null): ListingCondition | null => { + if (text === null) { + return null; + } + if (/居抜き|居抜/.test(text)) { + return 'inuki'; + } + if (text.includes('スケルトン')) { + return 'skeleton'; + } + return null; +}; + +/** 飲食条件:'重飲食可' → true;'重飲食不可' / '飲食不可' → false;plain '飲食可' → null. */ +export const parseHeavyFood = (text: string | null): boolean | null => { + if (text === null) { + return null; + } + // Negation first: '重飲食不可' also contains '重飲食'. + if (/重飲食(?:・[^\s/]+)?\s*(?:不可|NG|×)|飲食\s*不可/.test(text)) { + return false; + } + if (/重飲食\s*(?:可|OK|○)/.test(text)) { + return true; + } + return null; +}; + +/** 賃料 ÷ 坪 (the site does not give 坪単価). */ +export const tsuboUnit = (rentJpy: number | null, tsubo: number | null): number | null => (rentJpy !== null && tsubo !== null && tsubo > 0 ? Math.round(rentJpy / tsubo) : null); + +/** Structured one-line summary used as the item description. */ +export const summarize = (x: ListingExtra): string => + [ + x.rent_jpy === null ? null : `賃料 ${x.rent_jpy.toLocaleString('ja-JP')}円`, + x.tsubo === null ? null : `${x.tsubo}坪`, + x.floor, + x.station === null ? null : `${x.station}駅 徒歩${x.walk_min ?? '?'}分`, + x.condition === 'inuki' ? '居抜き' : x.condition === 'skeleton' ? 'スケルトン' : null, + x.prev_business === null ? null : `現況 ${x.prev_business}`, + x.fixtures_transfer_jpy === null ? null : `造作 ${x.fixtures_transfer_jpy === 0 ? '無償' : `${x.fixtures_transfer_jpy.toLocaleString('ja-JP')}円`}`, + x.ward, + ] + .filter((p): p is string => p !== null) + .join(' / '); From bc2a35783a42f6dcd2e354828c633722958ddc00 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:14:45 +0900 Subject: [PATCH 0778/1190] feat(route): add lg/tokyo food business permits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /lg/tokyo/food-permit/:ward? aggregating newly granted food business permits from Tokyo ward open data (渋谷区 ArcGIS FeatureServer, 港区 CSV; both CC BY). The lg namespace mirrors the existing go (go.jp) namespace for lg.jp local governments. --- lib/routes/lg/namespace.ts | 13 +++ lib/routes/lg/tokyo/food-permit.ts | 178 +++++++++++++++++++++++++++++ lib/routes/lg/tokyo/utils.ts | 57 +++++++++ 3 files changed, 248 insertions(+) create mode 100644 lib/routes/lg/namespace.ts create mode 100644 lib/routes/lg/tokyo/food-permit.ts create mode 100644 lib/routes/lg/tokyo/utils.ts diff --git a/lib/routes/lg/namespace.ts b/lib/routes/lg/namespace.ts new file mode 100644 index 000000000000..5f6a8c963f1f --- /dev/null +++ b/lib/routes/lg/namespace.ts @@ -0,0 +1,13 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Japan Local Government', + url: 'lg.jp', + categories: ['government'], + description: 'Local governments of Japan (`lg.jp`)', + lang: 'ja', + + ja: { + name: '地方公共団体', + }, +}; diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts new file mode 100644 index 000000000000..a53015ed4ea0 --- /dev/null +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -0,0 +1,178 @@ +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { csvRecords } from './utils'; + +/** + * 飲食店営業許可 (food business permits) newly granted in Tokyo wards, from each ward's CC BY open data. + * + * Only two of the 23 wards publish a machine-readable, regularly updated 台帳: + * shibuya — 渋谷区 ArcGIS FeatureServer (complete ledger incl. 廃業; the current and previous month are queried) + * minato — 港区 自治体標準オープンデータ CSV (snapshot of valid permits, monthly, ~2 MB) + * Rows are the publisher's original columns and are exposed verbatim in `_extra.raw`. + */ + +type Source = 'shibuya' | 'minato'; +type Row = Record; + +export interface PermitExtra { + source: Source; + ward: string; + permit_no: string; + name: string; + address: string | null; + permit_date: string | null; // YYYY-MM-DD + business_type: string | null; + raw: Row; +} + +const SOURCES: Record = { + shibuya: { + label: '渋谷区', + link: 'https://city-shibuya-data.opendata.arcgis.com/items/e68f41ebfa5f4ea490ca9af701d44e02', + }, + minato: { + label: '港区', + link: 'https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244', + }, +}; +const SHIBUYA_QUERY = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_food_businesses_list/FeatureServer/0/query'; +const SHIBUYA_DATE = '許可開始日もしくは届出受理日'; +const MINATO_CSV = 'https://opendata.city.minato.tokyo.jp/dataset/54d8c582-00e2-4730-a23f-4a5befec9ae5/resource/c9d0299e-8e05-4317-877f-83055709e41f/download/food_business_all.csv'; +const DEFAULT_LIMIT = 100; +const MAX_LIMIT = 500; +const DATE_COLS = ['許可年月日', SHIBUYA_DATE, '許可開始日', '許可日']; + +const pick = (row: Row, cols: readonly string[]): string | null => { + for (const c of cols) { + const v = row[c]?.trim(); + if (v) { + return v; + } + } + return null; +}; + +/** `2024-04-08` / `2026/8/7` → `YYYY-MM-DD`; anything else (和暦 …) → null. */ +const isoDate = (raw: string | null): string | null => { + const m = raw === null ? null : /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/.exec(raw.normalize('NFKC')); + return m ? `${m[1]}-${m[2].padStart(2, '0')}-${m[3].padStart(2, '0')}` : null; +}; + +/** 渋谷区 stores dates as text (`2026/8/7`), so the current and previous month (JST) are selected with LIKE and sorted locally. */ +const shibuyaQueryUrl = (now: Date): string => { + const jst = new Date(now.getTime() + 9 * 3600 * 1000); + const months = [0, 1].map((back) => { + const d = new Date(Date.UTC(jst.getUTCFullYear(), jst.getUTCMonth() - back, 1)); + return `${d.getUTCFullYear()}/${d.getUTCMonth() + 1}/`; + }); + const params = new URLSearchParams({ + where: months.map((m) => `${SHIBUYA_DATE} LIKE '${m}%'`).join(' OR '), + outFields: '*', + returnGeometry: 'false', + resultRecordCount: '1000', + f: 'json', + }); + return `${SHIBUYA_QUERY}?${params.toString()}`; +}; + +const fetchShibuya = async (): Promise => { + const body = await ofetch(shibuyaQueryUrl(new Date())); + const features: Array<{ attributes: Record }> = body?.features ?? []; + return features.map((f) => Object.fromEntries(Object.entries(f.attributes).map(([k, v]) => [k, v === null || v === undefined ? null : String(v)]))); +}; + +const fetchMinato = async (): Promise => csvRecords(await ofetch(MINATO_CSV, { responseType: 'text' })); + +const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => { + const permitNo = pick(raw, ['許可番号']) ?? ''; + const name = pick(raw, ['施設名称', '屋号']) ?? '(名称なし)'; + const address = pick(raw, ['所在地_連結表記', '施設所在地_連結表記']); + const permitDate = isoDate(pick(raw, DATE_COLS)); + const businessType = pick(raw, ['営業の種類', '営業の種類もしくは営業の形態', '業種']); + const ward = (pick(raw, ['施設所在地_市区町村', '地方公共団体名']) ?? SOURCES[source].label).replace(/^東京都/, ''); + return { + title: `${name}(${businessType ?? '業種不明'})`, + guid: `lg/tokyo/food-permit:${source}:${permitNo}`, + link: SOURCES[source].link, + pubDate: permitDate === null ? undefined : timezone(parseDate(permitDate, 'YYYY-MM-DD'), 9), + description: [ward, address, businessType, permitDate, `許可番号 ${permitNo}`].filter(Boolean).join(' / '), + _extra: { source, ward, permit_no: permitNo, name, address, permit_date: permitDate, business_type: businessType, raw }, + }; +}; + +/** Newest `limit` 許可 rows of one source; 届出 rows are not an opening signal and are skipped. */ +const fetchSource = async (source: Source, limit: number): Promise> => { + try { + const rows = source === 'shibuya' ? await fetchShibuya() : await fetchMinato(); + return rows + .filter((r) => (r['許可番号'] ?? '') !== '' && (r['許可あるいは届出'] ?? '許可') === '許可') + .map((raw) => toItem(source, raw)) + .filter((it) => it._extra.permit_date !== null) + .toSorted((a, b) => b._extra.permit_date!.localeCompare(a._extra.permit_date!)) + .slice(0, limit); + } catch (error) { + // One failing publisher must not take the whole feed down. + logger.warn(`lg/tokyo/food-permit: ${source} failed: ${String(error)}`); + return []; + } +}; + +export const handler = async (ctx): Promise => { + const ward: string | undefined = ctx.req.param('ward'); + const sources: Source[] = ward === undefined ? (Object.keys(SOURCES) as Source[]) : Object.hasOwn(SOURCES, ward) ? [ward as Source] : []; + if (sources.length === 0) { + throw new Error(`Unknown ward "${ward}", expected one of ${Object.keys(SOURCES).join(', ')}`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, MAX_LIMIT); + + const lists = await Promise.all(sources.map((s) => cache.tryGet(`lg/tokyo/food-permit:${s}:${limit}`, () => fetchSource(s, limit)) as Promise>)); + const items = lists.flat().toSorted((a, b) => (b._extra.permit_date ?? '').localeCompare(a._extra.permit_date ?? '')); + + return { + title: `東京都 飲食店営業許可 新規${ward ? ` (${SOURCES[ward as Source].label})` : ''}`, + link: 'https://catalog.data.metro.tokyo.lg.jp/', + language: 'ja', + item: items, + allowEmpty: true, + }; +}; + +export const route: Route = { + path: '/tokyo/food-permit/:ward?', + name: '東京都 飲食店営業許可 新規', + url: 'catalog.data.metro.tokyo.lg.jp', + maintainers: ['pseudoyu'], + handler, + example: '/lg/tokyo/food-permit', + parameters: { + ward: { + description: 'Ward; omit for all sources', + options: [ + { value: 'shibuya', label: '渋谷区' }, + { value: 'minato', label: '港区' }, + ], + }, + }, + description: `Newly granted food business permits (飲食店営業許可 etc.) in Tokyo wards, from each ward's CC BY open data: + +- 渋谷区: [食品営業許可施設一覧 (ArcGIS FeatureServer)](https://city-shibuya-data.opendata.arcgis.com/items/e68f41ebfa5f4ea490ca9af701d44e02) — current and previous month +- 港区: [食品営業許可一覧 (CSV)](https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244) — monthly snapshot of valid permits, newest first + +Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\` and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). + +| Query | Description | Default | +| ------- | ------------------------------------- | ------- | +| \`limit\` | Number of permits per source, max 500 | 100 |`, + categories: ['government'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: false, + }, +}; diff --git a/lib/routes/lg/tokyo/utils.ts b/lib/routes/lg/tokyo/utils.ts new file mode 100644 index 000000000000..560dfcce89ff --- /dev/null +++ b/lib/routes/lg/tokyo/utils.ts @@ -0,0 +1,57 @@ +/** Minimal RFC 4180 CSV parser: quoted fields, `""` escapes, newlines inside quotes, CRLF. */ +const parseCsv = (text: string): string[][] => { + const rows: string[][] = []; + let row: string[] = []; + let field = ''; + let quoted = false; + for (let i = 0; i < text.length; i++) { + const c = text[i]; + if (quoted) { + if (c !== '"') { + field += c; + } else if (text[i + 1] === '"') { + field += '"'; + i++; + } else { + quoted = false; + } + continue; + } + switch (c) { + case '"': + quoted = true; + break; + case ',': + row.push(field); + field = ''; + break; + case '\r': + case '\n': + if (c === '\r' && text[i + 1] === '\n') { + i++; + } + row.push(field); + rows.push(row); + row = []; + field = ''; + break; + default: + field += c; + } + } + if (field !== '' || row.length > 0) { + row.push(field); + rows.push(row); + } + return rows; +}; + +/** CSV text → records keyed by the (trimmed) header row; a missing cell is `null`. */ +export const csvRecords = (text: string): Array> => { + const [header, ...rows] = parseCsv(text); + if (!header) { + return []; + } + const keys = header.map((h) => h.trim()); + return rows.filter((r) => r.length > 1).map((r) => Object.fromEntries(keys.map((k, i) => [k, r[i] ?? null]))); +}; From 8de785cc42037d0dff4070703cbc18a056fa3384 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:32:06 +0900 Subject: [PATCH 0779/1190] refactor(route/temposmart): generalize ListingExtra source for reuse Widen ListingExtra.source to string so the other Japanese restaurant-property listing routes can import these helpers instead of duplicating them. --- lib/routes/temposmart/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/temposmart/utils.ts b/lib/routes/temposmart/utils.ts index ee45efccbda7..bef2adbb238a 100644 --- a/lib/routes/temposmart/utils.ts +++ b/lib/routes/temposmart/utils.ts @@ -1,12 +1,12 @@ /** - * Text → structure helpers for テンポスマート listing pages. + * Text → structure helpers for Japanese restaurant-property listing pages (shared by the JP tenant-listing routes). * Unknown values are `null`, never `0`; site text is kept verbatim in `raw`. */ export type ListingCondition = 'inuki' | 'skeleton'; export interface ListingExtra { - source: 'temposmart'; + source: string; // site id, e.g. 'temposmart' listing_id: string; rent_jpy: number | null; // 賃料(税込、円/月) tsubo: number | null; // 坪 From 50b285c125b430dd91011f2f4653aa9ee0d14513 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:37:00 +0900 Subject: [PATCH 0780/1190] feat(route): add inshokuten new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /inshokuten/bukken/:area? for 飲食店.COM (www.inshokuten.com) new restaurant-property listings by region or 首都圏 sub-area. Structured listing fields parsed from the list page are exposed in item._extra; no detail pages are fetched since the extra fields there are members-only. --- lib/routes/inshokuten/bukken.ts | 161 +++++++++++++++++++++++++++++ lib/routes/inshokuten/namespace.ts | 12 +++ 2 files changed, 173 insertions(+) create mode 100644 lib/routes/inshokuten/bukken.ts create mode 100644 lib/routes/inshokuten/namespace.ts diff --git a/lib/routes/inshokuten/bukken.ts b/lib/routes/inshokuten/bukken.ts new file mode 100644 index 000000000000..e68bead03be3 --- /dev/null +++ b/lib/routes/inshokuten/bukken.ts @@ -0,0 +1,161 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const HOST = 'https://www.inshokuten.com'; + +/** Region is the first path segment; the 首都圏 sub-areas are an extra `local-*` segment after `list/`. */ +const AREAS = [ + { slug: 'kanto', label: '首都圏', path: 'kanto/bukkens/list' }, + { slug: '23ward', label: '東京23区', path: 'kanto/bukkens/list/local-23ward' }, + { slug: '23ward_out', label: '東京都下', path: 'kanto/bukkens/list/local-23ward_out' }, + { slug: 'yokohama_kawasaki', label: '神奈川', path: 'kanto/bukkens/list/local-yokohama_kawasaki' }, + { slug: 'chiba', label: '千葉', path: 'kanto/bukkens/list/local-chiba' }, + { slug: 'saitama', label: '埼玉', path: 'kanto/bukkens/list/local-saitama' }, + { slug: 'kansai', label: '関西', path: 'kansai/bukkens/list' }, + { slug: 'tokai', label: '東海', path: 'tokai/bukkens/list' }, + { slug: 'kyushu', label: '九州', path: 'kyushu/bukkens/list' }, +]; + +/** + * List page cards (`a.bukkenItem`, 20 per page, server-rendered): + * title .bukkenItem__title, id input.js-bukkenKeyId, rows table.bukkenItem__detailTable th → td: + * 賃料/坪単価 '60万円/28,749円', 階数/面積 '地上2階/20.87坪(69.0m2)', 最寄り駅 '東京メトロ南北線 六本木一丁目 徒歩 3分', + * 所在地 '港区六本木4', 前テナント/希望譲渡額 'コンカフェ/0万円' | '喫茶店/相談' | '飲食店/', + * 現況 .bukkenItem__inukiIcon--inuki | --skeleton, 出店可能業態 .bukkenItem__openableBusinessTypeCategory, + * 登録日 '登録日:2026-09-13', tags .bukkenItem__newIcon / .bukkenItem__restaurantNotOpenableIcon + * 保証金 / 礼金 are members-only on the detail page, so no detail page is fetched. + */ +const parseList = (html: string): DataItem[] => { + const $ = load(html); + return $('a.bukkenItem') + .toArray() + .map((el): DataItem | null => { + const $el = $(el); + const href = $el.attr('href'); + const title = clean($el.find('.bukkenItem__title').text()); + const id = $el.find('input.js-bukkenKeyId').attr('value') ?? href?.match(/\/bukkens\/(\d+)/)?.[1]; + if (!href || !title || !id) { + return null; + } + const cell = (label: string): string | null => clean($el.find(`th:contains("${label}")`).first().next('td').text()); + + const raw: ListingExtra['raw'] = { + rent: cell('賃料/坪単価'), + floor_area: cell('階数/面積'), + station: cell('最寄り駅'), + address: cell('所在地'), + transfer: cell('希望譲渡額'), + business_types: clean( + $el + .find('.bukkenItem__openableBusinessTypeCategory') + .toArray() + .map((t) => clean($(t).text())) + .filter(Boolean) + .join(', ') + ), + listed_at: clean($el.find('div:contains("登録日:")').last().text())?.replace(/^登録日[::]\s*/, '') ?? null, + comment: clean($el.find('.bukkenItem__shortComment').text()), + }; + + const [rentText, tsuboUnitText] = (raw.rent ?? '').split('/', 2); + const [floorText, areaText] = (raw.floor_area ?? '').split('/', 2); + const [prevBusiness, fixturesText] = (raw.transfer ?? '').split('/', 2); + // '東京メトロ南北線 六本木一丁目 徒歩 3分' → line, station (whitespace already collapsed by clean) + const stationParts = raw.station?.split(' ') ?? []; + const walkIndex = stationParts.findIndex((p) => p.startsWith('徒歩')); + const notOpenable = $el.find('.bukkenItem__restaurantNotOpenableIcon').length > 0; + const tags = [$el.find('.bukkenItem__newIcon').length > 0 ? 'NEW' : null, notOpenable ? '飲食店不可' : null].filter((t): t is string => t !== null); + + const rentJpy = parseJpy(clean(rentText)); + const { tsubo, area_m2 } = parseArea(clean(areaText)); + const extra: ListingExtra = { + source: 'inshokuten', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(clean(tsuboUnitText)) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(floorText)?.replaceAll('地下', 'B').replaceAll('地上', '') ?? null), + station: walkIndex > 1 ? stationParts.slice(1, walkIndex).join(' ') : null, + line: walkIndex > 1 ? stationParts[0] : null, + walk_min: parseWalkMin(raw.station), + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: parseJpy(clean(fixturesText)), + condition: $el.find('.bukkenItem__inukiIcon--inuki').length > 0 ? 'inuki' : $el.find('.bukkenItem__inukiIcon--skeleton').length > 0 ? 'skeleton' : null, + prev_business: clean(prevBusiness), + heavy_food_ok: notOpenable ? false : raw.business_types === null ? null : raw.business_types.includes('重飲食'), + business_limit: notOpenable ? '飲食店不可' : raw.business_types, + listed_at: parseYmd(raw.listed_at), + ward: parseWard(raw.address), + address_hint: raw.address, + tags, + raw, + }; + const link = new URL(href, HOST).href; + const image = $el.find('img.bukkenItem__picture').attr('src'); + return { + title, + link, + guid: link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: [raw.comment, summarize(extra)].filter(Boolean).join(' / '), + image: image && !image.includes('no_image') ? image : undefined, + _extra: extra, + }; + }) + .filter((item): item is DataItem => item !== null); +}; + +export const handler = async (ctx): Promise => { + const slug: string = ctx.req.param('area') ?? 'kanto'; + const area = AREAS.find((a) => a.slug === slug); + if (!area) { + throw new Error(`Unknown area "${slug}", expected one of ${AREAS.map((a) => a.slug).join(', ')}`); + } + const listUrl = `${HOST}/bukken/${area.path}/?mode=latest`; + + return { + title: `飲食店.COM 新着物件 (${area.label})`, + link: listUrl, + language: 'ja', + item: parseList(await ofetch(listUrl)), + }; +}; + +export const route: Route = { + path: '/bukken/:area?', + name: '新着物件', + url: 'www.inshokuten.com', + maintainers: ['pseudoyu'], + handler, + example: '/inshokuten/bukken/23ward', + parameters: { + area: { + description: 'Region or 首都圏 sub-area', + default: 'kanto', + options: AREAS.map((a) => ({ value: a.slug, label: a.label })), + }, + }, + description: `New restaurant-property listings on 飲食店.COM sorted by 登録日 (first page, 20 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,造作譲渡料,現況,前業態,出店可能業態,登録日,…); unknown values are \`null\`. 保証金 and 礼金 are members-only on the site and therefore always \`null\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.inshokuten.com/bukken/:region/bukkens/list', 'www.inshokuten.com/bukken/:region/bukkens/list/local-:area'], + target: (params) => `/bukken/${params.area ?? params.region}`, + }, + ], +}; diff --git a/lib/routes/inshokuten/namespace.ts b/lib/routes/inshokuten/namespace.ts new file mode 100644 index 000000000000..512053f870a1 --- /dev/null +++ b/lib/routes/inshokuten/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Inshokuten.com', + url: 'www.inshokuten.com', + description: '飲食店.COM — 飲食店向け居抜き・貸店舗物件情報サイト', + lang: 'ja', + + ja: { + name: '飲食店.COM', + }, +}; From 81bbf98d5a79225b437adfad5568e593d5c591c7 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:38:25 +0900 Subject: [PATCH 0781/1190] feat(route): add bukenavi new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /bukenavi/object/:region?/:pref? for ぶけなび (bukenavi.jp) new 居抜き listings that are currently 募集中, by region and optional prefecture. Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/bukenavi/namespace.ts | 12 ++ lib/routes/bukenavi/object.ts | 234 +++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 lib/routes/bukenavi/namespace.ts create mode 100644 lib/routes/bukenavi/object.ts diff --git a/lib/routes/bukenavi/namespace.ts b/lib/routes/bukenavi/namespace.ts new file mode 100644 index 000000000000..608518d60434 --- /dev/null +++ b/lib/routes/bukenavi/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Bukenavi', + url: 'bukenavi.jp', + description: 'ぶけなび — 飲食店の居抜き物件専門サイト', + lang: 'ja', + + ja: { + name: 'ぶけなび', + }, +}; diff --git a/lib/routes/bukenavi/object.ts b/lib/routes/bukenavi/object.ts new file mode 100644 index 000000000000..7fbdff5343e5 --- /dev/null +++ b/lib/routes/bukenavi/object.ts @@ -0,0 +1,234 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://bukenavi.jp'; +const DETAIL_CONCURRENCY = 2; + +const REGIONS = [ + { slug: 'kanto', label: '関東' }, + { slug: 'kansai', label: '関西' }, + { slug: 'tokai', label: '東海' }, +]; + +/** JIS X 0401 codes accepted as `prefecture[]`; any two-digit code is passed through. */ +const PREFECTURES: Record = { + tokyo: '13', + kanagawa: '14', + saitama: '11', + chiba: '12', + osaka: '27', + kyoto: '26', + hyogo: '28', + aichi: '23', +}; + +interface ListCard { + title: string; + link: string; + extra: ListingExtra; + image?: string; +} + +interface DetailFields { + address: string | null; // 住所 '東京都目黒区目黒 ※詳細はお問い合わせください' + line: string | null; // 沿線 + area: string | null; // 面積 '25.7坪 /84.94m2' + prev_business: string | null; // 前の業態 + business_types: string | null; // 業種(可能) + ng_business: string | null; // 不可業態 + note: string | null; // 特記事項 +} + +/** + * List page (`/{region}/object/list?wanted=1`, 10 per page, 新着順 by default, server-rendered): + * card .box__property, title .box__property__title, link .box__property__info__to-detail a, + * 路線/駅/徒歩 .box__property__info__station 'JR山手線/目黒駅 (徒歩約 4分)', 階 + 坪 .rent__size '1階、2階 25.7 坪', + * 賃料 .rent__price em '105万円', 坪単価 .rent__price .unit-price '@4.09万円', PR .box__property__info__lead + */ +const parseList = (html: string, region: string): ListCard[] => { + const $ = load(html); + return $('.box__property') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const href = $el.find('.box__property__info__to-detail a').attr('href'); + const title = clean($el.find('.box__property__title').text()); + const id = href?.match(/\/(\d+)$/)?.[1]; + if (!href || !title || !id) { + return null; + } + const raw: ListingExtra['raw'] = { + station: clean($el.find('.box__property__info__station').text()), + size: clean($el.find('.rent__size').text()), + rent: clean($el.find('.rent__price em').text()), + tsubo_unit: clean($el.find('.rent__price .unit-price').text()), + lead: clean($el.find('.box__property__info__lead').text()), + }; + const [line, stationPart] = (raw.station ?? '').split('/', 2); + // '1階、2階 25.7 坪' → floor text before the 坪 figure + const tsuboText = raw.size?.match(/\d+(?:\.\d+)?\s*坪/)?.[0] ?? null; + const floorText = tsuboText === null ? null : clean(raw.size?.slice(0, raw.size.indexOf(tsuboText))); + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(tsuboText); + const image = $el.find('.box__property__left__image img').attr('src'); + + return { + title, + // The ID-only URL serves the same page as the station-named one and makes a stable guid. + link: `${HOST}/${region}/object/${id}`, + image: image && !image.includes('/img/') ? image.replace('//storage', '/storage') : undefined, + extra: { + source: 'bukenavi', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(raw.tsubo_unit) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(floorText), + station: clean(stationPart?.replace(/\(.*$/, ''))?.replace(/駅$/, '') ?? null, + line: clean(line), + walk_min: parseWalkMin(raw.station), + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: parseCondition(title), + prev_business: null, + heavy_food_ok: null, + business_limit: null, + listed_at: null, + ward: null, + address_hint: null, + tags: [], + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page: `table.box__property__table tr > th + td`; 住所 is town-level for guests. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const th = $('table.box__property__table th') + .toArray() + .find((el) => clean($(el).text()) === label); + return th ? clean($(th).next('td').text()) : null; + }; + return { + address: cell('住所'), + line: cell('沿線'), + area: cell('面積'), + prev_business: cell('前の業態'), + business_types: cell('業種'), + ng_business: cell('不可業態'), + note: cell('特記事項'), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { + const address = clean(d.address?.replace(/※.*$/, '')) ?? null; + const { area_m2 } = parseArea(d.area); + const limitParts = [d.business_types === null ? null : `可: ${d.business_types}`, d.ng_business === null ? null : `不可: ${d.ng_business}`].filter((p): p is string => p !== null); + return { + ...base, + area_m2: area_m2 ?? base.area_m2, + line: base.line ?? d.line, + prev_business: d.prev_business, + heavy_food_ok: d.ng_business === null ? null : !/飲食/.test(d.ng_business), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, + ward: parseWard(address), + address_hint: address, + raw: { ...base.raw, address: d.address, line: d.line, area: d.area, prev_business: d.prev_business, business_types: d.business_types, ng_business: d.ng_business, note: d.note }, + }; +}; + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`bukenavi: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const regionSlug: string = ctx.req.param('region') ?? 'kanto'; + const region = REGIONS.find((r) => r.slug === regionSlug); + if (!region) { + throw new Error(`Unknown region "${regionSlug}", expected one of ${REGIONS.map((r) => r.slug).join(', ')}`); + } + const pref: string | undefined = ctx.req.param('pref'); + const prefCode = pref === undefined ? undefined : (PREFECTURES[pref] ?? pref); + if (prefCode !== undefined && !/^\d{2}$/.test(prefCode)) { + throw new Error(`Unknown prefecture "${pref}", expected a slug (${Object.keys(PREFECTURES).join(', ')}) or a two-digit JIS code`); + } + // wanted=1 limits the list to 募集中 listings. + const listUrl = `${HOST}/${region.slug}/object/list?wanted=1${prefCode === undefined ? '' : `&prefecture%5B%5D=${prefCode}`}`; + + const cards = parseList(await ofetch(listUrl), region.slug); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + // The site does not publish a listing date; no pubDate is fabricated. + description: [extra.raw.lead, summarize(extra)].filter(Boolean).join(' / '), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `ぶけなび 新着物件 (${region.label}${prefCode === undefined ? '' : ` ${pref}`})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/object/:region?/:pref?', + name: '新着物件', + url: 'bukenavi.jp', + maintainers: ['pseudoyu'], + handler, + example: '/bukenavi/object/kanto/tokyo', + parameters: { + region: { + description: 'Region', + default: 'kanto', + options: REGIONS.map((r) => ({ value: r.slug, label: r.label })), + }, + pref: 'Prefecture slug (tokyo, kanagawa, saitama, chiba, osaka, kyoto, hyogo, aichi) or two-digit JIS X 0401 code; omit for the whole region', + }, + description: `New 居抜き listings on ぶけなび that are currently 募集中,newest first (first page, 10 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,前業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['bukenavi.jp/:region/object/list', 'bukenavi.jp/:region'], + target: '/object/:region', + }, + ], +}; From 5049a3295573644750982ce0bc466628b1607309 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:40:40 +0900 Subject: [PATCH 0782/1190] feat(route): add iri-search new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /iri-search/estate/:area? for 居抜き物件検索 iri-search (www.iri-search.net) new listings nationwide, by region or by 首都圏 prefecture. Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/iri-search/estate.ts | 262 +++++++++++++++++++++++++++++ lib/routes/iri-search/namespace.ts | 12 ++ 2 files changed, 274 insertions(+) create mode 100644 lib/routes/iri-search/estate.ts create mode 100644 lib/routes/iri-search/namespace.ts diff --git a/lib/routes/iri-search/estate.ts b/lib/routes/iri-search/estate.ts new file mode 100644 index 000000000000..0059d0317fd9 --- /dev/null +++ b/lib/routes/iri-search/estate.ts @@ -0,0 +1,262 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const HOST = 'https://www.iri-search.net'; +const DETAIL_CONCURRENCY = 2; +const PAGE_SIZE = 30; + +/** `area_code` values of the search form. */ +const REGIONS = [ + { slug: 'shutoken', code: '1', label: '首都圏' }, + { slug: 'hokkaido', code: '2', label: '北海道' }, + { slug: 'tohoku', code: '3', label: '東北' }, + { slug: 'kitakanto', code: '4', label: '北関東' }, + { slug: 'hokuriku', code: '5', label: '北陸' }, + { slug: 'koshinetsu', code: '6', label: '甲信越' }, + { slug: 'tokai', code: '7', label: '東海' }, + { slug: 'kinki', code: '8', label: '近畿' }, + { slug: 'chugoku', code: '9', label: '中国' }, + { slug: 'shikoku', code: '10', label: '四国' }, + { slug: 'kyushu', code: '11', label: '九州' }, + { slug: 'okinawa', code: '12', label: '沖縄' }, +]; + +/** 首都圏 prefectures (`prefectural_code`, JIS X 0401), the only ones paired with `area_code=1`. */ +const PREFECTURES: Record = { + tokyo: '13', + kanagawa: '14', + saitama: '11', + chiba: '12', +}; + +interface ListCard { + title: string; + link: string; + extra: ListingExtra; +} + +interface DetailFields { + listed_at: string | null; // 掲載日 '2026/09/12' + deposit: string | null; // 保証金・敷金 '2,443,638円' + key_money: string | null; // 礼金 '448,000円' + fixtures: string | null; // 居抜き譲渡代 + status: string | null; // 現況 '空室' + note: string | null; // 備考 +} + +/** + * List page (`/estate_search/index?...&order_by=6&hotlist=1`, 30 per page, server-rendered): each card is a + * `.estate_link_title` row followed by a `table[summary="物件詳細2"]` row of th/td pairs: + * 賃料 '445,500円(税込) (坪単価 11,013円)', 建物面積 '40.45坪(約133.72m²)', フロア '2階', + * 沿線 '東急目黒線 武蔵小山駅 徒歩7分', 所在 '東京都品川区荏原3-5-14', 以前の業態 'スケルトン', 現(前)テナント, + * 業種可否 nested table th.mini_th (サービス/物販/軽飲食/重飲食/娯楽) → td.center ('可' | '不可' | '相談' | '確認中') + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('#search_result .estate_link_title') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const a = $el.find('a').first(); + const href = a.attr('href'); + const title = clean(a.text()); + const id = $el.find('input[name="inquiry[]"]').attr('value') ?? href?.match(/id=(\d+)/)?.[1]; + if (!href || !title || !id) { + return null; + } + const table = $el.closest('tr').next('tr').find('table[summary="物件詳細2"]'); + const cell = (label: string): string | null => { + const th = table + .find('th') + .toArray() + .find((t) => clean($(t).text()) === label); + return th ? clean($(th).next('td').text()) : null; + }; + const permissions = table + .find('th.mini_th') + .toArray() + .map((t) => clean($(t).text())); + const permissionValues = table + .find('th.mini_th') + .first() + .closest('tr') + .next('tr') + .find('td') + .toArray() + .map((t) => clean($(t).text())); + const heavyFood = permissionValues[permissions.indexOf('重飲食')] ?? null; + + const raw: ListingExtra['raw'] = { + rent: cell('賃料'), + area: cell('建物面積'), + floor: cell('フロア'), + station: cell('沿線'), + address: cell('所在'), + prev_business: cell('以前の業態'), + tenant: cell('現(前)テナント'), + building: cell('名称'), + permissions: permissions.length > 0 ? permissions.map((p, i) => `${p}:${permissionValues[i] ?? ''}`).join(' / ') : null, + }; + const [line, station] = (raw.station ?? '').split(' ', 2); + const rentJpy = parseJpy(clean(raw.rent?.replace(/\(.*$/, ''))); + const { tsubo, area_m2 } = parseArea(raw.area); + const tags = $el + .closest('tr') + .next('tr') + .find('td.icon img') + .toArray() + .map((img) => $(img).attr('alt') ?? '') + .filter((alt) => alt !== '' && !alt.includes('ではない')); + + return { + title, + link: new URL(href, HOST).href, + extra: { + source: 'iri-search', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(raw.rent?.match(/坪単価\s*([\d,]+円)/)?.[1] ?? null) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor?.normalize('NFKC').replaceAll('地下', 'B') ?? null), + station: clean(station)?.replace(/駅$/, '') ?? null, + line: clean(line), + walk_min: parseWalkMin(raw.station), + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: parseCondition(raw.prev_business, title), + prev_business: raw.prev_business === 'スケルトン' ? null : raw.prev_business, + heavy_food_ok: heavyFood === '可' ? true : heavyFood === '不可' ? false : null, + business_limit: raw.permissions, + listed_at: null, + ward: parseWard(raw.address), + address_hint: raw.address, + tags, + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page: th/td tables; some labels carry a ` *1` footnote suffix, so match by prefix. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const th = $('th') + .toArray() + .find((t) => clean($(t).text())?.startsWith(label)); + return th ? clean($(th).next('td').text()) : null; + }; + return { + listed_at: cell('掲載日'), + deposit: cell('保証金・敷金'), + key_money: cell('礼金'), + fixtures: cell('居抜き譲渡代'), + status: cell('現況'), + note: cell('備考'), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ + ...base, + deposit_jpy: parseJpy(d.deposit), + fixtures_transfer_jpy: parseJpy(d.fixtures), + listed_at: parseYmd(d.listed_at), + raw: { ...base.raw, listed_at: d.listed_at, deposit: d.deposit, key_money: d.key_money, fixtures: d.fixtures, status: d.status, note: d.note }, +}); + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`iri-search: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const area: string | undefined = ctx.req.param('area'); + const region = area === undefined ? undefined : REGIONS.find((r) => r.slug === area); + const prefCode = area === undefined || region ? undefined : (PREFECTURES[area] ?? (Object.values(PREFECTURES).includes(area) ? area : undefined)); + if (area !== undefined && !region && !prefCode) { + throw new Error(`Unknown area "${area}", expected a region (${REGIONS.map((r) => r.slug).join(', ')}) or a 首都圏 prefecture (${Object.keys(PREFECTURES).join(', ')})`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : PAGE_SIZE, PAGE_SIZE); + const query = new URLSearchParams({ page_start_num: '0', order_by: '6', hotlist: '1' }); + if (region) { + query.set('area_code', region.code); + } else if (prefCode) { + query.set('area_code', '1'); + query.set('prefectural_code', prefCode); + } + const listUrl = `${HOST}/estate_search/index?${query.toString()}`; + + const cards = parseList(await ofetch(listUrl)).slice(0, limit); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: summarize(extra), + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + const scope = region?.label ?? (prefCode === undefined ? '全国' : (Object.keys(PREFECTURES).find((k) => PREFECTURES[k] === prefCode) ?? prefCode)); + return { + title: `iri-search 新着物件 (${scope})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/estate/:area?', + name: '新着物件', + url: 'www.iri-search.net', + maintainers: ['pseudoyu'], + handler, + example: '/iri-search/estate/tokyo', + parameters: { + area: `Region slug (${REGIONS.map((r) => r.slug).join(', ')}) or a 首都圏 prefecture (tokyo, kanagawa, saitama, chiba or its JIS code); omit for nationwide`, + }, + description: `New listings on 居抜き物件検索 iri-search sorted by 新着順 (first page, 30 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,保証金・敷金,居抜き譲渡代,以前の業態,業種可否,掲載日,…) parsed from the list and detail pages; unknown values are \`null\`. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 30 | 30 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.iri-search.net/estate_search', 'www.iri-search.net/'], + target: '/estate', + }, + ], +}; diff --git a/lib/routes/iri-search/namespace.ts b/lib/routes/iri-search/namespace.ts new file mode 100644 index 000000000000..d6272aa0e44e --- /dev/null +++ b/lib/routes/iri-search/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'iri-search', + url: 'www.iri-search.net', + description: '居抜き物件検索 iri-search — 全国の貸店舗・居抜き物件情報サイト', + lang: 'ja', + + ja: { + name: '居抜き物件検索 iri-search', + }, +}; From 8a99283aaa830e8d5ebb38f27fb42333c7d0d471 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:42:09 +0900 Subject: [PATCH 0783/1190] feat(route): add abc-tenpo new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /abc-tenpo/property/:pref? for ABC店舗 (www.abc-tenpo.com) listings sorted by 新着順, optionally by prefecture. Structured listing fields parsed from the list and detail pages are exposed in item._extra; members-only fields are not surfaced. --- lib/routes/abc-tenpo/namespace.ts | 12 ++ lib/routes/abc-tenpo/property.ts | 234 ++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 lib/routes/abc-tenpo/namespace.ts create mode 100644 lib/routes/abc-tenpo/property.ts diff --git a/lib/routes/abc-tenpo/namespace.ts b/lib/routes/abc-tenpo/namespace.ts new file mode 100644 index 000000000000..b9baf080d9b4 --- /dev/null +++ b/lib/routes/abc-tenpo/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'ABC Tenpo', + url: 'www.abc-tenpo.com', + description: 'ABC 店舗 — 飲食店向け居抜き・貸店舗物件情報サイト', + lang: 'ja', + + ja: { + name: 'ABC店舗', + }, +}; diff --git a/lib/routes/abc-tenpo/property.ts b/lib/routes/abc-tenpo/property.ts new file mode 100644 index 000000000000..8f659c15e94e --- /dev/null +++ b/lib/routes/abc-tenpo/property.ts @@ -0,0 +1,234 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const HOST = 'https://www.abc-tenpo.com'; +const DETAIL_CONCURRENCY = 2; +const PAGE_SIZE = 20; + +/** `pref[]` values of the search form (Kanto edition). */ +const PREFECTURES = [ + { slug: 'tokyo', code: '13', label: '東京都' }, + { slug: 'kanagawa', code: '14', label: '神奈川県' }, + { slug: 'saitama', code: '11', label: '埼玉県' }, + { slug: 'chiba', code: '12', label: '千葉県' }, +]; + +interface ListCard { + title: string; + link: string; + image?: string; + extra: ListingExtra; +} + +interface DetailFields { + updated_at: string | null; // 情報更新日 '2026/09/13' + business_limit: string | null; // 業種制限 '中華料理、大衆居酒屋NG' | '無し' + food_condition: string | null; // 飲食条件 '重飲食可' | '相談' + structure: string | null; // 構造 + note: string | null; // 備考 +} + +/** + * List page (`/property/search?sort=1`, 20 per page, server-rendered): card article.c-property-box, + * headline .c-property-box-footer__lead (fallback .c-property-box-header__title = station), link a.c-property-box__anchor, + * 所在地 .c-property-box-header__area '神奈川県鎌倉市 / 駅から徒歩4分', dl rows dt.c-property-box-table__title → dd: + * 賃料 '23.2万円', 初期費用 '268.0万円', 面積 '6.71坪 / 22.18㎡', 階層 '1階' | '地下1階' | '1階~2階一括', 最寄り駅 'JR横須賀線 鎌倉駅 徒歩4分', + * 引渡状態 'スケルトン' | '居抜き', 現業態 'ダイニングバー'; tags .c-tag-property__item (狭小, 居抜き, 重飲食可 …), .c-property-box-main__tag--new + * 保証金 / 礼金 / 造作譲渡料 are members-only on the site. + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('article.c-property-box') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const href = $el.find('a.c-property-box__anchor').attr('href'); + const id = href?.match(/\/view\/(\d+)/)?.[1]; + const title = clean($el.find('.c-property-box-footer__lead').text()) ?? clean($el.find('.c-property-box-header__title').text()); + if (!href || !id || !title) { + return null; + } + const cell = (label: string): string | null => { + const dt = $el + .find('dt.c-property-box-table__title') + .toArray() + .find((d) => clean($(d).text()) === label); + return dt ? clean($(dt).next('dd').text()) : null; + }; + const raw: ListingExtra['raw'] = { + station_title: clean($el.find('.c-property-box-header__title').text()), + area_header: clean($el.find('.c-property-box-header__area').text()), + rent: cell('賃料'), + initial_cost: cell('初期費用'), + area: cell('面積'), + floor: cell('階層'), + station: cell('最寄り駅'), + condition: cell('引渡状態'), + prev_business: cell('現業態'), + }; + const tags = [ + ...$el + .find('.c-tag-property__item') + .toArray() + .map((t) => clean($(t).text())), + $el.find('.c-property-box-main__tag--new').length > 0 ? 'NEW' : null, + ].filter((t): t is string => t !== null); + const [line, station] = (raw.station ?? '').split(' ', 2); + // '東京都 中野区 / 駅から徒歩2分' → '東京都中野区' + const address = clean(raw.area_header?.split('/', 1)[0])?.replaceAll(' ', '') ?? null; + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + const image = $el.find('.c-property-box-main__img img[src^="/img/bukken/"]').first().attr('src'); + + return { + title, + link: new URL(href, HOST).href, + image: image ? new URL(image, HOST).href : undefined, + extra: { + source: 'abc-tenpo', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor?.replaceAll('地下', 'B') ?? null), + station: clean(station)?.replace(/駅$/, '') ?? null, + line: clean(line), + walk_min: parseWalkMin(raw.station), + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: parseCondition(raw.condition), + prev_business: raw.prev_business === 'その他' ? null : raw.prev_business, + heavy_food_ok: tags.includes('重飲食可') ? true : null, + business_limit: null, + listed_at: null, + ward: parseWard(address), + address_hint: address, + tags, + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page (`/property/view/{id}`): 物件情報 dl.c-table-a__def dt → dd; 情報更新日 in .p-property-updated-date__def. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const dt = $('dl.c-table-a__def dt') + .toArray() + .find((d) => clean($(d).text()) === label); + return dt ? clean($(dt).next('dd').text()) : null; + }; + return { + updated_at: clean($('.p-property-updated-date__def dd').first().text()), + business_limit: cell('業種制限'), + food_condition: cell('飲食条件'), + structure: cell('構造'), + note: cell('備考'), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { + const limitParts = [d.business_limit === null || /^(?:無し|なし|-)$/.test(d.business_limit) ? null : d.business_limit, d.food_condition === null ? null : `飲食条件: ${d.food_condition}`].filter((p): p is string => p !== null); + return { + ...base, + heavy_food_ok: base.heavy_food_ok ?? parseHeavyFood(d.food_condition), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, + // The site only publishes 情報更新日, which is used as the item date. + listed_at: parseYmd(d.updated_at), + raw: { ...base.raw, updated_at: d.updated_at, business_limit: d.business_limit, food_condition: d.food_condition, structure: d.structure, note: d.note }, + }; +}; + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`abc-tenpo: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string | undefined = ctx.req.param('pref'); + const prefecture = pref === undefined ? undefined : PREFECTURES.find((p) => p.slug === pref || p.code === pref); + if (pref !== undefined && !prefecture) { + throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : PAGE_SIZE, PAGE_SIZE); + // sort=1 is 新着順. + const listUrl = `${HOST}/property/search?sort=1${prefecture ? `&pref%5B%5D=${prefecture.code}` : ''}`; + + const cards = parseList(await ofetch(listUrl)).slice(0, limit); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: summarize(extra), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `ABC店舗 新着物件${prefecture ? ` (${prefecture.label})` : ''}`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/property/:pref?', + name: '新着物件', + url: 'www.abc-tenpo.com', + maintainers: ['pseudoyu'], + handler, + example: '/abc-tenpo/property/tokyo', + parameters: { + pref: { + description: 'Prefecture slug or JIS X 0401 code; omit for all of 東京・神奈川・千葉・埼玉', + options: PREFECTURES.map((p) => ({ value: p.slug, label: `${p.label} (${p.code})` })), + }, + }, + description: `Listings on ABC 店舗 sorted by 新着順 (first page, 20 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,階,最寄駅,引渡状態,現業態,業種制限,飲食条件,情報更新日,…) parsed from the list and detail pages; unknown values are \`null\`. 保証金,礼金 and 造作譲渡料 are members-only on the site and therefore always \`null\`; the item date is the site's 情報更新日. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 20 | 20 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.abc-tenpo.com/property/search', 'www.abc-tenpo.com/feature/new_arrival', 'www.abc-tenpo.com/'], + target: '/property', + }, + ], +}; From 5544fbfbccb2b882b03dd7ed2e5dda0c474f3a7f Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 02:43:58 +0900 Subject: [PATCH 0784/1190] feat(route): add sonomama new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /sonomama/property/:pref? for 店舗そのままオークション (www.sonomama.net) new listings nationwide or by prefecture. Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/sonomama/namespace.ts | 12 ++ lib/routes/sonomama/property.ts | 236 +++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 lib/routes/sonomama/namespace.ts create mode 100644 lib/routes/sonomama/property.ts diff --git a/lib/routes/sonomama/namespace.ts b/lib/routes/sonomama/namespace.ts new file mode 100644 index 000000000000..31bccbb49fe6 --- /dev/null +++ b/lib/routes/sonomama/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Sonomama', + url: 'www.sonomama.net', + description: '店舗そのままオークション — 居抜き店舗物件情報サイト', + lang: 'ja', + + ja: { + name: '店舗そのままオークション', + }, +}; diff --git a/lib/routes/sonomama/property.ts b/lib/routes/sonomama/property.ts new file mode 100644 index 000000000000..d27c0348b8ae --- /dev/null +++ b/lib/routes/sonomama/property.ts @@ -0,0 +1,236 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.sonomama.net'; +const DETAIL_CONCURRENCY = 2; +const PAGE_SIZE = 20; + +/** `pref[]` values (JIS X 0401 codes without zero padding); any code 1–47 is passed through. */ +const PREFECTURES: Record = { + tokyo: '13', + kanagawa: '14', + saitama: '11', + chiba: '12', + osaka: '27', + kyoto: '26', + hyogo: '28', + aichi: '23', + fukuoka: '40', +}; + +/** Values that mean "not published" in the site's cells. */ +const isBlank = (text: string | null): boolean => text === null || /^[-−-]$/.test(text); + +interface ListCard { + title: string; + link: string; + image?: string; + extra: ListingExtra; +} + +interface DetailFields { + address: string | null; // 住所 + access: string | null; // 交通手段 '(沿線) 都営大江戸線 (最寄駅) 門前仲町駅 (距離) 徒歩 7分' + area: string | null; // 建坪数(店舗坪数) '14.97坪 (49.5平米)' + key_money: string | null; // 礼金 + business_limit: string | null; // 業種制限 + description: string | null; // 出展タイトル / 物件説明 +} + +/** + * List page (`/app/?action=public_property_list_search`, 20 per page, 登録順 desc, server-rendered): + * row #list_body .list_row, id + title .result4 .title a ('[ 75059 ]', title), 賃料 .item_hire '49.5万円', 面積 .item_width '19.8坪', + * 階 .item_floor '1階', 所在地 + 駅 .item_station '東京都 江東区 ( 門前仲町駅 )', 敷金/保証金 .item_bond '賃料の10ヶ月分' | '45万円/ 0万円', + * 造作 .item_price '0万円' | '応相談', 業態 .item_category '飲食店 レストラン' | 'スケルトン 重飲食可', NEW .result4 .title span.deco_accent + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('#list_body .list_row') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const a = $el.find('.result4 .title a').last(); + const id = a.attr('href')?.match(/pid=(\d+)/)?.[1]; + const title = clean(a.text()); + if (!id || isBlank(title)) { + return null; + } + // '飲食店
    レストラン' | 'スケルトン
    重飲食可' → [大分類, 中分類] + const categoryParts = $el + .find('.item_category p') + .contents() + .toArray() + .filter((n) => n.type === 'text') + .map((n) => clean($(n).text())) + .filter((t): t is string => t !== null); + const raw: ListingExtra['raw'] = { + rent: clean($el.find('.item_hire').text()), + area: clean($el.find('.item_width').text()), + floor: clean($el.find('.item_floor').text()), + station: clean($el.find('.item_station').text()), + bond: clean($el.find('.item_bond').text()), + fixtures: clean($el.find('.item_price').text()), + category: categoryParts.join(' / ') || null, + }; + const [address, stationPart] = (raw.station ?? '').split('(', 2); + const station = clean(stationPart?.replaceAll(/[()]/g, '')); + const [category, subCategory] = categoryParts; + const isSkeleton = category === 'スケルトン'; + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + const image = $el.find('img.list_image').attr('src'); + + return { + title: title!, + link: `${HOST}/app/?action=public_property_detail&pid=${id}`, + image: image ? new URL(image, HOST).href : undefined, + extra: { + source: 'sonomama', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor), + station: isBlank(station) ? null : station!.replace(/駅$/, ''), + line: null, + walk_min: null, + deposit_months: parseMonths(raw.bond), + deposit_jpy: /[万円]/.test(raw.bond ?? '') ? parseJpy(raw.bond) : null, + key_money_months: null, + fixtures_transfer_jpy: parseJpy(raw.fixtures), + condition: isSkeleton ? 'skeleton' : 'inuki', + prev_business: isSkeleton ? null : (subCategory ?? null), + heavy_food_ok: isSkeleton ? parseHeavyFood(subCategory ?? null) : null, + business_limit: null, + listed_at: null, + ward: parseWard(clean(address)?.replaceAll(' ', '') ?? null), + address_hint: clean(address)?.replaceAll(' ', '') ?? null, + tags: $el.find('.result4 .title span.deco_accent').length > 0 ? ['NEW'] : [], + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page: `table.table_body th` → sibling `td`; `−` means not published. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const th = $('table.table_body th') + .toArray() + .find((t) => clean($(t).text()) === label); + const value = th ? clean($(th).next('td').text()) : null; + return isBlank(value) ? null : value; + }; + return { + address: cell('住所'), + access: cell('交通手段'), + area: cell('建坪数(店舗坪数)'), + key_money: cell('礼金'), + business_limit: cell('業種制限'), + description: clean($('div.detail_body div.shop_text p').first().text()), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { + const { area_m2 } = parseArea(d.area); + return { + ...base, + area_m2: area_m2 ?? base.area_m2, + line: clean(d.access?.split('(最寄駅)', 1)[0]?.replace('(沿線)', '')), + walk_min: parseWalkMin(d.access), + business_limit: d.business_limit, + address_hint: d.address ?? base.address_hint, + ward: parseWard(d.address) ?? base.ward, + raw: { ...base.raw, address: d.address, access: d.access, area_detail: d.area, key_money: d.key_money, business_limit: d.business_limit, description: d.description }, + }; +}; + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`sonomama: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string | undefined = ctx.req.param('pref'); + const prefCode = pref === undefined ? undefined : (PREFECTURES[pref] ?? pref); + if (prefCode !== undefined && !/^(?:[1-9]|[1-3]\d|4[0-7])$/.test(prefCode)) { + throw new Error(`Unknown prefecture "${pref}", expected a slug (${Object.keys(PREFECTURES).join(', ')}) or a JIS X 0401 code`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : PAGE_SIZE, PAGE_SIZE); + // The prefecture filter is only honoured together with the submit-button parameters. + const listUrl = + prefCode === undefined + ? `${HOST}/app/?action=public_property_list_search&view=1&page_index=0&page_num=${PAGE_SIZE}&sort_id=0&sort_type=1` + : `${HOST}/app/?action=public_property_list_search&Btn_start.x=1&Btn_start.y=1&pref%5B%5D=${prefCode}`; + + const cards = parseList(await ofetch(listUrl)).slice(0, limit); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + // The site does not publish listing dates; no pubDate is fabricated. + description: [extra.raw.description, summarize(extra)].filter(Boolean).join(' / '), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `店舗そのままオークション 新着物件${pref ? ` (${pref})` : ''}`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/property/:pref?', + name: '新着物件', + url: 'www.sonomama.net', + maintainers: ['pseudoyu'], + handler, + example: '/sonomama/property/tokyo', + parameters: { + pref: `Prefecture slug (${Object.keys(PREFECTURES).join(', ')}) or JIS X 0401 code; omit for nationwide`, + }, + description: `New listings on 店舗そのままオークション,newest first (first page, 20 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,階,最寄駅,敷金・保証金,造作価格,業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 20 | 20 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.sonomama.net/app/', 'www.sonomama.net/'], + target: '/property', + }, + ], +}; From 66b8424d95a6283eafb9410f2d523149561c733d Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 10:02:11 +0900 Subject: [PATCH 0785/1190] feat(route): add tenant-shop new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /tenant-shop/chintai/:pref?/:type? for テナントショップネットワーク (tenant-shop.com / tenant-shop.jp) new listings by prefecture with an optional property-type filter. Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/tenant-shop/chintai.ts | 270 ++++++++++++++++++++++++++++ lib/routes/tenant-shop/namespace.ts | 12 ++ 2 files changed, 282 insertions(+) create mode 100644 lib/routes/tenant-shop/chintai.ts create mode 100644 lib/routes/tenant-shop/namespace.ts diff --git a/lib/routes/tenant-shop/chintai.ts b/lib/routes/tenant-shop/chintai.ts new file mode 100644 index 000000000000..5bb13412c910 --- /dev/null +++ b/lib/routes/tenant-shop/chintai.ts @@ -0,0 +1,270 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseJpy, parseMonths, parseWalkMin, parseWard, parseYmd, sumKnown, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const DETAIL_CONCURRENCY = 2; +const PAGE_SIZE = 30; + +/** `pa` codes of the site; East Japan is served by tenant-shop.com, 愛知 and westward by tenant-shop.jp (data is not mirrored). */ +const PREFECTURES = [ + { slug: 'tokyo', code: '14', label: '東京都', host: 'https://www.tenant-shop.com' }, + { slug: 'kanagawa', code: '15', label: '神奈川県', host: 'https://www.tenant-shop.com' }, + { slug: 'saitama', code: '16', label: '埼玉県', host: 'https://www.tenant-shop.com' }, + { slug: 'chiba', code: '17', label: '千葉県', host: 'https://www.tenant-shop.com' }, + { slug: 'ibaraki', code: '18', label: '茨城県', host: 'https://www.tenant-shop.com' }, + { slug: 'tochigi', code: '19', label: '栃木県', host: 'https://www.tenant-shop.com' }, + { slug: 'gunma', code: '20', label: '群馬県', host: 'https://www.tenant-shop.com' }, + { slug: 'hokkaido', code: '7', label: '北海道', host: 'https://www.tenant-shop.com' }, + { slug: 'miyagi', code: '10', label: '宮城県', host: 'https://www.tenant-shop.com' }, + { slug: 'niigata', code: '21', label: '新潟県', host: 'https://www.tenant-shop.com' }, + { slug: 'nagano', code: '28', label: '長野県', host: 'https://www.tenant-shop.com' }, + { slug: 'aichi', code: '25', label: '愛知県', host: 'https://www.tenant-shop.jp' }, + { slug: 'gifu', code: '26', label: '岐阜県', host: 'https://www.tenant-shop.jp' }, + { slug: 'shizuoka', code: '27', label: '静岡県', host: 'https://www.tenant-shop.jp' }, + { slug: 'osaka', code: '1', label: '大阪府', host: 'https://www.tenant-shop.jp' }, + { slug: 'kyoto', code: '31', label: '京都府', host: 'https://www.tenant-shop.jp' }, + { slug: 'hyogo', code: '32', label: '兵庫県', host: 'https://www.tenant-shop.jp' }, + { slug: 'shiga', code: '5', label: '滋賀県', host: 'https://www.tenant-shop.jp' }, + { slug: 'nara', code: '34', label: '奈良県', host: 'https://www.tenant-shop.jp' }, +]; + +/** `f{n}-1` path filters of the site. */ +const TYPES = [ + { slug: 'inuki', code: 'f1', label: '居抜き' }, + { slug: 'food', code: 'f2', label: '飲食' }, + { slug: 'office', code: 'f3', label: 'オフィス' }, + { slug: 'retail', code: 'f4', label: '物販・サービス' }, + { slug: 'warehouse', code: 'f5', label: '倉庫・工場' }, + { slug: 'beauty', code: 'f6', label: '美容・エステ・医療' }, + { slug: 'roadside', code: 'f7', label: '沿道サービス・借地' }, + { slug: 'mall', code: 'f8', label: '商業施設' }, +]; + +interface ListCard { + title: string; + link: string; + image?: string; + extra: ListingExtra; +} + +interface DetailFields { + status: string | null; // 現況 '空き' + registered_at: string | null; // 物件登録日 '2026年9月13日' + updated_at: string | null; // 情報更新日 + facilities: string[]; // 居抜き / 飲食 / 物販・サービス / … + remarks: string | null; // 備考 +} + +/** + * List page (`/chintai_biz/pa-14/shin-1/`, 30 per page, newest first, server-rendered): one `table.result` per card. + * title/link div.estatename a ('/detail/e-169996/'), 種別 div.esttype, 賃料 div.price '37.40万(税込)', 坪単価 .smallText '(坪単価:5.80万/坪)', + * 面積 div.area_wrap '建物21.33㎡(6.45坪)', icon rows div.add img[src$="/{icon}.png"] with no text label: + * kanri 管理費, rei 礼金 '2ヶ月', hoken 保証金 '10ヶ月', shiki 敷金 '5ヶ月', kaiyaku 償却, addr 所在地 '文京区湯島3', + * station 最寄駅 '湯島駅(徒歩1分)', chikunen 築年月, floor 階 '1階/9階建'; PR div.cmnt1; NEW img.new_label + */ +const parseList = (html: string, host: string): ListCard[] => { + const $ = load(html); + return $('table.result') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const a = $el.find('div.estatename a').first(); + const href = a.attr('href'); + const title = clean(a.text()); + const id = href?.match(/\/detail\/e-(\d+)/)?.[1]; + if (!href || !title || !id) { + return null; + } + const iconRow = (icon: string): string | null => clean($el.find(`div.add img[src$="/${icon}.png"]`).first().parent().text()); + const raw: ListingExtra['raw'] = { + type: clean($el.find('div.esttype').text()), + rent: clean($el.find('div.price').text()), + tsubo_unit: clean($el.find('div.price').next('.smallText').text()), + area: clean($el.find('div.area_wrap').text()), + management_fee: iconRow('kanri'), + key_money: iconRow('rei'), + guarantee: iconRow('hoken'), + security: iconRow('shiki'), + amortization: iconRow('kaiyaku'), + address: iconRow('addr'), + station: iconRow('station'), + built: iconRow('chikunen'), + floor: iconRow('floor'), + comment: clean($el.find('div.cmnt1').text()), + }; + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + // '湯島駅(徒歩1分)' | '南海本線 難波駅(徒歩2分)' → optional line + station + const stationParts = clean(raw.station?.replace(/[((].*$/, ''))?.split(' ') ?? []; + const station = stationParts.length > 0 ? stationParts.at(-1)!.replace(/駅$/, '') : null; + const line = stationParts.length > 1 ? stationParts.slice(0, -1).join(' ') : null; + // 保証金 / 敷金 are given either in months ('10ヶ月') or in yen ('145.26万円') + const deposits = [raw.guarantee, raw.security]; + const depositMonths = deposits.every((d) => d === null) ? null : sumKnown(parseMonths(raw.guarantee), parseMonths(raw.security)); + const depositYen = deposits.filter((d): d is string => d !== null && /[万円]/.test(d)); + const image = $el.find('div.photo img').first().attr('src'); + + return { + title, + link: new URL(href, host).href, + image: image ? new URL(image, host).href : undefined, + extra: { + source: 'tenant-shop', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(raw.tsubo_unit) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(raw.floor?.split('/', 1)[0])), + station, + line, + walk_min: parseWalkMin(raw.station), + deposit_months: depositMonths, + deposit_jpy: depositYen.length === 0 ? null : sumKnown(parseJpy(depositYen[0]), parseJpy(depositYen[1] ?? null)), + key_money_months: parseMonths(raw.key_money), + fixtures_transfer_jpy: null, + condition: null, + prev_business: null, + heavy_food_ok: null, + business_limit: null, + listed_at: null, + ward: parseWard(raw.address), + address_hint: raw.address, + tags: $el.find('img.new_label').length > 0 ? ['NEW'] : [], + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page (`/detail/e-{id}/`): div.info-block4 table th → td (現況, 物件登録日, 情報更新日); category tags div.facility_list li a. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const th = $('div.info-block4 th') + .toArray() + .find((t) => clean($(t).text()) === label); + return th ? clean($(th).next('td').text()) : null; + }; + return { + status: cell('現況'), + registered_at: cell('物件登録日'), + updated_at: cell('情報更新日'), + facilities: $('div.facility_list li a') + .toArray() + .map((t) => clean($(t).text())) + .filter((t): t is string => t !== null), + remarks: clean($('div.pr_cmnt3').text()), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ + ...base, + condition: d.facilities.includes('居抜き') ? 'inuki' : base.condition, + listed_at: parseYmd(d.registered_at), + tags: [...base.tags, ...d.facilities], + raw: { ...base.raw, status: d.status, registered_at: d.registered_at, updated_at: d.updated_at, remarks: d.remarks }, +}); + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`tenant-shop: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string = ctx.req.param('pref') ?? 'tokyo'; + const prefecture = PREFECTURES.find((p) => p.slug === pref || p.code === pref); + if (!prefecture) { + throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); + } + const typeSlug: string | undefined = ctx.req.param('type'); + const type = typeSlug === undefined ? undefined : TYPES.find((t) => t.slug === typeSlug); + if (typeSlug !== undefined && !type) { + throw new Error(`Unknown type "${typeSlug}", expected one of ${TYPES.map((t) => t.slug).join(', ')}`); + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : PAGE_SIZE, PAGE_SIZE); + // shin-1 selects 新着物件; the page is ordered by 登録日 desc. + const listUrl = `${prefecture.host}/chintai_biz/pa-${prefecture.code}/${type ? `${type.code}-1/` : ''}shin-1/`; + + const cards = parseList(await ofetch(listUrl), prefecture.host).slice(0, limit); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: [extra.raw.comment, summarize(extra)].filter(Boolean).join(' / '), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `テナントショップ 新着物件 (${prefecture.label}${type ? ` ${type.label}` : ''})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/chintai/:pref?/:type?', + name: '新着物件', + url: 'www.tenant-shop.com', + maintainers: ['pseudoyu'], + handler, + example: '/tenant-shop/chintai/tokyo/food', + parameters: { + pref: { + description: "Prefecture slug or the site's `pa` code", + default: 'tokyo', + options: PREFECTURES.map((p) => ({ value: p.slug, label: `${p.label} (${p.code})` })), + }, + type: { + description: 'Property type filter; omit for all types', + options: TYPES.map((t) => ({ value: t.slug, label: t.label })), + }, + }, + description: `New listings (新着物件) on テナントショップネットワーク for one prefecture, newest first (first page, 30 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,保証金・敷金,礼金,物件登録日,…) parsed from the list and detail pages; unknown values are \`null\`. East Japan prefectures are served by tenant-shop.com and 愛知 and westward by tenant-shop.jp. + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 30 | 30 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.tenant-shop.com/chintai_biz/pa-:pa/shin-1', 'www.tenant-shop.com/chintai_biz/pa-:pa'], + target: '/chintai/:pa', + }, + { + source: ['www.tenant-shop.jp/chintai_biz/pa-:pa/shin-1', 'www.tenant-shop.jp/chintai_biz/pa-:pa'], + target: '/chintai/:pa', + }, + ], +}; diff --git a/lib/routes/tenant-shop/namespace.ts b/lib/routes/tenant-shop/namespace.ts new file mode 100644 index 000000000000..51dde14f9492 --- /dev/null +++ b/lib/routes/tenant-shop/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Tenant Shop Network', + url: 'www.tenant-shop.com', + description: 'テナントショップネットワーク — 貸店舗・貸事務所・居抜き物件情報サイト(東日本は tenant-shop.com、愛知以西は tenant-shop.jp)', + lang: 'ja', + + ja: { + name: 'テナントショップネットワーク', + }, +}; From 9f2267e918b6a769df7b03549f0c266584ed6886 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 10:38:38 +0900 Subject: [PATCH 0786/1190] feat(route): add temponw new listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /temponw/estates/:area? for 店舗ネットワーク (www.temponw.com) listings sorted by 新着順, nationwide or for the 23 wards of Tokyo. Structured listing fields parsed from the list and detail pages are exposed in item._extra. --- lib/routes/temponw/estates.ts | 257 ++++++++++++++++++++++++++++++++ lib/routes/temponw/namespace.ts | 12 ++ 2 files changed, 269 insertions(+) create mode 100644 lib/routes/temponw/estates.ts create mode 100644 lib/routes/temponw/namespace.ts diff --git a/lib/routes/temponw/estates.ts b/lib/routes/temponw/estates.ts new file mode 100644 index 000000000000..9e29b9898814 --- /dev/null +++ b/lib/routes/temponw/estates.ts @@ -0,0 +1,257 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.temponw.com'; +const DETAIL_CONCURRENCY = 2; +const PAGES = 2; // 10 cards per page + +/** Tokyo 23 wards as the site's `cities[]` codes (101 千代田区 … 123 江戸川区). */ +const TOKYO_WARDS = Array.from({ length: 23 }, (_, i) => String(101 + i)); + +const isBlank = (text: string | null): boolean => text === null || /^[-−-]$/.test(text); + +interface ListCard { + title: string; + link: string; + image?: string; + extra: ListingExtra; +} + +interface DetailFields { + access: string | null; // 最寄駅 'JR総武線 新小岩 徒歩3分' + fixtures: string | null; // 造作譲渡料 '造作無償' | '-' + status: string | null; // 現況 '空' + contract: string | null; // 契約形態 '普通借家' + term: string | null; // 契約期間 '3年' + industry_blocks: string[][]; // [適用業種, 特長, 立地] + notes: string[]; // 備考 '不可業態: 重飲食不可/24時間営業不可' +} + +/** + * List page (10 cards, each rendered twice — only the PC copy `.result-contents.sm-hidden` is read): + * title .result-content-title span, id/link hidden inputs source_system_code + item_no, + * rows .result-detail-row > div(label) + div(value): 賃料 '月額 22.5万円 税別', 面積 '57.91 m2(17.52 坪)', + * 使用階/部屋番号 '2/202' | 'B1/B001', 最寄駅 'JR総武線 新小岩', 所在地 '東京都葛飾区新小岩1丁目41番地12号', + * 保証金/礼金 (two

    : '3.00ヶ月', '2.00ヶ月' | '110万円'), 管理費等; PR .point-content; image .result-content-img img + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('.result-contents.sm-hidden') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const source = $el.find('input[name="source_system_code"]').first().attr('value'); + const itemNo = $el.find('input[name="item_no"]').first().attr('value'); + const title = clean($el.find('.result-content-title span').text()); + if (!source || !itemNo || !title) { + return null; + } + const row = (label: string) => + $el + .find('.result-detail-row') + .toArray() + .map((r) => $(r).children()) + .find((children) => clean(children.first().text()) === label) + ?.last(); + const value = (label: string): string | null => { + const v = clean(row(label)?.text()); + return isBlank(v) ? null : v; + }; + const deposits = + row('保証金/礼金') + ?.find('p') + .toArray() + .map((p) => clean($(p).text())) ?? []; + + const raw: ListingExtra['raw'] = { + rent: value('賃料'), + area: value('面積'), + floor_room: value('使用階/部屋番号'), + station: value('最寄駅'), + address: value('所在地'), + guarantee: isBlank(deposits[0] ?? null) ? null : deposits[0], + key_money: isBlank(deposits[1] ?? null) ? null : deposits[1], + management_fee: value('管理費等'), + comment: clean($el.find('.point-content').text()), + }; + const [line, station] = (raw.station?.normalize('NFKC') ?? '').split(' ', 2); + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + const image = $el.find('.result-content-img img').first().attr('src'); + + return { + title, + link: `${HOST}/store_detail?source_system_code=${source}&item_no=${itemNo}`, + image: image ? new URL(image, HOST).href : undefined, + extra: { + source: 'temponw', + listing_id: `${source}-${itemNo}`, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(raw.floor_room?.split('/', 1)[0])), + station: clean(station?.replace(/、他.*$/, ''))?.replace(/駅$/, '') ?? null, + line: clean(line), + walk_min: null, + deposit_months: parseMonths(raw.guarantee), + deposit_jpy: /[万円]/.test(raw.guarantee ?? '') ? parseJpy(raw.guarantee) : null, + key_money_months: parseMonths(raw.key_money), + fixtures_transfer_jpy: null, + condition: null, + prev_business: null, + heavy_food_ok: null, + business_limit: null, + listed_at: null, + ward: parseWard(raw.address?.normalize('NFKC') ?? null), + address_hint: raw.address, + tags: [], + raw, + }, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page: `.content-detail .container-div.inline-container` → span:first p (label), span:last p (value). */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const field = (label: string): string | null => { + const box = $('.content-detail .container-div.inline-container') + .toArray() + .find((b) => clean($(b).find('span').first().find('p').text()) === label); + const v = box ? clean($(box).find('span').last().find('p').text()) : null; + return isBlank(v) ? null : v; + }; + return { + access: clean($('.detail-access .detail-access-info').first().text()), + fixtures: field('造作譲渡料'), + status: field('現況'), + contract: field('契約形態'), + term: field('契約期間'), + industry_blocks: $('.applicable-industry-items') + .toArray() + .map((block) => + $(block) + .find('.grey-div p') + .toArray() + .map((p) => clean($(p).text())) + .filter((t): t is string => t !== null) + ), + notes: $('.detail-note li') + .toArray() + .map((li) => clean($(li).text())) + .filter((t): t is string => t !== null), + }; +}; + +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { + const [applicable = [], features = [], location = []] = d.industry_blocks; + const restriction = clean(d.notes.find((n) => n.includes('不可業態'))?.replace(/^不可業態[::]\s*/, '')); + const limitParts = [applicable.length > 0 ? `可: ${applicable.join('、')}` : null, restriction === null ? null : `不可: ${restriction}`].filter((p): p is string => p !== null); + return { + ...base, + walk_min: parseWalkMin(d.access), + fixtures_transfer_jpy: parseJpy(d.fixtures), + condition: features.includes('スケルトン') ? 'skeleton' : base.condition, + heavy_food_ok: parseHeavyFood(restriction) ?? (applicable.length > 0 ? applicable.includes('重飲食') : null), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, + tags: [...features, ...location], + raw: { ...base.raw, access: d.access, fixtures: d.fixtures, status: d.status, contract: d.contract, term: d.term, notes: d.notes.join(' / ') || null }, + }; +}; + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + } catch (error) { + logger.warn(`temponw: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +const listUrlFor = (area: string | undefined, page: number): string => { + if (area === undefined) { + return `${HOST}/result?sort=new&page=${page}`; + } + // The site requires `cities[]`; all 23 wards are passed at once for Tokyo. + const query = new URLSearchParams({ prefectures_code: '13', name_j: '東京都' }); + for (const code of TOKYO_WARDS) { + query.append('cities[]', code); + } + query.set('sort', 'new'); + query.set('page', String(page)); + return `${HOST}/area_search/result?${query.toString()}`; +}; + +export const handler = async (ctx): Promise => { + const area: string | undefined = ctx.req.param('area'); + if (area !== undefined && area !== 'tokyo') { + throw new Error(`Unknown area "${area}", expected "tokyo" or none`); + } + const pages = await Promise.all(Array.from({ length: PAGES }, (_, i) => ofetch(listUrlFor(area, i + 1)))); + const seen = new Set(); + const cards = pages.flatMap((html) => parseList(html)).filter((card) => (seen.has(card.link) ? false : seen.add(card.link))); + + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + // The site does not publish listing dates; no pubDate is fabricated. + description: [extra.raw.comment, summarize(extra)].filter(Boolean).join(' / '), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `店舗ネットワーク 新着物件${area === 'tokyo' ? ' (東京23区)' : ''}`, + link: listUrlFor(area, 1), + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/estates/:area?', + name: '新着物件', + url: 'www.temponw.com', + maintainers: ['pseudoyu'], + handler, + example: '/temponw/estates/tokyo', + parameters: { + area: { + description: '`tokyo` for the 23 wards of Tokyo; omit for nationwide', + options: [{ value: 'tokyo', label: '東京23区' }], + }, + }, + description: `Listings on 店舗ネットワーク sorted by 新着順 (first two pages, 20 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,階,最寄駅,保証金,礼金,造作譲渡料,不可業態,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`; the same unit may be listed by several agencies under different ids.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.temponw.com/result', 'www.temponw.com/'], + target: '/estates', + }, + ], +}; diff --git a/lib/routes/temponw/namespace.ts b/lib/routes/temponw/namespace.ts new file mode 100644 index 000000000000..f973e3afc36c --- /dev/null +++ b/lib/routes/temponw/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Temponw', + url: 'www.temponw.com', + description: '店舗ネットワーク — 貸店舗・テナント物件情報サイト', + lang: 'ja', + + ja: { + name: '店舗ネットワーク', + }, +}; From d89814dadbdb2d817a8a134b2d00eb920e04091f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:20:23 +0000 Subject: [PATCH 0787/1190] chore(deps): bump @honeybadger-io/js from 6.16.1 to 6.16.3 (#23284) Bumps [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js) from 6.16.1 to 6.16.3. - [Release notes](https://github.com/honeybadger-io/honeybadger-js/releases) - [Commits](https://github.com/honeybadger-io/honeybadger-js/compare/@honeybadger-io/js@6.16.1...@honeybadger-io/js@6.16.3) --- updated-dependencies: - dependency-name: "@honeybadger-io/js" dependency-version: 6.16.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 86ee66c8bb1a..a61d8895aac8 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/plugin-helper": "4.4.1", "@bbob/preset-html5": "4.4.1", "@googleapis/youtube": "35.0.0", - "@honeybadger-io/js": "6.16.1", + "@honeybadger-io/js": "6.16.3", "@hono/node-server": "2.1.1", "@hono/zod-openapi": "1.6.3", "@jocmp/mercury-parser": "3.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42b98c3cea11..a4cab4ed961a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 35.0.0 version: 35.0.0 '@honeybadger-io/js': - specifier: 6.16.1 - version: 6.16.1 + specifier: 6.16.3 + version: 6.16.3 '@hono/node-server': specifier: 2.1.1 version: 2.1.1(hono@4.13.7) @@ -1231,8 +1231,8 @@ packages: resolution: {integrity: sha512-ojVk6q2GUXqW80szrk/zp2XC1YQFJWQvGHdNNyc1RPGTekguSMpa7Pp9jKitHpdYIsbmEnj1wPTyZassOfSLeg==} engines: {node: '>=14'} - '@honeybadger-io/js@6.16.1': - resolution: {integrity: sha512-QN16xawSoTytzNj2A/+eZ0N/iOo1sCETfm70A+mFSlwFqQF0Y50NMKK9DQ4WpeKUleESl5Szmv3FJCR9bW76sQ==} + '@honeybadger-io/js@6.16.3': + resolution: {integrity: sha512-AsQaFji3Qu9z7r7RAhkCzkaveWvqGglA+ytaVOUnAsIYihojrnV1D3t0T34/gRHsjme23VWop4X3buSXPq8jzw==} engines: {node: '>=14'} hasBin: true peerDependencies: @@ -2735,8 +2735,8 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/aws-lambda@8.10.162': - resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} + '@types/aws-lambda@8.10.163': + resolution: {integrity: sha512-+4zuoEB3S8RIhimtOFT7zAEk2SbpwrKjjGl9CyYnQR6k08uynxfauIIB0pDU1ssr05F8oyGiETwpn+8eXZMqPw==} '@types/bluebird@3.5.42': resolution: {integrity: sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==} @@ -3727,7 +3727,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -5025,6 +5025,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -6993,10 +6998,10 @@ snapshots: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.16.1': + '@honeybadger-io/js@6.16.3': dependencies: '@honeybadger-io/core': 6.11.1 - '@types/aws-lambda': 8.10.162 + '@types/aws-lambda': 8.10.163 '@types/express': 5.0.6 '@hono/node-server@2.1.1(hono@4.13.7)': @@ -8083,7 +8088,7 @@ snapshots: tslib: 2.8.1 optional: true - '@types/aws-lambda@8.10.162': {} + '@types/aws-lambda@8.10.163': {} '@types/bluebird@3.5.42': {} @@ -10612,6 +10617,8 @@ snapshots: nanoid@3.3.18: {} + nanoid@3.3.19: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10988,7 +10995,7 @@ snapshots: postcss@8.5.28: dependencies: - nanoid: 3.3.18 + nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.1 From a9c89d80bdae8f0992208ecafb5b50c359680932 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:26:12 +0000 Subject: [PATCH 0788/1190] chore(deps-dev): bump @types/node from 26.5.0 to 26.5.1 (#23287) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.5.0 to 26.5.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.5.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 134 ++++++++++++++++++++++++------------------------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index a61d8895aac8..b1a1d677ade8 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,7 @@ "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", - "@types/node": "26.5.0", + "@types/node": "26.5.1", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.70.0", "@typescript-eslint/parser": "8.70.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4cab4ed961a..006bcdabdf67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -348,8 +348,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.5.0 - version: 26.5.0 + specifier: 26.5.1 + version: 26.5.1 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -376,7 +376,7 @@ importers: version: 10.10.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.5.0)(eslint@10.10.0(jiti@2.7.0)) + version: 9.1.1(@types/node@26.5.1)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) @@ -415,7 +415,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) @@ -466,10 +466,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: specifier: 4.129.1 version: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -2822,8 +2822,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.5.0': - resolution: {integrity: sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==} + '@types/node@26.5.1': + resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -6621,7 +6621,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -7147,76 +7147,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.5.0)': + '@inquirer/checkbox@4.3.2(@types/node@26.5.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.5.0) + '@inquirer/core': 10.3.2(@types/node@26.5.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.0) + '@inquirer/type': 3.0.10(@types/node@26.5.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/confirm@5.1.21(@types/node@26.5.0)': + '@inquirer/confirm@5.1.21(@types/node@26.5.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.5.0) - '@inquirer/type': 3.0.10(@types/node@26.5.0) + '@inquirer/core': 10.3.2(@types/node@26.5.1) + '@inquirer/type': 3.0.10(@types/node@26.5.1) optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/confirm@6.1.1(@types/node@26.5.0)': + '@inquirer/confirm@6.1.1(@types/node@26.5.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.5.0) - '@inquirer/type': 4.0.7(@types/node@26.5.0) + '@inquirer/core': 11.2.1(@types/node@26.5.1) + '@inquirer/type': 4.0.7(@types/node@26.5.1) optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/core@10.3.2(@types/node@26.5.0)': + '@inquirer/core@10.3.2(@types/node@26.5.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.0) + '@inquirer/type': 3.0.10(@types/node@26.5.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/core@11.2.1(@types/node@26.5.0)': + '@inquirer/core@11.2.1(@types/node@26.5.1)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.5.0) + '@inquirer/type': 4.0.7(@types/node@26.5.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.5.0)': + '@inquirer/select@4.4.2(@types/node@26.5.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.5.0) + '@inquirer/core': 10.3.2(@types/node@26.5.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.0) + '@inquirer/type': 3.0.10(@types/node@26.5.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/type@3.0.10(@types/node@26.5.0)': + '@inquirer/type@3.0.10(@types/node@26.5.1)': optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 - '@inquirer/type@4.0.7(@types/node@26.5.0)': + '@inquirer/type@4.0.7(@types/node@26.5.1)': optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@ioredis/commands@2.0.0': {} @@ -8095,7 +8095,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/caseless@0.12.5': {} @@ -8108,7 +8108,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/crypto-js@4.2.2': {} @@ -8129,11 +8129,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/express-serve-static-core@5.1.3': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8147,7 +8147,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/html-to-text@9.0.4': {} @@ -8159,7 +8159,7 @@ snapshots: '@types/jsdom@30.0.0': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/tough-cookie': 4.0.5 parse5: 8.0.1 undici-types: 8.10.2 @@ -8170,7 +8170,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/jsrsasign@10.5.15': {} @@ -8182,7 +8182,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.5.0': + '@types/node@26.5.1': dependencies: undici-types: 8.9.0 @@ -8198,7 +8198,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8208,16 +8208,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@types/statuses@2.0.6': {} @@ -8417,7 +8417,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/expect@4.1.11': dependencies: @@ -8428,14 +8428,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + msw: 2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) '@vitest/pretty-format@4.1.11': dependencies: @@ -9203,12 +9203,12 @@ snapshots: eslint: 10.10.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.5.0)(eslint@10.10.0(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.5.1)(eslint@10.10.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.5.0) - '@inquirer/confirm': 5.1.21(@types/node@26.5.0) - '@inquirer/select': 4.4.2(@types/node@26.5.0) + '@inquirer/checkbox': 4.3.2(@types/node@26.5.1) + '@inquirer/confirm': 5.1.21(@types/node@26.5.1) + '@inquirer/select': 4.4.2(@types/node@26.5.1) eslint: 10.10.0(jiti@2.7.0) eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) optionator: 0.9.4 @@ -9976,7 +9976,7 @@ snapshots: jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 - '@types/node': 26.5.0 + '@types/node': 26.5.1 jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: @@ -10583,9 +10583,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.5.0) + '@inquirer/confirm': 6.1.1(@types/node@26.5.1) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -11041,7 +11041,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.5.0 + '@types/node': 26.5.1 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -11876,16 +11876,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): + vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11894,17 +11894,17 @@ snapshots: rollup: 4.63.1 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.5.0 + '@types/node': 26.5.1 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 yaml: 2.9.0 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.0)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.5.0)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11921,11 +11921,11 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.5.0)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.5.0 + '@types/node': 26.5.1 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: From d66cacc1f157c242073582e72804470fdcfe4e48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:27:04 +0000 Subject: [PATCH 0789/1190] chore(deps): bump vm2 from 3.12.1 to 3.12.2 (#23288) Bumps [vm2](https://github.com/patriksimek/vm2) from 3.12.1 to 3.12.2. - [Release notes](https://github.com/patriksimek/vm2/releases) - [Changelog](https://github.com/patriksimek/vm2/blob/main/CHANGELOG.md) - [Commits](https://github.com/patriksimek/vm2/compare/v3.12.1...v3.12.2) --- updated-dependencies: - dependency-name: vm2 dependency-version: 3.12.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b1a1d677ade8..d0a4a8e2095c 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "ufo": "1.6.4", "undici": "8.10.2", "uuid": "14.0.2", - "vm2": "3.12.1", + "vm2": "3.12.2", "winston": "3.19.0", "xxhash-wasm": "1.1.0", "youtube-caption-extractor": "1.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 006bcdabdf67..300ec2d32654 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -269,8 +269,8 @@ importers: specifier: 14.0.2 version: 14.0.2 vm2: - specifier: 3.12.1 - version: 3.12.1 + specifier: 3.12.2 + version: 3.12.2 winston: specifier: 3.19.0 version: 3.19.0 @@ -6253,8 +6253,8 @@ packages: jsdom: optional: true - vm2@3.12.1: - resolution: {integrity: sha512-VMqg/4swWz7lkse95rxPlgtnb7WkHL2wfDtrgcNIaIjvpskjRmEmS+MKoItl4npwtW+u2Kalle4zarf3aEWjGQ==} + vm2@3.12.2: + resolution: {integrity: sha512-LRHt4xU6eXshlZEcSZVrTehLGxRDjHvjmFPGwEp4xGpZX61QzH6Xm9wK2nU7XAp36V1w2xILAL1LwxVkgD1Ipw==} engines: {node: '>=6.0'} hasBin: true @@ -11931,7 +11931,7 @@ snapshots: transitivePeerDependencies: - msw - vm2@3.12.1: + vm2@3.12.2: dependencies: acorn: 8.18.0 acorn-walk: 8.3.5 From aa441b670c72777334356c64b19977930b7b0dc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:28:13 +0000 Subject: [PATCH 0790/1190] chore(deps-dev): bump rolldown from 1.2.7 to 1.2.8 (#23291) Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.2.7 to 1.2.8. - [Release notes](https://github.com/rolldown/rolldown/releases) - [Changelog](https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md) - [Commits](https://github.com/rolldown/rolldown/commits/v1.2.8/packages/rolldown) --- updated-dependencies: - dependency-name: rolldown dependency-version: 1.2.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 147 ++++++++++++++++++++++++------------------------- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index d0a4a8e2095c..a30a17dcafa3 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "rolldown": "1.2.7", + "rolldown": "1.2.8", "tsdown": "0.23.0", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 300ec2d32654..e82a96f4871f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -447,8 +447,8 @@ importers: specifier: 11.0.0 version: 11.0.0 rolldown: - specifier: 1.2.7 - version: 1.2.7 + specifier: 1.2.8 + version: 1.2.8 tsdown: specifier: 0.23.0 version: 0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1) @@ -1928,9 +1928,6 @@ packages: cpu: [x64] os: [win32] - '@oxc-project/types@0.148.0': - resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} - '@oxc-project/types@0.149.0': resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} @@ -2372,98 +2369,98 @@ packages: '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rolldown/binding-android-arm-eabi@1.2.7': - resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} + '@rolldown/binding-android-arm-eabi@1.2.8': + resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rolldown/binding-android-arm64@1.2.7': - resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==} + '@rolldown/binding-android-arm64@1.2.8': + resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.2.7': - resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==} + '@rolldown/binding-darwin-arm64@1.2.8': + resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.7': - resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==} + '@rolldown/binding-darwin-x64@1.2.8': + resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.7': - resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==} + '@rolldown/binding-freebsd-x64@1.2.8': + resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.7': - resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.7': - resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==} + '@rolldown/binding-linux-arm64-gnu@1.2.8': + resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.2.7': - resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==} + '@rolldown/binding-linux-arm64-musl@1.2.8': + resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.2.7': - resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==} + '@rolldown/binding-linux-ppc64-gnu@1.2.8': + resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.7': - resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==} + '@rolldown/binding-linux-s390x-gnu@1.2.8': + resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.7': - resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==} + '@rolldown/binding-linux-x64-gnu@1.2.8': + resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.2.7': - resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==} + '@rolldown/binding-linux-x64-musl@1.2.8': + resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.2.7': - resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==} + '@rolldown/binding-openharmony-arm64@1.2.8': + resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-win32-arm64-msvc@1.2.7': - resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==} + '@rolldown/binding-win32-arm64-msvc@1.2.8': + resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.7': - resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==} + '@rolldown/binding-win32-x64-msvc@1.2.8': + resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -5567,8 +5564,8 @@ packages: vue-tsc: optional: true - rolldown@1.2.7: - resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==} + rolldown@1.2.8: + resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7573,8 +7570,6 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.149.0': optional: true - '@oxc-project/types@0.148.0': {} - '@oxc-project/types@0.149.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': @@ -7830,49 +7825,49 @@ snapshots: dependencies: quansync: 1.0.0 - '@rolldown/binding-android-arm-eabi@1.2.7': + '@rolldown/binding-android-arm-eabi@1.2.8': optional: true - '@rolldown/binding-android-arm64@1.2.7': + '@rolldown/binding-android-arm64@1.2.8': optional: true - '@rolldown/binding-darwin-arm64@1.2.7': + '@rolldown/binding-darwin-arm64@1.2.8': optional: true - '@rolldown/binding-darwin-x64@1.2.7': + '@rolldown/binding-darwin-x64@1.2.8': optional: true - '@rolldown/binding-freebsd-x64@1.2.7': + '@rolldown/binding-freebsd-x64@1.2.8': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.7': + '@rolldown/binding-linux-arm64-gnu@1.2.8': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.7': + '@rolldown/binding-linux-arm64-musl@1.2.8': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.7': + '@rolldown/binding-linux-ppc64-gnu@1.2.8': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.7': + '@rolldown/binding-linux-s390x-gnu@1.2.8': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.7': + '@rolldown/binding-linux-x64-gnu@1.2.8': optional: true - '@rolldown/binding-linux-x64-musl@1.2.7': + '@rolldown/binding-linux-x64-musl@1.2.8': optional: true - '@rolldown/binding-openharmony-arm64@1.2.7': + '@rolldown/binding-openharmony-arm64@1.2.8': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.7': + '@rolldown/binding-win32-arm64-msvc@1.2.8': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.7': + '@rolldown/binding-win32-x64-msvc@1.2.8': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -11234,12 +11229,12 @@ snapshots: dependencies: glob: 10.5.0 - rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.7): + rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.8): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.6 obug: 2.1.4 - rolldown: 1.2.7 + rolldown: 1.2.8 yuku-ast: 0.9.3 yuku-codegen: 0.9.3 yuku-parser: 0.9.3 @@ -11248,26 +11243,26 @@ snapshots: transitivePeerDependencies: - oxc-resolver - rolldown@1.2.7: + rolldown@1.2.8: dependencies: - '@oxc-project/types': 0.148.0 + '@oxc-project/types': 0.149.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.7 - '@rolldown/binding-android-arm64': 1.2.7 - '@rolldown/binding-darwin-arm64': 1.2.7 - '@rolldown/binding-darwin-x64': 1.2.7 - '@rolldown/binding-freebsd-x64': 1.2.7 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.7 - '@rolldown/binding-linux-arm64-gnu': 1.2.7 - '@rolldown/binding-linux-arm64-musl': 1.2.7 - '@rolldown/binding-linux-ppc64-gnu': 1.2.7 - '@rolldown/binding-linux-s390x-gnu': 1.2.7 - '@rolldown/binding-linux-x64-gnu': 1.2.7 - '@rolldown/binding-linux-x64-musl': 1.2.7 - '@rolldown/binding-openharmony-arm64': 1.2.7 - '@rolldown/binding-win32-arm64-msvc': 1.2.7 - '@rolldown/binding-win32-x64-msvc': 1.2.7 + '@rolldown/binding-android-arm-eabi': 1.2.8 + '@rolldown/binding-android-arm64': 1.2.8 + '@rolldown/binding-darwin-arm64': 1.2.8 + '@rolldown/binding-darwin-x64': 1.2.8 + '@rolldown/binding-freebsd-x64': 1.2.8 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 + '@rolldown/binding-linux-arm64-gnu': 1.2.8 + '@rolldown/binding-linux-arm64-musl': 1.2.8 + '@rolldown/binding-linux-ppc64-gnu': 1.2.8 + '@rolldown/binding-linux-s390x-gnu': 1.2.8 + '@rolldown/binding-linux-x64-gnu': 1.2.8 + '@rolldown/binding-linux-x64-musl': 1.2.8 + '@rolldown/binding-openharmony-arm64': 1.2.8 + '@rolldown/binding-win32-arm64-msvc': 1.2.8 + '@rolldown/binding-win32-x64-msvc': 1.2.8 rollup@4.63.1: dependencies: @@ -11646,8 +11641,8 @@ snapshots: import-without-cache: 0.4.0 obug: 2.1.4 picomatch: 4.0.7 - rolldown: 1.2.7 - rolldown-plugin-dts: 0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.7) + rolldown: 1.2.8 + rolldown-plugin-dts: 0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.8) tinyexec: 1.3.1 tinyglobby: 0.2.17 tree-kill: 1.2.2 @@ -11809,7 +11804,7 @@ snapshots: unrun@0.3.1: dependencies: - rolldown: 1.2.7 + rolldown: 1.2.8 until-async@3.0.2: {} From a2140104b4fd3ff1b71b99a1d13ea9b05e2c524a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:28:52 +0000 Subject: [PATCH 0791/1190] chore(deps-dev): bump lint-staged from 17.5.0 to 17.5.1 (#23290) Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 17.5.0 to 17.5.1. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v17.5.0...v17.5.1) --- updated-dependencies: - dependency-name: lint-staged dependency-version: 17.5.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a30a17dcafa3..705d368000d6 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,7 @@ "globals": "17.12.0", "husky": "9.1.7", "js-beautify": "2.0.3", - "lint-staged": "17.5.0", + "lint-staged": "17.5.1", "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e82a96f4871f..40ba6835e756 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,8 +408,8 @@ importers: specifier: 2.0.3 version: 2.0.3 lint-staged: - specifier: 17.5.0 - version: 17.5.0 + specifier: 17.5.1 + version: 17.5.1 mockdate: specifier: 3.0.5 version: 3.0.5 @@ -4727,8 +4727,8 @@ packages: linkify-it@6.1.0: resolution: {integrity: sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==} - lint-staged@17.5.0: - resolution: {integrity: sha512-ah2qsNtvKP1+Ak4rAvEEIvcXDLjjbr/xmxCvlequxqEOFYk0qINcZcRxD3Ic8wRn7/oQ8+wC9s0DfDrdMVCRFg==} + lint-staged@17.5.1: + resolution: {integrity: sha512-7EDuco1xnBMeVpvAbeMq1U5KXwJGLmY8q6l+Ye78r36C4mPc+Vg1Z2SK8gHyRTyvPro90A0q5/FAZ+Au23d6QQ==} engines: {node: '>=22.22.1'} hasBin: true @@ -10134,7 +10134,7 @@ snapshots: dependencies: uc.micro: 3.0.0 - lint-staged@17.5.0: + lint-staged@17.5.1: dependencies: picomatch: 4.0.7 string-argv: 0.3.2 From e87fad2d948ddb219d7de215f6d4b22d63896609 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 08:32:52 +0000 Subject: [PATCH 0792/1190] chore(deps): bump markdown-it from 15.0.1 to 15.0.2 (#23293) Bumps [markdown-it](https://github.com/markdown-it/markdown-it) from 15.0.1 to 15.0.2. - [Changelog](https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md) - [Commits](https://github.com/markdown-it/markdown-it/compare/15.0.1...15.0.2) --- updated-dependencies: - dependency-name: markdown-it dependency-version: 15.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 705d368000d6..03bc73294fc0 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "jsrsasign": "11.1.5", "lru-cache": "11.5.2", "lz-string": "1.5.0", - "markdown-it": "15.0.1", + "markdown-it": "15.0.2", "mixi2": "0.2.2", "narou": "2.0.1", "notion-to-md": "3.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40ba6835e756..024cd8504dfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -176,8 +176,8 @@ importers: specifier: 1.5.0 version: 1.5.0 markdown-it: - specifier: 15.0.1 - version: 15.0.1 + specifier: 15.0.2 + version: 15.0.2 mixi2: specifier: 0.2.2 version: 0.2.2 @@ -3290,8 +3290,8 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@3.0.0: - resolution: {integrity: sha512-BOp5NMrHqKxmq/OLr+clzzrRxgOKSLkcjmkWuChp7Irqwn4s74WjOBPIgWfA/HMcBnVkZ5XEuf9uUqzlpfCQ6A==} + argparse@3.0.2: + resolution: {integrity: sha512-mFdDM6WqWKraGLsVb+C9CahPnzTXOefAOLq3jYcca2YZ8bEWpr++Tzj+zSaKW9+X9L5uSxcm1AZ3Y6aZJ09OhQ==} asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -4796,8 +4796,8 @@ packages: resolution: {integrity: sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - markdown-it@15.0.1: - resolution: {integrity: sha512-9/7gE95FNPkfUWrjJIoHZza2iLmuJlPD0UNMxPi7bxUrbCR525YZY0r+zyfes0dZI5ZZ/uNIXUJca0pJvtw41g==} + markdown-it@15.0.2: + resolution: {integrity: sha512-q4IGxMv56jCqT4OCRCADBoDP3LO4MhmTXjFbphHPXs4g3j9Xg5RDnxqN8IF/3vIWEU+VCnUq+7JUg/cfy2E6Qw==} hasBin: true markdown-table@2.0.0: @@ -8579,7 +8579,7 @@ snapshots: arg@5.0.2: {} - argparse@3.0.0: {} + argparse@3.0.2: {} asn1@0.2.6: dependencies: @@ -10201,9 +10201,9 @@ snapshots: map-obj@5.0.0: {} - markdown-it@15.0.1: + markdown-it@15.0.2: dependencies: - argparse: 3.0.0 + argparse: 3.0.2 entities: 8.1.0 linkify-it: 6.1.0 mdurl: 2.1.0 From 9aebb94db59d2b1bcb8235c46bdf8f5ccbc4ebb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:10:42 +0800 Subject: [PATCH 0793/1190] chore(deps): bump the nix group with 2 updates (#23295) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `190959a` to `fce7cf3` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/190959a9a4bb52d4802f076a90c3c4e3aa2e6fa2...fce7cf37d531acca06c68d0b0ee5c6579cedf200) Updates `nixpkgs` from `dc5d91f` to `8ce4ef6` - [Commits](https://github.com/NixOS/nixpkgs/compare/dc5d91f840324650bac8c379428c7037a416959a...8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe) --- updated-dependencies: - dependency-name: devenv dependency-version: fce7cf37d531acca06c68d0b0ee5c6579cedf200 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: 8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 968723e169aa..9416c82d295c 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1788819331, - "narHash": "sha256-PXClmncMTmWutJvFGrA6rKyvxWtUwaMUT615XrDHdB8=", + "lastModified": 1789034488, + "narHash": "sha256-1q5wOw1YfOFGBQ6mh8bDqRJlYgHAKSW7eIsNhU97a7o=", "owner": "cachix", "repo": "devenv", - "rev": "190959a9a4bb52d4802f076a90c3c4e3aa2e6fa2", + "rev": "fce7cf37d531acca06c68d0b0ee5c6579cedf200", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1788752844, - "narHash": "sha256-VaWGJ6+cIYN2erfSecbRV+4ljI185Ty2wUrXyvQbgOw=", + "lastModified": 1789006805, + "narHash": "sha256-xB8mKMOx1IA9vTDNLmJZ6n4wCMq/cuWBBOzGCRnqxrU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dc5d91f840324650bac8c379428c7037a416959a", + "rev": "8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe", "type": "github" }, "original": { From 6cffb045f08652b9d876236259528e7b9d5ef9e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:12:14 +0800 Subject: [PATCH 0794/1190] chore(deps): bump DeterminateSystems/magic-nix-cache-action (#23283) Bumps [DeterminateSystems/magic-nix-cache-action](https://github.com/determinatesystems/magic-nix-cache-action) from 14 to 15. - [Release notes](https://github.com/determinatesystems/magic-nix-cache-action/releases) - [Commits](https://github.com/determinatesystems/magic-nix-cache-action/compare/908b263ff629f4cc17666315b7fd3ec127c6244d...84c0677f58dcedf3b91f8223ce36a9ea5b3c84b7) --- updated-dependencies: - dependency-name: DeterminateSystems/magic-nix-cache-action dependency-version: '15' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-nix-hash.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-nix-hash.yml b/.github/workflows/update-nix-hash.yml index 3250650919a0..6fc91e28417b 100644 --- a/.github/workflows/update-nix-hash.yml +++ b/.github/workflows/update-nix-hash.yml @@ -24,7 +24,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Cache Nix store - uses: DeterminateSystems/magic-nix-cache-action@908b263ff629f4cc17666315b7fd3ec127c6244d # v14 + uses: DeterminateSystems/magic-nix-cache-action@84c0677f58dcedf3b91f8223ce36a9ea5b3c84b7 # v15 - name: Update Nix flake hash id: update-hash From e90415905a8a10f76b7867a9e3c594803b5a383a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:20:15 +0800 Subject: [PATCH 0795/1190] chore(deps): bump @googleapis/youtube from 35.0.0 to 38.0.0 (#23285) Bumps [@googleapis/youtube](https://github.com/googleapis/google-api-nodejs-client) from 35.0.0 to 38.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/v38.0.0/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v35.0.0...v38.0.0) --- updated-dependencies: - dependency-name: "@googleapis/youtube" dependency-version: 38.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 230 ++++++++----------------------------------------- 2 files changed, 35 insertions(+), 197 deletions(-) diff --git a/package.json b/package.json index 03bc73294fc0..2db40bfa3c9e 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@bbob/html": "4.4.1", "@bbob/plugin-helper": "4.4.1", "@bbob/preset-html5": "4.4.1", - "@googleapis/youtube": "35.0.0", + "@googleapis/youtube": "38.0.0", "@honeybadger-io/js": "6.16.3", "@hono/node-server": "2.1.1", "@hono/zod-openapi": "1.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 024cd8504dfd..ce772cb7debc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 4.4.1 version: 4.4.1 '@googleapis/youtube': - specifier: 35.0.0 - version: 35.0.0 + specifier: 38.0.0 + version: 38.0.0 '@honeybadger-io/js': specifier: 6.16.3 version: 6.16.3 @@ -1223,9 +1223,9 @@ packages: '@noble/hashes': optional: true - '@googleapis/youtube@35.0.0': - resolution: {integrity: sha512-mUD2mMgqFmmSZbTgKisVFpOwWj1aOt3UbMCVorwYTVyZkqdY1QURs2SMaP5g6IPHvuUYWBaBIn7CMhFWIEqQ2Q==} - engines: {node: '>=12.0.0'} + '@googleapis/youtube@38.0.0': + resolution: {integrity: sha512-1E8QQ9cZKt2bs0ly6lketaNPO1pmgqLLCBUXkKzwPiRB7Gm478wUzlAdFc4gbQaT4ZirlO0xUIUon+lVMv8Svg==} + engines: {node: '>=22.0.0'} '@honeybadger-io/core@6.11.1': resolution: {integrity: sha512-ojVk6q2GUXqW80szrk/zp2XC1YQFJWQvGHdNNyc1RPGTekguSMpa7Pp9jKitHpdYIsbmEnj1wPTyZassOfSLeg==} @@ -1536,10 +1536,6 @@ packages: '@ioredis/commands@2.0.0': resolution: {integrity: sha512-vrx0AE/T0h7cRZwfo1M39Cr+ZhZrkf0V8mQN75wucKCxCLD9l/VX6no3gFvrLqD1IlG/1LtzWovqEw3t0Vr9zg==} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - '@isaacs/fs-minipass@4.0.1': resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} @@ -2315,10 +2311,6 @@ packages: '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@poppinss/colors@4.1.6': resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} @@ -3275,18 +3267,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.3.0: - resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -3333,9 +3317,6 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -3386,9 +3367,6 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@2.1.4: - resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} - brace-expansion@5.0.9: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} @@ -3776,9 +3754,6 @@ packages: oxc-resolver: optional: true - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} @@ -3796,9 +3771,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - empathic@2.0.1: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} @@ -4111,10 +4083,6 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -4152,13 +4120,13 @@ packages: resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} engines: {node: '>=18'} - gaxios@7.1.3: - resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} + gaxios@7.3.1: + resolution: {integrity: sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==} engines: {node: '>=18'} - gcp-metadata@8.1.4: - resolution: {integrity: sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==} - engines: {node: '>=18'} + gcp-metadata@9.0.3: + resolution: {integrity: sha512-2YYnIlHaKBGT2IPg3G2M57hia9Galz15zsEOvw9T3oRf0lSn6KN6VcHQLqby7x8ksYKnjXvp3rp2KJyLCN6zfQ==} + engines: {node: '>=22'} generative-bayesian-network@2.1.88: resolution: {integrity: sha512-kxbW6CCsiEAVdBYPont/6ZVOa47Pyfv5ldYFIvj8wmOx9uQOZ4c8wdR0jEf2pE6DeVuIAfmolm+91bYne6/3uA==} @@ -4193,11 +4161,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -4213,20 +4176,20 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - google-auth-library@10.5.0: - resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} - engines: {node: '>=18'} + google-auth-library@11.0.2: + resolution: {integrity: sha512-vzpgPutxrghPsnjrjpzLX2bdv8IOL719Rh0oEjGnQu8YCIbnbMuTTQ5zU9LcKvLdOPgCxBwppbvnhgW90Qna5Q==} + engines: {node: '>=22'} - google-logging-utils@1.1.3: - resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} - engines: {node: '>=14'} + google-logging-utils@2.0.1: + resolution: {integrity: sha512-HMhaQghlOTvbcb3c4T5jmmOMtG3JUF1iOQMezaJXL86CDS+Tm2vHd0IeLFRAx3+ewd+bo9E1HFHoy17X5aJa9A==} + engines: {node: '>=22'} google-play-scraper@10.1.3: resolution: {integrity: sha512-T5zkrPqTvDoKx8jHGx8ZRca2viJQS0KpfFNMMMAR5Q/4/8Wl8GBG9HxuvfqtnKAZ8TzufnLN1N+8ShvHpSt6+g==} - googleapis-common@8.0.3: - resolution: {integrity: sha512-7g1yzQKx0mmNTjiK0H9dJ8eqKqDBveES9vLHeg5neb3BMQy/d1oQefIMhIpOVT8a+f+LOcixMEdRbFIW/cQUJw==} - engines: {node: '>=18'} + googleapis-common@9.0.4: + resolution: {integrity: sha512-fcI5bm1gN+9MNb+gvANRPFsxaJ07h/ZYvKCh6H3RoDJBUTwp5ySaCpCqnmX8gnGJUD/9bKmkacivRJT1g+xXyg==} + engines: {node: '>=22'} got@14.6.6: resolution: {integrity: sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==} @@ -4239,10 +4202,6 @@ packages: resolution: {integrity: sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - gtoken@8.0.0: - resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} - engines: {node: '>=18'} - har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -4537,9 +4496,6 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -4757,9 +4713,6 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -4972,10 +4925,6 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} @@ -5239,9 +5188,6 @@ packages: peerDependencies: quickjs-wasi: ^2.2.0 - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pangu@4.0.7: resolution: {integrity: sha512-weZKJIwwy5gjt4STGVUH9bix3BGk7wZ2ahtIypwe3e/mllsrIZIvtfLx1dPX56GcpZFOCFKmeqI1qVuB9enRzA==} hasBin: true @@ -5286,10 +5232,6 @@ packages: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -5541,10 +5483,6 @@ packages: rfc4648@1.5.4: resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} - rimraf@5.0.10: - resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} - hasBin: true - rolldown-plugin-dts@0.28.5: resolution: {integrity: sha512-yYd3C9CeJwqjOc9X23m0Tyxcqic491uLZlfg51szT287S8zCCqLR2uoySoElgqy2CLn7PdXcEo1dlkBs4n1WHg==} engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} @@ -5736,10 +5674,6 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -5747,10 +5681,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -6342,10 +6272,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - write-file-atomic@1.3.4: resolution: {integrity: sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==} @@ -6984,9 +6910,9 @@ snapshots: optionalDependencies: '@noble/hashes': 2.3.0 - '@googleapis/youtube@35.0.0': + '@googleapis/youtube@38.0.0': dependencies: - googleapis-common: 8.0.3 + googleapis-common: 9.0.4 transitivePeerDependencies: - supports-color @@ -7217,15 +7143,6 @@ snapshots: '@ioredis/commands@2.0.0': {} - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 @@ -7769,9 +7686,6 @@ snapshots: '@pinojs/redact@0.4.0': {} - '@pkgjs/parseargs@0.11.0': - optional: true - '@poppinss/colors@4.1.6': dependencies: kleur: 4.1.5 @@ -8569,14 +8483,10 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.3.0: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.3: {} - arg@5.0.2: {} argparse@3.0.2: {} @@ -8613,8 +8523,6 @@ snapshots: bail@2.0.2: {} - balanced-match@1.0.2: {} - balanced-match@4.0.4: {} base64-js@1.5.1: {} @@ -8652,10 +8560,6 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@2.1.4: - dependencies: - balanced-match: 1.0.2 - brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -9004,8 +8908,6 @@ snapshots: optionalDependencies: oxc-resolver: 11.24.2 - eastasianwidth@0.2.0: {} - ecc-jsbn@0.1.2: dependencies: jsbn: 0.1.1 @@ -9026,8 +8928,6 @@ snapshots: emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - empathic@2.0.1: {} enabled@2.0.0: {} @@ -9466,11 +9366,6 @@ snapshots: fn.name@1.1.0: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - forever-agent@0.6.1: {} form-data-encoder@4.1.0: {} @@ -9510,19 +9405,18 @@ snapshots: function-timeout@1.0.2: {} - gaxios@7.1.3: + gaxios@7.3.1: dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6 node-fetch: 3.3.2 - rimraf: 5.0.10 transitivePeerDependencies: - supports-color - gcp-metadata@8.1.4: + gcp-metadata@9.0.3: dependencies: - gaxios: 7.1.3 - google-logging-utils: 1.1.3 + gaxios: 7.3.1 + google-logging-utils: 2.0.1 json-bigint: 1.0.0 transitivePeerDependencies: - supports-color @@ -9565,15 +9459,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - glob@13.0.6: dependencies: minimatch: 10.2.6 @@ -9586,19 +9471,18 @@ snapshots: globrex@0.1.2: {} - google-auth-library@10.5.0: + google-auth-library@11.0.2: dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.3 - gcp-metadata: 8.1.4 - google-logging-utils: 1.1.3 - gtoken: 8.0.0 + gaxios: 7.3.1 + gcp-metadata: 9.0.3 + google-logging-utils: 2.0.1 jws: 4.0.1 transitivePeerDependencies: - supports-color - google-logging-utils@1.1.3: {} + google-logging-utils@2.0.1: {} google-play-scraper@10.1.3: dependencies: @@ -9611,12 +9495,12 @@ snapshots: transitivePeerDependencies: - supports-color - googleapis-common@8.0.3: + googleapis-common@9.0.4: dependencies: extend: 3.0.2 - gaxios: 7.1.3 - google-auth-library: 10.5.0 - google-logging-utils: 1.1.3 + gaxios: 7.3.1 + google-auth-library: 11.0.2 + google-logging-utils: 2.0.1 qs: 6.16.0 url-template: 2.0.8 transitivePeerDependencies: @@ -9641,13 +9525,6 @@ snapshots: graphql@16.14.2: {} - gtoken@8.0.0: - dependencies: - gaxios: 7.1.3 - jws: 4.0.1 - transitivePeerDependencies: - - supports-color - har-schema@2.0.0: {} har-validator@5.1.5: @@ -9943,12 +9820,6 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jiti@2.7.0: optional: true @@ -10165,8 +10036,6 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@10.4.3: {} - lru-cache@11.5.2: {} lru-queue@0.1.0: @@ -10558,10 +10427,6 @@ snapshots: dependencies: brace-expansion: 5.0.9 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.4 - minipass@7.1.3: {} minizlib@3.1.0: @@ -10892,8 +10757,6 @@ snapshots: netmask: 2.1.1 quickjs-wasi: 2.2.0 - package-json-from-dist@1.0.1: {} - pangu@4.0.7: {} parse-srcset@1.0.2: {} @@ -10934,11 +10797,6 @@ snapshots: path-key@4.0.0: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.3 - path-scurry@2.0.2: dependencies: lru-cache: 11.5.2 @@ -11225,10 +11083,6 @@ snapshots: rfc4648@1.5.4: {} - rimraf@5.0.10: - dependencies: - glob: 10.5.0 - rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.8): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) @@ -11483,12 +11337,6 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.2.0 - string_decoder@1.3.0: dependencies: safe-buffer: '@nolyfill/safe-buffer@1.0.44' @@ -11497,10 +11345,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.3.0 - strip-final-newline@3.0.0: {} strip-indent@4.1.1: {} @@ -12040,12 +11884,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.2.0 - write-file-atomic@1.3.4: dependencies: graceful-fs: 4.2.11 From 39b807dd0b2dae2218f6160cc38351f613cb3509 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:24:36 +0800 Subject: [PATCH 0796/1190] chore(deps): bump @sentry/node from 10.73.0 to 10.74.0 (#23289) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.73.0 to 10.74.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/10.74.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.73.0...10.74.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.74.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 69 +++++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 2db40bfa3c9e..52c3bfada7af 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.12.1", - "@sentry/node": "10.73.0", + "@sentry/node": "10.74.0", "acorn": "8.18.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce772cb7debc..b0166f87d18d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,8 +89,8 @@ importers: specifier: 0.12.1 version: 0.12.1(hono@4.13.7) '@sentry/node': - specifier: 10.73.0 - version: 10.73.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) + specifier: 10.74.0 + version: 10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) acorn: specifier: 8.18.0 version: 8.18.0 @@ -2657,12 +2657,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.73.0': - resolution: {integrity: sha512-FLO1UgH19RyasVpofu612WCOgb2nEH0dZy+R72d7p65XU9i0wxlMKm3+sgfwKmiSJp1Qhilaaxs4Jg6BbiM5HA==} + '@sentry/core@10.74.0': + resolution: {integrity: sha512-u9rY8vcZfktccwm6LznfCZlqP5C9A+p76r4/pFS1grqpuTO0m21Cl8rosnlESrDGP/Xd9tfr91rWYk0jPH8jeQ==} engines: {node: '>=18'} - '@sentry/node-core@10.73.0': - resolution: {integrity: sha512-GHAGUmZPmm6FKfxfv2maVVJ/A99YAmd7oFOuKMDmITI6O/Msl6JB3vPTEpopVAHLW0LYJQBOjddL9C7o+Jt44g==} + '@sentry/node-core@10.74.0': + resolution: {integrity: sha512-btgZXcGmkOGgojbxHo/gfGiyqYpzC9E8zVR78Q3MtM6Xnemk9gwJiQlhNmEX/FM+C36WBRPZrdcZcnMaJhbfJw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2682,20 +2682,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.73.0': - resolution: {integrity: sha512-jiMJ6GgXDw6UMGzJY+o0c8OoeA9OfHqZ/xEpHfDqy75hn+9CEkRkbNGCIdMvsc7wW/Se1Os394hHfTpU+YEskg==} + '@sentry/node@10.74.0': + resolution: {integrity: sha512-u1wsarTOWHn9CCev81Da5T4IQHZgdcosXRfX2+4DMII/lVJMBYesKixTQuMwXexjVG2+pkf16zTmgjZDa+75jA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.73.0': - resolution: {integrity: sha512-fQouPQKsH0CQrw6oAn1k0Z2I+tgyochCovifr5qNS69i0OzjknLa03WJyiZ/IuzXc4AVa5jAKfOeE9slABz8Qw==} + '@sentry/opentelemetry@10.74.0': + resolution: {integrity: sha512-ua5mt0NDBfye+/ACKjAw9Ad2i+y42lOLvhxXZepZXjszblMz80MEsIZflMB7uLZUCTNH7MbZN8tlzCy8KsJKmQ==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.73.0': - resolution: {integrity: sha512-QskripdKFbM/+gipC6mpa2crLwL7+VbkX84IpHg2z9UlYQ1kNKd3aMT+Qk9NLRSw/zu1rSIAvlbWfx4D3rgNAA==} + '@sentry/server-utils@10.74.0': + resolution: {integrity: sha512-AHmPIGE8yVRyywpZRhAkm/H0cHTgsQSPhFZFbaeQLcYHFE/eOP9bAMh8Nj/Apr7WMagFXYHd04tR/63BxtLgKw==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -3818,6 +3818,9 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-module-lexer@3.0.2: + resolution: {integrity: sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==} + es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} engines: {node: '>=0.10'} @@ -4345,8 +4348,8 @@ packages: imapflow@1.7.8: resolution: {integrity: sha512-dJoCIdZOJh26Rn2PdwEzwj0bRDgGBxxX38pio534FagIHVuR2l0SAfLr6sJo32YfuJHiSs/U2ntNDHnMg3/Hlg==} - import-in-the-middle@3.4.0: - resolution: {integrity: sha512-Xfjwfarhe+LGmoaof+sexeNo3sGRysb5x56WrZLwHtmqT0OilSKtVWkv0lrCcMgSKyKP9oBogBAisHZvtJH0aw==} + import-in-the-middle@3.5.0: + resolution: {integrity: sha512-O6wo8l0lhBuytKz3x8S65TvacFtoopji+gCVeEBbjCaRy75gkk4bCBQoVmY7OEZ63cbFK+yf77pDCTWaadV7hQ==} engines: {node: '>=18'} import-without-cache@0.4.0: @@ -7343,7 +7346,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 - import-in-the-middle: 3.4.0 + import-in-the-middle: 3.5.0 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -7923,16 +7926,16 @@ snapshots: '@sentry/conventions@0.16.0': {} - '@sentry/core@10.73.0': + '@sentry/core@10.74.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.73.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.73.0 - '@sentry/opentelemetry': 10.73.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - import-in-the-middle: 3.4.0 + '@sentry/core': 10.74.0 + '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + import-in-the-middle: 3.5.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) @@ -7940,34 +7943,34 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.73.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.73.0 - '@sentry/node-core': 10.73.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.73.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.73.0 - import-in-the-middle: 3.4.0 + '@sentry/core': 10.74.0 + '@sentry/node-core': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.74.0 + import-in-the-middle: 3.5.0 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.73.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.73.0 + '@sentry/core': 10.74.0 - '@sentry/server-utils@10.73.0': + '@sentry/server-utils@10.74.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.73.0 + '@sentry/core': 10.74.0 '@sindresorhus/is@4.6.0': {} @@ -8962,6 +8965,8 @@ snapshots: es-module-lexer@2.3.2: {} + es-module-lexer@3.0.2: {} + es5-ext@0.10.64: dependencies: es6-iterator: 2.0.3 @@ -9682,10 +9687,10 @@ snapshots: pino: 10.3.1 socks: 2.8.9 - import-in-the-middle@3.4.0: + import-in-the-middle@3.5.0: dependencies: cjs-module-lexer: 2.2.1 - es-module-lexer: 2.3.2 + es-module-lexer: 3.0.2 module-details-from-path: 1.0.4 import-without-cache@0.4.0: {} From dd41f30d2baca24f553db0dff5122eefb3920ec4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:51:46 +0800 Subject: [PATCH 0797/1190] chore(deps): bump zod from 4.5.4 to 4.6.2 (#23292) Bumps [zod](https://github.com/colinhacks/zod) from 4.5.4 to 4.6.2. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v4.5.4...v4.6.2) --- updated-dependencies: - dependency-name: zod dependency-version: 4.6.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 52c3bfada7af..e4e035aed11d 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "xxhash-wasm": "1.1.0", "youtube-caption-extractor": "1.10.2", "youtubei.js": "18.0.0", - "zod": "4.5.4" + "zod": "4.6.2" }, "devDependencies": { "@actions/core": "3.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0166f87d18d..91b58858c267 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: version: 2.1.1(hono@4.13.7) '@hono/zod-openapi': specifier: 1.6.3 - version: 1.6.3(hono@4.13.7)(zod@4.5.4) + version: 1.6.3(hono@4.13.7)(zod@4.6.2) '@jocmp/mercury-parser': specifier: 3.1.0 version: 3.1.0 @@ -284,8 +284,8 @@ importers: specifier: 18.0.0 version: 18.0.0 zod: - specifier: 4.5.4 - version: 4.5.4 + specifier: 4.6.2 + version: 4.6.2 devDependencies: '@actions/core': specifier: 3.0.1 @@ -6392,8 +6392,8 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zod@4.5.4: - resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} + zod@4.6.2: + resolution: {integrity: sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -6446,10 +6446,10 @@ snapshots: is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 - '@asteasolutions/zod-to-openapi@9.1.0(zod@4.5.4)': + '@asteasolutions/zod-to-openapi@9.1.0(zod@4.6.2)': dependencies: openapi3-ts: 4.6.1 - zod: 4.5.4 + zod: 4.6.2 '@babel/code-frame@7.29.7': dependencies: @@ -6934,18 +6934,18 @@ snapshots: dependencies: hono: 4.13.7 - '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.5.4)': + '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.6.2)': dependencies: - '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.5.4) - '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.5.4) + '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.6.2) + '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.6.2) hono: 4.13.7 openapi3-ts: 4.6.1 - zod: 4.5.4 + zod: 4.6.2 - '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.5.4)': + '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.6.2)': dependencies: hono: 4.13.7 - zod: 4.5.4 + zod: 4.6.2 '@humanfs/core@0.19.2': dependencies: @@ -7910,7 +7910,7 @@ snapshots: '@scalar/helpers': 0.11.3 nanoid: 5.1.16 type-fest: 5.9.0 - zod: 4.5.4 + zod: 4.6.2 '@scalar/validation@0.6.3': {} @@ -12026,6 +12026,6 @@ snapshots: zod@4.4.3: {} - zod@4.5.4: {} + zod@4.6.2: {} zwitch@2.0.4: {} From 045328f8912166b1b73f990e0bd51f95be190204 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 22:01:15 +0800 Subject: [PATCH 0798/1190] chore(deps): bump imapflow from 1.7.8 to 2.0.2 (#23294) Bumps [imapflow](https://github.com/postalsys/imapflow) from 1.7.8 to 2.0.2. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v1.7.8...v2.0.2) --- updated-dependencies: - dependency-name: imapflow dependency-version: 2.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index e4e035aed11d..2bf3e7017916 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "1.7.8", + "imapflow": "2.0.2", "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91b58858c267..df7eeaa73d2e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 1.7.8 - version: 1.7.8 + specifier: 2.0.2 + version: 2.0.2 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4345,8 +4345,9 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.7.8: - resolution: {integrity: sha512-dJoCIdZOJh26Rn2PdwEzwj0bRDgGBxxX38pio534FagIHVuR2l0SAfLr6sJo32YfuJHiSs/U2ntNDHnMg3/Hlg==} + imapflow@2.0.2: + resolution: {integrity: sha512-jrWWecWFifN3Feum2MqaTUupNeYSAdPEltKuwGdr8IPdxW72NZXf7ObVUfTWoUH2MPrli0q7SZm9pRt8ob/9XQ==} + engines: {node: '>=20.0.0'} import-in-the-middle@3.5.0: resolution: {integrity: sha512-O6wo8l0lhBuytKz3x8S65TvacFtoopji+gCVeEBbjCaRy75gkk4bCBQoVmY7OEZ63cbFK+yf77pDCTWaadV7hQ==} @@ -9676,7 +9677,7 @@ snapshots: image-size@0.7.5: {} - imapflow@1.7.8: + imapflow@2.0.2: dependencies: '@zone-eu/mailsplit': 5.4.16 encoding-japanese: 2.3.0 @@ -9685,7 +9686,7 @@ snapshots: libmime: 5.4.3 libqp: 2.1.1 pino: 10.3.1 - socks: 2.8.9 + socks: 2.8.10 import-in-the-middle@3.5.0: dependencies: From 0719454ddb955eb96d73a21175c26e97506db8d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 22:16:23 +0800 Subject: [PATCH 0799/1190] chore(deps): bump patchright from 1.62.3 to 1.63.0 (#23286) Bumps [patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) from 1.62.3 to 1.63.0. - [Release notes](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/releases) - [Commits](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright/commits/v1.63.0) --- updated-dependencies: - dependency-name: patchright dependency-version: 1.63.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 28 +++++++++------------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 2bf3e7017916..fe60aae7bed2 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "otplib": "13.5.0", "p-map": "7.0.7", "pac-proxy-agent": "9.1.0", - "patchright": "1.62.3", + "patchright": "1.63.0", "postal-mime": "3.0.0", "query-string": "9.5.1", "rate-limiter-flexible": "11.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df7eeaa73d2e..d14ae4be2e4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,8 +203,8 @@ importers: specifier: 9.1.0 version: 9.1.0 patchright: - specifier: 1.62.3 - version: 1.62.3 + specifier: 1.63.0 + version: 1.63.0 postal-mime: specifier: 3.0.0 version: 3.0.0 @@ -4109,11 +4109,6 @@ packages: resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} engines: {node: '>=14.14'} - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5214,13 +5209,13 @@ packages: parseley@0.13.1: resolution: {integrity: sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==} - patchright-core@1.62.3: - resolution: {integrity: sha512-RQf0M2THMf4TL9HNNUxdYbd4Oe3DOVPni6G/bJYsEpD9F1cgqEpWiTJIh+pfuK9JwGRo4Du6lzagJosMUXbh/Q==} + patchright-core@1.63.0: + resolution: {integrity: sha512-nuRJVTMVKoOFzY8vmwFqJZR3F0trUCKiPzDSgCOPq/yHHTefm9czILV23dXJsi5mvS7sCy7U1CqG7edA/21DtQ==} engines: {node: '>=20'} hasBin: true - patchright@1.62.3: - resolution: {integrity: sha512-TMpWzcZVWUmOe1251PHWpk0gTP2d7+mvS00h1CLL2IQAUKX7UexKfPqEjdKkH8F2TT5AlyrSbwZKr5lu1dThAQ==} + patchright@1.63.0: + resolution: {integrity: sha512-WVXhmNhAJ5I+CmcHEu/I3KMKhOlQaKS1XJsk+5nRIypKJ3Dq9IaQP29p4vB3I1FWCTarUXVRj02Na30wGcuztQ==} engines: {node: '>=20'} hasBin: true @@ -9403,9 +9398,6 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fsevents@2.3.2: - optional: true - fsevents@2.3.3: optional: true @@ -10789,13 +10781,11 @@ snapshots: leac: 0.7.0 peberminta: 0.10.0 - patchright-core@1.62.3: {} + patchright-core@1.63.0: {} - patchright@1.62.3: + patchright@1.63.0: dependencies: - patchright-core: 1.62.3 - optionalDependencies: - fsevents: 2.3.2 + patchright-core: 1.63.0 path-exists@4.0.0: {} From b3fe6cf1e310015ff7fe87e30e449982e1eecfd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=81=E6=97=A5=E5=85=89?= Date: Mon, 14 Sep 2026 22:45:42 +0800 Subject: [PATCH 0800/1190] feat(route/acfun): add user article route (#23282) Co-authored-by: tiaod --- lib/routes/acfun/user-article.ts | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 lib/routes/acfun/user-article.ts diff --git a/lib/routes/acfun/user-article.ts b/lib/routes/acfun/user-article.ts new file mode 100644 index 000000000000..b54258f5d526 --- /dev/null +++ b/lib/routes/acfun/user-article.ts @@ -0,0 +1,90 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import { ViewType } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const host = 'https://www.acfun.cn'; + +export const route: Route = { + path: '/user/article/:uid', + radar: [ + { + source: ['www.acfun.cn/u/:id'], + target: '/user/article/:id', + }, + ], + name: '用户文章', + parameters: { + uid: '用户 UID', + }, + categories: ['anime'], + example: '/acfun/user/article/1384329', + view: ViewType.Articles, + maintainers: ['tiaod'], + handler, +}; + +async function handler(ctx) { + const uid = ctx.req.param('uid'); + const link = `${host}/u/${uid}`; + const page = await ofetch(link); + const $ = load(page); + + const username = $('#ac-space-info .name').first().text().trim(); + const description = $('.signature .complete').text(); + const image = $('head style:contains("user-photo")') + .text() + .match(/.user-photo\{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)?.[1]; + + const list = $('#ac-space-article-list .weblog-item') + .toArray() + .map((item) => { + const $item = $(item); + const $title = $item.find('a.title'); + const itemDate = $item + .find('.info') + .text() + .match(/\d{4}\/\d{1,2}\/\d{1,2}/)?.[0]; + + return { + title: $title.text(), + link: `${host}${$title.attr('href')}`, + pubDate: itemDate ? parseDate(itemDate, 'YYYY/MM/DD') : undefined, + }; + }); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link, async () => { + const articlePage = await ofetch(item.link, { + headers: { + referer: link, + }, + }); + const $article = load(articlePage); + const articleInfo = $article('.main script') + .text() + .match(/window\.articleInfo = (.*);\n\s*window.likeDomain/)![1]; + const data = JSON.parse(articleInfo); + + return { + ...item, + description: data.parts[0].content, + author: data.user?.name, + category: [...(data.channel?.name ? [data.channel.name] : []), ...(data.tagList ?? []).map((tag) => tag.name)], + }; + }) + ) + ); + + return { + title: username ? `${username}的文章` : $('title').text(), + link, + description, + image, + item: items, + }; +} From cc17fe841c6074f49ce42ff16effb48c178f4e09 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 23:39:58 +0900 Subject: [PATCH 0801/1190] feat(route): add inshokuten rent benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /inshokuten/rent-benchmark/:area?/:line? emitting one item per 市区町村 (or per station when a line id is given) from 飲食店.COM's 賃料相場 detail pages, with a shared RentBenchmarkExtra type in temposmart/utils (円/坪/月, unknown = null, site text kept in raw). --- lib/routes/inshokuten/rent-benchmark.ts | 200 ++++++++++++++++++++++++ lib/routes/temposmart/utils.ts | 18 +++ 2 files changed, 218 insertions(+) create mode 100644 lib/routes/inshokuten/rent-benchmark.ts diff --git a/lib/routes/inshokuten/rent-benchmark.ts b/lib/routes/inshokuten/rent-benchmark.ts new file mode 100644 index 000000000000..f3c2964bc564 --- /dev/null +++ b/lib/routes/inshokuten/rent-benchmark.ts @@ -0,0 +1,200 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { RentBenchmarkExtra } from '@/routes/temposmart/utils'; +import { clean, parseJpy } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.inshokuten.com'; +const MARKET = `${HOST}/bukken/kanto/market/rent`; +const DETAIL_CONCURRENCY = 2; +const DETAIL_CACHE_SECONDS = 24 * 60 * 60; // the benchmarks change monthly at most + +/** Area selection pages of the 首都圏 edition (`/market/rent/line/{slug}`); line ids on them are area-scoped. */ +const AREAS = [ + { slug: '23ward', label: '東京23区', pref: '東京都' }, + { slug: '23ward_out', label: '東京都下', pref: '東京都' }, + { slug: 'chiba', label: '千葉', pref: '千葉県' }, + { slug: 'saitama', label: '埼玉', pref: '埼玉県' }, + { slug: 'yokohama_kawasaki', label: '神奈川', pref: '神奈川県' }, +]; + +interface Target { + name: string; + url: string; +} + +/** Selection pages list `

  • 千代田区` (or `q[station_id]=243` for a line). */ +const parseTargets = (html: string, param: 'region_id' | 'station_id'): Target[] => { + const $ = load(html); + return $(`a[href*="${param}"]`) + .toArray() + .map((a) => ({ name: clean($(a).text()) ?? '', url: new URL($(a).attr('href') ?? '', HOST).href })) + .filter((t) => t.name !== '' && t.url.includes(param)); +}; + +/** Lines are `JR山手線` on the area page; the numeric id is scoped to that area. */ +const parseLines = (html: string): Array<{ id: string; name: string }> => { + const $ = load(html); + return $('a[href*="/market/rent/station/"]') + .toArray() + .map((a) => ({ + id: + $(a) + .attr('href') + ?.match(/\/station\/(\d+)/)?.[1] ?? '', + name: clean($(a).text()) ?? '', + })) + .filter((l) => l.id !== '' && l.name !== ''); +}; + +/** + * Detail page (`/market/rent/result/?q[region_id]=10` or `?q[line_id]=2&q[station_id]=243`): + * h2.rate '千代田区の店舗賃料相場情報(直近1年間)', #rate-total th → td: 平均坪単価 / 最高坪単価 / 最低坪単価 / 一番多い階, + * 賃料分布図 hidden inputs js-circle-graph-val1..6 = listing counts per rent bucket (A: 20万円未満 … F: 100万円以上), + * yearly trend table (平均坪単価 by year). No 中央値 and no 更新日 are published. + */ +const parseDetail = (html: string, base: Pick): RentBenchmarkExtra => { + const $ = load(html); + const cell = (label: string): string | null => clean($(`#rate-total th:contains("${label}")`).first().next('td').text()); + const period = + $('h2.rate') + .first() + .text() + .match(/(([^)]+))/)?.[1] ?? null; + const buckets = $('input[name^="js-circle-graph-val"]') + .toArray() + .map((el) => Number($(el).attr('value'))) + .filter((n) => Number.isFinite(n)); + const trend = $('#rate-total') + .eq(1) + .find('tr') + .toArray() + .map((tr) => clean($(tr).text())) + .filter((t): t is string => t !== null && /^\d{4}年/.test(t)) + .join(' / '); + const raw: RentBenchmarkExtra['raw'] = Object.fromEntries( + ( + [ + ['avg', cell('平均坪単価')], + ['max', cell('最高坪単価')], + ['min', cell('最低坪単価')], + ['top_floor', cell('一番多い階')], + ['distribution', buckets.length > 0 ? buckets.join(',') : null], + ['distribution_buckets', clean($('.right-graf-ex').text())], + ['trend', trend === '' ? null : trend], + ] as Array<[string, string | null]> + ).filter((e): e is [string, string] => e[1] !== null) + ); + return { + source: 'inshokuten', + ...base, + rent_per_tsubo_median_jpy: null, + rent_per_tsubo_avg_jpy: parseJpy(raw.avg ?? null), + rent_per_tsubo_min_jpy: parseJpy(raw.min ?? null), + rent_per_tsubo_max_jpy: parseJpy(raw.max ?? null), + sample_count: buckets.length > 0 ? buckets.reduce((a, b) => a + b, 0) : null, + period, + raw, + }; +}; + +const describe = (x: RentBenchmarkExtra): string => + [ + x.rent_per_tsubo_avg_jpy === null ? null : `平均坪単価 ${x.rent_per_tsubo_avg_jpy.toLocaleString('ja-JP')}円`, + x.rent_per_tsubo_max_jpy === null ? null : `最高 ${x.rent_per_tsubo_max_jpy.toLocaleString('ja-JP')}円`, + x.rent_per_tsubo_min_jpy === null ? null : `最低 ${x.rent_per_tsubo_min_jpy.toLocaleString('ja-JP')}円`, + x.sample_count === null ? null : `${x.period ?? ''}${x.sample_count}件`, + x.raw.top_floor === undefined ? null : `一番多い階 ${x.raw.top_floor}`, + ] + .filter((p): p is string => p !== null) + .join(' / '); + +const toItem = (target: Target, kind: RentBenchmarkExtra['area_kind'], pref: string): Promise => + cache.tryGet( + target.url, + async (): Promise => { + const extra = parseDetail(await ofetch(target.url), { area_kind: kind, area_name: target.name, pref }); + return { + title: `${extra.area_name} 賃料相場 ${extra.period ?? ''}`.trim(), + link: `${target.url}#rate-total`, + guid: target.url, + description: describe(extra), + _extra: extra, + }; + }, + DETAIL_CACHE_SECONDS + ) as Promise; + +export const handler = async (ctx): Promise => { + const slug: string = ctx.req.param('area') ?? '23ward'; + const lineId: string | undefined = ctx.req.param('line'); + const area = AREAS.find((a) => a.slug === slug); + if (!area) { + throw new Error(`Unknown area "${slug}", expected one of ${AREAS.map((a) => a.slug).join(', ')}`); + } + const areaUrl = `${MARKET}/line/${area.slug}`; + const areaHtml: string = await cache.tryGet(areaUrl, () => ofetch(areaUrl), DETAIL_CACHE_SECONDS); + + if (lineId === undefined) { + const wards = parseTargets(areaHtml, 'region_id'); + return { + title: `飲食店.COM 賃料相場 (${area.label}・市区町村別)`, + link: areaUrl, + language: 'ja', + item: await pMap(wards, (w) => toItem(w, 'ward', area.pref), { concurrency: DETAIL_CONCURRENCY }), + }; + } + + const line = parseLines(areaHtml).find((l) => l.id === lineId); + if (!line) { + throw new Error(`Line ${lineId} is not listed for ${area.label}; see ${areaUrl} for its line ids`); + } + const lineUrl = `${MARKET}/station/${line.id}`; + const stations = parseTargets(await cache.tryGet(lineUrl, () => ofetch(lineUrl), DETAIL_CACHE_SECONDS), 'station_id'); + return { + title: `飲食店.COM 賃料相場 (${area.label}・${line.name}・駅別)`, + link: lineUrl, + language: 'ja', + item: await pMap(stations, (s) => toItem(s, 'station', area.pref), { concurrency: DETAIL_CONCURRENCY }), + }; +}; + +export const route: Route = { + path: '/rent-benchmark/:area?/:line?', + name: '賃料相場', + url: 'www.inshokuten.com', + maintainers: ['pseudoyu'], + handler, + example: '/inshokuten/rent-benchmark/23ward', + parameters: { + area: { + description: '首都圏 sub-area', + default: '23ward', + options: AREAS.map((a) => ({ value: a.slug, label: a.label })), + }, + line: { + description: 'Line id from the area page (`/bukken/kanto/market/rent/line/{area}`, e.g. `2` = JR山手線 in 東京23区); when given, one item per station on that line instead of one per 市区町村', + }, + }, + description: `Restaurant-property rent benchmarks (坪単価, 消費税込み募集金額, 直近1年間) published by 飲食店.COM. Without \`line\` the feed has one item per 市区町村 of the area; with \`line\` one item per station on that line. Each item's \`_extra\` carries \`rent_per_tsubo_avg_jpy\` / \`_min_jpy\` / \`_max_jpy\` (円/坪/月), \`sample_count\` (sum of the 賃料分布図 buckets), \`period\` and the raw site text; the site publishes no 中央値 and no 更新日, so \`rent_per_tsubo_median_jpy\` is always \`null\` and items carry no \`pubDate\`. Detail pages are cached for one day.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.inshokuten.com/bukken/kanto/market/rent/line/:area'], + target: '/rent-benchmark/:area', + }, + { + source: ['www.inshokuten.com/bukken/kanto/market/rent/station/:line'], + target: '/rent-benchmark/23ward/:line', + }, + ], +}; diff --git a/lib/routes/temposmart/utils.ts b/lib/routes/temposmart/utils.ts index bef2adbb238a..5952dd3e2818 100644 --- a/lib/routes/temposmart/utils.ts +++ b/lib/routes/temposmart/utils.ts @@ -31,6 +31,24 @@ export interface ListingExtra { raw: Record; } +/** + * One row of a 賃料相場 (rent benchmark) page — 円 per 坪 per month, unknown = `null`. + * Sites that publish 万円 or per-㎡ figures are converted here and keep the original text in `raw`. + */ +export interface RentBenchmarkExtra { + source: string; // site id, e.g. 'inshokuten' + area_kind: 'station' | 'ward'; + area_name: string; // 新宿 / 千代田区 (station names without 駅) + pref: string; // 東京都 / 千葉県 … + rent_per_tsubo_median_jpy: number | null; + rent_per_tsubo_avg_jpy: number | null; + rent_per_tsubo_min_jpy: number | null; + rent_per_tsubo_max_jpy: number | null; + sample_count: number | null; + period: string | null; // '2026-08' or the page's stated period, e.g. '直近1年間' + raw: Record; +} + const TSUBO_M2 = 3.30579; /** Collapse whitespace from cheerio `.text()`; empty → null. */ From 4bbea2c72aa4619d7b05191ebd82f6643b7e6a7e Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 23:39:59 +0900 Subject: [PATCH 0802/1190] feat(route): add abc-tenpo rent benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /abc-tenpo/rent-benchmark/:pref? emitting one item per 区 of 東京23区 from ABC 店舗's エリア別の賃料相場 page (the only prefecture the site publishes). --- lib/routes/abc-tenpo/rent-benchmark.ts | 113 +++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 lib/routes/abc-tenpo/rent-benchmark.ts diff --git a/lib/routes/abc-tenpo/rent-benchmark.ts b/lib/routes/abc-tenpo/rent-benchmark.ts new file mode 100644 index 000000000000..9bdb6feeb148 --- /dev/null +++ b/lib/routes/abc-tenpo/rent-benchmark.ts @@ -0,0 +1,113 @@ +import { load } from 'cheerio'; + +import type { RentBenchmarkExtra } from '@/routes/temposmart/utils'; +import { clean, parseJpy } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const PAGE = 'https://www.abc-tenpo.com/feature/rent'; +const PAGE_CACHE_SECONDS = 24 * 60 * 60; // the figures change monthly at most + +/** The site only publishes 東京23区 on this page (Kanto edition). */ +const PREFECTURES = [{ slug: 'tokyo', label: '東京都' }]; + +/** + * `/feature/rent`: one `.p-rent` block (東京23区) with `.p-rent__row` per ward: + * .p-rent__name '千代田区', .p-rent__price '28,032円', .p-rent__button a → /property/search?muni[]=141. + * The page states only 「賃料相場は坪単価」 — no 平均 / 中央値 label, no sample count, no period and no 更新日. + * The single figure is stored as `rent_per_tsubo_avg_jpy`; treat it as the site's unqualified 相場. + */ +const parseRows = (html: string, pref: string): DataItem[] => { + const $ = load(html); + const note = clean($('*:contains("賃料相場は坪単価")').last().text()); + return $('.p-rent .p-rent__row') + .toArray() + .map((el): DataItem | null => { + const $el = $(el); + const name = clean($el.find('.p-rent__name').text()); + const price = clean($el.find('.p-rent__price').text()); + const searchUrl = $el.find('.p-rent__button a').attr('href'); + const muni = searchUrl?.match(/muni(?:%5B%5D|\[\])=(\d+)/)?.[1]; + if (!name || !price) { + return null; + } + const raw: RentBenchmarkExtra['raw'] = Object.fromEntries( + ( + [ + ['price', price], + ['price_label', '賃料相場(坪単価)'], + ['search_url', searchUrl ?? null], + ['note', note], + ] as Array<[string, string | null]> + ).filter((e): e is [string, string] => e[1] !== null) + ); + const extra: RentBenchmarkExtra = { + source: 'abc-tenpo', + area_kind: 'ward', + area_name: name, + pref, + rent_per_tsubo_median_jpy: null, + rent_per_tsubo_avg_jpy: parseJpy(price), + rent_per_tsubo_min_jpy: null, + rent_per_tsubo_max_jpy: null, + sample_count: null, + period: null, + raw, + }; + const anchor = muni ? `muni-${muni}` : encodeURIComponent(name); + return { + title: `${name} 賃料相場`, + link: `${PAGE}#${anchor}`, + guid: `${PAGE}#${anchor}`, + description: extra.rent_per_tsubo_avg_jpy === null ? price : `賃料相場(坪単価) ${extra.rent_per_tsubo_avg_jpy.toLocaleString('ja-JP')}円`, + _extra: extra, + }; + }) + .filter((item): item is DataItem => item !== null); +}; + +export const handler = async (ctx): Promise => { + const slug: string = ctx.req.param('pref') ?? 'tokyo'; + const prefecture = PREFECTURES.find((p) => p.slug === slug); + if (!prefecture) { + throw new Error(`Unknown prefecture "${slug}"; the site only publishes ${PREFECTURES.map((p) => `${p.slug} (${p.label})`).join(', ')}`); + } + const html: string = await cache.tryGet(PAGE, () => ofetch(PAGE), PAGE_CACHE_SECONDS); + return { + title: `ABC店舗 エリア別賃料相場 (${prefecture.label})`, + link: PAGE, + language: 'ja', + item: parseRows(html, prefecture.label), + }; +}; + +export const route: Route = { + path: '/rent-benchmark/:pref?', + name: 'エリア別賃料相場', + url: 'www.abc-tenpo.com', + maintainers: ['pseudoyu'], + handler, + example: '/abc-tenpo/rent-benchmark/tokyo', + parameters: { + pref: { + description: 'Prefecture slug; the site currently publishes 東京23区 only', + default: 'tokyo', + options: PREFECTURES.map((p) => ({ value: p.slug, label: p.label })), + }, + }, + description: `Ward-level restaurant-property rent benchmarks (坪単価) from ABC 店舗's エリア別の賃料相場 page, one item per 区 of 東京23区. The page gives a single unqualified 相場 figure per ward (「賃料相場は坪単価」, compiled from the site's own listings) — it is stored in \`_extra.rent_per_tsubo_avg_jpy\`; 中央値 / 最高 / 最低, sample count, period and 更新日 are not published and stay \`null\`. The page is cached for one day.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.abc-tenpo.com/feature/rent'], + target: '/rent-benchmark', + }, + ], +}; From a1b382afd61d6956c0bf573e85a86489ca2302a5 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Mon, 14 Sep 2026 23:42:23 +0900 Subject: [PATCH 0803/1190] =?UTF-8?q?refactor(route):=20keep=20unlabelled?= =?UTF-8?q?=20=E5=9D=AA=E5=8D=98=E4=BE=A1=20in=20rent=5Fper=5Ftsubo=5Fjpy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit abc-tenpo only publishes an unqualified 賃料相場 figure, so it no longer claims to be an average; RentBenchmarkExtra gains rent_per_tsubo_jpy for that case and avg/median stay null unless the site labels them. --- lib/routes/abc-tenpo/rent-benchmark.ts | 9 +++++---- lib/routes/inshokuten/rent-benchmark.ts | 1 + lib/routes/temposmart/utils.ts | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/routes/abc-tenpo/rent-benchmark.ts b/lib/routes/abc-tenpo/rent-benchmark.ts index 9bdb6feeb148..a164449e27f8 100644 --- a/lib/routes/abc-tenpo/rent-benchmark.ts +++ b/lib/routes/abc-tenpo/rent-benchmark.ts @@ -16,7 +16,7 @@ const PREFECTURES = [{ slug: 'tokyo', label: '東京都' }]; * `/feature/rent`: one `.p-rent` block (東京23区) with `.p-rent__row` per ward: * .p-rent__name '千代田区', .p-rent__price '28,032円', .p-rent__button a → /property/search?muni[]=141. * The page states only 「賃料相場は坪単価」 — no 平均 / 中央値 label, no sample count, no period and no 更新日. - * The single figure is stored as `rent_per_tsubo_avg_jpy`; treat it as the site's unqualified 相場. + * The single figure is stored as `rent_per_tsubo_jpy` (statistic unspecified); avg / median / min / max stay null. */ const parseRows = (html: string, pref: string): DataItem[] => { const $ = load(html); @@ -47,8 +47,9 @@ const parseRows = (html: string, pref: string): DataItem[] => { area_kind: 'ward', area_name: name, pref, + rent_per_tsubo_jpy: parseJpy(price), rent_per_tsubo_median_jpy: null, - rent_per_tsubo_avg_jpy: parseJpy(price), + rent_per_tsubo_avg_jpy: null, rent_per_tsubo_min_jpy: null, rent_per_tsubo_max_jpy: null, sample_count: null, @@ -60,7 +61,7 @@ const parseRows = (html: string, pref: string): DataItem[] => { title: `${name} 賃料相場`, link: `${PAGE}#${anchor}`, guid: `${PAGE}#${anchor}`, - description: extra.rent_per_tsubo_avg_jpy === null ? price : `賃料相場(坪単価) ${extra.rent_per_tsubo_avg_jpy.toLocaleString('ja-JP')}円`, + description: extra.rent_per_tsubo_jpy === null ? price : `賃料相場(坪単価) ${extra.rent_per_tsubo_jpy.toLocaleString('ja-JP')}円`, _extra: extra, }; }) @@ -96,7 +97,7 @@ export const route: Route = { options: PREFECTURES.map((p) => ({ value: p.slug, label: p.label })), }, }, - description: `Ward-level restaurant-property rent benchmarks (坪単価) from ABC 店舗's エリア別の賃料相場 page, one item per 区 of 東京23区. The page gives a single unqualified 相場 figure per ward (「賃料相場は坪単価」, compiled from the site's own listings) — it is stored in \`_extra.rent_per_tsubo_avg_jpy\`; 中央値 / 最高 / 最低, sample count, period and 更新日 are not published and stay \`null\`. The page is cached for one day.`, + description: `Ward-level restaurant-property rent benchmarks (坪単価) from ABC 店舗's エリア別の賃料相場 page, one item per 区 of 東京23区. The page gives a single unqualified 相場 figure per ward (「賃料相場は坪単価」, compiled from the site's own listings) — it is stored in \`_extra.rent_per_tsubo_jpy\` because the site does not say whether it is a mean or a median; 平均 / 中央値 / 最高 / 最低, sample count, period and 更新日 are not published and stay \`null\`. The page is cached for one day.`, categories: ['other'], features: { requireConfig: false, diff --git a/lib/routes/inshokuten/rent-benchmark.ts b/lib/routes/inshokuten/rent-benchmark.ts index f3c2964bc564..073fc8415224 100644 --- a/lib/routes/inshokuten/rent-benchmark.ts +++ b/lib/routes/inshokuten/rent-benchmark.ts @@ -91,6 +91,7 @@ const parseDetail = (html: string, base: Pick Date: Mon, 14 Sep 2026 15:38:17 +0000 Subject: [PATCH 0804/1190] style: auto format --- lib/routes/abc-tenpo/rent-benchmark.ts | 2 +- lib/routes/inshokuten/rent-benchmark.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/abc-tenpo/rent-benchmark.ts b/lib/routes/abc-tenpo/rent-benchmark.ts index a164449e27f8..5ceda61ccf87 100644 --- a/lib/routes/abc-tenpo/rent-benchmark.ts +++ b/lib/routes/abc-tenpo/rent-benchmark.ts @@ -97,7 +97,7 @@ export const route: Route = { options: PREFECTURES.map((p) => ({ value: p.slug, label: p.label })), }, }, - description: `Ward-level restaurant-property rent benchmarks (坪単価) from ABC 店舗's エリア別の賃料相場 page, one item per 区 of 東京23区. The page gives a single unqualified 相場 figure per ward (「賃料相場は坪単価」, compiled from the site's own listings) — it is stored in \`_extra.rent_per_tsubo_jpy\` because the site does not say whether it is a mean or a median; 平均 / 中央値 / 最高 / 最低, sample count, period and 更新日 are not published and stay \`null\`. The page is cached for one day.`, + description: `Ward-level restaurant-property rent benchmarks (坪単価) from ABC 店舗's エリア別の賃料相場 page, one item per 区 of 東京 23 区. The page gives a single unqualified 相場 figure per ward (「賃料相場は坪単価」, compiled from the site's own listings) — it is stored in \`_extra.rent_per_tsubo_jpy\` because the site does not say whether it is a mean or a median; 平均 / 中央値 / 最高 / 最低,sample count, period and 更新日 are not published and stay \`null\`. The page is cached for one day.`, categories: ['other'], features: { requireConfig: false, diff --git a/lib/routes/inshokuten/rent-benchmark.ts b/lib/routes/inshokuten/rent-benchmark.ts index 073fc8415224..0caaac725652 100644 --- a/lib/routes/inshokuten/rent-benchmark.ts +++ b/lib/routes/inshokuten/rent-benchmark.ts @@ -180,7 +180,7 @@ export const route: Route = { description: 'Line id from the area page (`/bukken/kanto/market/rent/line/{area}`, e.g. `2` = JR山手線 in 東京23区); when given, one item per station on that line instead of one per 市区町村', }, }, - description: `Restaurant-property rent benchmarks (坪単価, 消費税込み募集金額, 直近1年間) published by 飲食店.COM. Without \`line\` the feed has one item per 市区町村 of the area; with \`line\` one item per station on that line. Each item's \`_extra\` carries \`rent_per_tsubo_avg_jpy\` / \`_min_jpy\` / \`_max_jpy\` (円/坪/月), \`sample_count\` (sum of the 賃料分布図 buckets), \`period\` and the raw site text; the site publishes no 中央値 and no 更新日, so \`rent_per_tsubo_median_jpy\` is always \`null\` and items carry no \`pubDate\`. Detail pages are cached for one day.`, + description: `Restaurant-property rent benchmarks (坪単価,消費税込み募集金額,直近 1 年間) published by 飲食店.COM. Without \`line\` the feed has one item per 市区町村 of the area; with \`line\` one item per station on that line. Each item's \`_extra\` carries \`rent_per_tsubo_avg_jpy\` / \`_min_jpy\` / \`_max_jpy\` (円 / 坪 / 月), \`sample_count\` (sum of the 賃料分布図 buckets), \`period\` and the raw site text; the site publishes no 中央値 and no 更新日,so \`rent_per_tsubo_median_jpy\` is always \`null\` and items carry no \`pubDate\`. Detail pages are cached for one day.`, categories: ['other'], features: { requireConfig: false, From a696a3bcf573e90d2b7127ed78a30861328a44ed Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:06:18 +0900 Subject: [PATCH 0805/1190] feat(route): add tokyometro ridership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /tokyometro/ridership/:year? emitting one item per station and fiscal year from 東京メトロ's 各駅の乗降人員ランキング (index.html = latest, 2020.html … via the page's year tabs). Ranked and 直通連絡駅 tables are both read; the latter has no rank. ▲ in 前年比 is a decrease. RidershipExtra (source, operator, station, line, fiscal_year, daily_average 人/日, rank, yoy_pct, raw) lives in tokyometro/utils and is shared by the other operators' ridership routes. --- lib/routes/tokyometro/namespace.ts | 12 +++ lib/routes/tokyometro/ridership.ts | 137 +++++++++++++++++++++++++++++ lib/routes/tokyometro/utils.ts | 62 +++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 lib/routes/tokyometro/namespace.ts create mode 100644 lib/routes/tokyometro/ridership.ts create mode 100644 lib/routes/tokyometro/utils.ts diff --git a/lib/routes/tokyometro/namespace.ts b/lib/routes/tokyometro/namespace.ts new file mode 100644 index 000000000000..584148ba3e96 --- /dev/null +++ b/lib/routes/tokyometro/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Tokyo Metro', + url: 'www.tokyometro.jp', + description: '東京メトロ — 東京地下鉄株式会社', + lang: 'ja', + + ja: { + name: '東京メトロ', + }, +}; diff --git a/lib/routes/tokyometro/ridership.ts b/lib/routes/tokyometro/ridership.ts new file mode 100644 index 000000000000..96de5ad064b3 --- /dev/null +++ b/lib/routes/tokyometro/ridership.ts @@ -0,0 +1,137 @@ +import { load } from 'cheerio'; + +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +import type { RidershipExtra } from './utils'; +import { cellText, parseCount, parseFiscalYear, parsePct, ridershipItem } from './utils'; + +const BASE = 'https://www.tokyometro.jp/corporate/enterprise/passenger_rail/transportation/passengers/'; +const OPERATOR = '東京メトロ'; + +/** + * 各駅の乗降人員ランキング — one page per fiscal year (index.html = latest, `2024.html` …), linked from `ul.v2_linkTab`. + * Two station tables, both `table.v2_table`, distinguished by their `summary`: + * 駅別乗降人員順位表: 順位 | 路線 (one per line) | 駅名 | 人員 | 前年比 + * 直通連絡駅・共用駅: 線名 | 駅名 | 人員 | 前年比 (no rank — these stations are counted by the partner railway) + * The third table (総駅数) is not station data. 前年比 uses ▲ for a decrease. + */ +const parsePage = (html: string, link: string): { fiscalYear: number; items: Array>; years: Record } => { + const $ = load(html); + const fiscalYear = parseFiscalYear($('h3').first().text()); + if (fiscalYear === null) { + throw new Error('tokyometro: fiscal year not found in the page heading'); + } + const years = Object.fromEntries( + $('ul.v2_linkTab a') + .toArray() + .map((a) => [String(parseFiscalYear($(a).text())), $(a).attr('href') ?? '']) + .filter(([y, href]) => y !== 'null' && href !== '') + ); + + const items = $('table.v2_table') + .toArray() + .flatMap((table) => { + const summary = $(table).attr('summary') ?? ''; + const ranked = summary.includes('順位表') && !summary.includes('総駅数'); + const through = summary.includes('直通連絡駅'); + if (!ranked && !through) { + return []; + } + return $(table) + .find('tr') + .toArray() + .map((tr) => { + const cells = $(tr).find('td').toArray(); + // ranked: [順位, 路線, 駅名, 人員, 前年比]; through: [線名, 駅名, 人員, 前年比] + const [rankCell, lineCell, stationCell, countCell, yoyCell] = ranked ? cells : [undefined, ...cells]; + if (!stationCell || !countCell) { + return null; + } + const lines = $(lineCell) + .find('img') + .toArray() + .map((img) => cellText($(img).attr('alt'))) + .filter(Boolean); + const raw: RidershipExtra['raw'] = { + table: through ? '他鉄道との直通連絡駅および共用している駅' : '駅別乗降人員順位表', + rank: rankCell ? cellText($(rankCell).text()) : '', + lines: lines.join(' '), + station: cellText($(stationCell).text()), + passengers: cellText($(countCell).text()), + yoy: cellText($(yoyCell).text()), + }; + if (raw.station === '') { + return null; + } + const extra: RidershipExtra = { + source: 'tokyometro', + operator: OPERATOR, + station: raw.station, + line: lines.length > 0 ? lines.join('・') : null, + fiscal_year: fiscalYear, + daily_average: parseCount(raw.passengers), + unit: '人/日', + rank: rankCell ? parseCount(raw.rank) : null, + yoy_pct: parsePct(raw.yoy), + raw, + }; + return ridershipItem(extra, link); + }) + .filter((item): item is ReturnType => item !== null); + }); + return { fiscalYear, items, years }; +}; + +export const handler = async (ctx): Promise => { + const year: string | undefined = ctx.req.param('year'); + const indexLink = `${BASE}index.html`; + const latest = (await cache.tryGet(indexLink, async () => parsePage(await ofetch(indexLink), indexLink))) as ReturnType; + + let page = latest; + let link = indexLink; + if (year !== undefined && Number(year) !== latest.fiscalYear) { + const href = latest.years[year]; + if (!href) { + throw new Error(`tokyometro: FY${year} is not published; available: ${Object.keys(latest.years).join(', ')}`); + } + link = new URL(href, BASE).href; + page = (await cache.tryGet(link, async () => parsePage(await ofetch(link), link))) as ReturnType; + } + + return { + title: `東京メトロ 各駅の乗降人員ランキング(${page.fiscalYear}年度一日平均)`, + link, + language: 'ja', + item: page.items, + }; +}; + +export const route: Route = { + path: '/ridership/:year?', + name: '各駅の乗降人員ランキング', + url: 'www.tokyometro.jp', + maintainers: ['pseudoyu'], + handler, + example: '/tokyometro/ridership', + parameters: { + year: { + description: 'Fiscal year (`2020` … latest); omit for the latest year', + }, + }, + description: `Annual 駅別乗降人員 (one-day average) for every Tokyo Metro station, from [各駅の乗降人員ランキング](${BASE}index.html). One item per station and fiscal year; \`_extra\` carries \`operator\`, \`station\`, \`line\` (several lines joined with \`・\`), \`fiscal_year\`, \`daily_average\` (人/日), \`rank\`, \`yoy_pct\` and the page's cell text in \`raw\`. Stations in the 直通連絡駅・共用駅 table (渋谷, 北千住, 中目黒, …) have no rank because the operator does not rank them. 国会議事堂前 and 溜池山王 are listed as one station (国会・溜池), as on the page. The operator does not publish a release date, so items have no \`pubDate\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.tokyometro.jp/corporate/enterprise/passenger_rail/transportation/passengers/:page'], + target: '/ridership', + }, + ], +}; diff --git a/lib/routes/tokyometro/utils.ts b/lib/routes/tokyometro/utils.ts new file mode 100644 index 000000000000..fbbedb182b9e --- /dev/null +++ b/lib/routes/tokyometro/utils.ts @@ -0,0 +1,62 @@ +/** + * Shared shape + helpers for the 駅別乗降人員 (station ridership) routes of Tokyo private railways + * (tokyometro / tokyu / odakyu / keio). One item per station per fiscal year; unknown = `null`, never `0`; + * the operator's cell text is kept verbatim in `raw`. + */ + +import type { DataItem } from '@/types'; + +export type RidershipSource = 'tokyometro' | 'tokyu' | 'odakyu' | 'keio'; + +export interface RidershipExtra { + source: RidershipSource; + operator: string; // 東京メトロ / 東急電鉄 / 小田急電鉄 / 京王電鉄 + station: string; // as printed by the operator (without 駅) + line: string | null; // 路線; several lines joined with '・' when the operator lists a station once for all of them + fiscal_year: number; // 2025 = FY2025 (2025-04 … 2026-03) + daily_average: number | null; // 一日平均乗降人員 + unit: '人/日'; + rank: number | null; // only when the operator publishes a 順位 + yoy_pct: number | null; // 前年比 / 増減率 (%); negative when the operator prints ▲ or a minus + raw: Record; +} + +/** Collapse whitespace (incl. NBSP) from cheerio `.text()`. */ +export const cellText = (text: string | undefined | null): string => (text ?? '').replaceAll(/\s+/g, ' ').trim(); + +/** '529,947' → 529947; '-' / '' / '(151,951)' (a parenthesised note) → null. */ +export const parseCount = (text: string): number | null => { + const s = text.replaceAll(/[,,\s]/g, '').normalize('NFKC'); + return /^\d+$/.test(s) ? Number(s) : null; +}; + +/** '2.3' → 2.3; '▲ 11.9' / '-0.1' / '△0.8' → negative; '' / '-' → null. */ +export const parsePct = (text: string): number | null => { + const s = text.replaceAll(/[\s,]/g, '').normalize('NFKC'); + const m = s.match(/^([▲△-])?(\d+(?:\.\d+)?)$/); + if (!m) { + return null; + } + const n = Number(m[2]); + return m[1] ? -n : n; +}; + +/** '2025年度' anywhere in the text → 2025; nothing → null. */ +export const parseFiscalYear = (text: string): number | null => { + const m = text.match(/(\d{4})年度/); + return m ? Number(m[1]) : null; +}; + +/** Item title / guid / description are derived from `_extra` only, so all four operators read the same way. */ +export const ridershipItem = (extra: RidershipExtra, link: string): DataItem & { _extra: RidershipExtra } => { + const people = extra.daily_average === null ? '不明' : `${extra.daily_average.toLocaleString('ja-JP')}人/日`; + const yoy = extra.yoy_pct === null ? null : `前年比 ${extra.yoy_pct > 0 ? '+' : ''}${extra.yoy_pct}%`; + return { + title: `${extra.station}(${extra.line ?? extra.operator}) ${extra.fiscal_year}年度 ${people}`, + link, + guid: `${extra.source}/ridership:${extra.fiscal_year}:${extra.line ?? '-'}:${extra.station}`, + // Operators do not publish a release date for the table; no pubDate is fabricated. + description: [extra.operator, extra.line, `${extra.fiscal_year}年度`, people, yoy, extra.rank === null ? null : `${extra.rank}位`].filter((p): p is string => p !== null).join(' / '), + _extra: extra, + }; +}; From c4422fa103a8409ed3a79ebc84fe2a9a3cfa53a5 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:06:19 +0900 Subject: [PATCH 0806/1190] feat(route): add tokyu ridership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /tokyu/ridership/:year? emitting one item per station, line and fiscal year from 東急電鉄's 駅別乗降人員・輸送人員 year pages (2014 … latest, latest resolved from the index). 定期 / 定期外 / 計 / 前年比 / previous-year cells are kept in raw; 世田谷線 is published as a single 全線 figure and is not a station, so it is skipped. --- lib/routes/tokyu/namespace.ts | 12 +++ lib/routes/tokyu/ridership.ts | 136 ++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 lib/routes/tokyu/namespace.ts create mode 100644 lib/routes/tokyu/ridership.ts diff --git a/lib/routes/tokyu/namespace.ts b/lib/routes/tokyu/namespace.ts new file mode 100644 index 000000000000..5d890b6c2948 --- /dev/null +++ b/lib/routes/tokyu/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Tokyu Railways', + url: 'www.tokyu.co.jp', + description: '東急電鉄株式会社', + lang: 'ja', + + ja: { + name: '東急電鉄', + }, +}; diff --git a/lib/routes/tokyu/ridership.ts b/lib/routes/tokyu/ridership.ts new file mode 100644 index 000000000000..337680c1125b --- /dev/null +++ b/lib/routes/tokyu/ridership.ts @@ -0,0 +1,136 @@ +import { load } from 'cheerio'; + +import type { RidershipExtra } from '@/routes/tokyometro/utils'; +import { cellText, parseCount, parseFiscalYear, parsePct, ridershipItem } from '@/routes/tokyometro/utils'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const BASE = 'https://www.tokyu.co.jp/railway/company/business/passengers/'; +const OPERATOR = '東急電鉄'; + +/** + * 駅別乗降人員・輸送人員 — the index lists one page per fiscal year (`2014/` … `2025/`). + * A year page has one `table.table` per line, preceded by `.portal-heading h2` (line name): + * 駅名\種別 | 定期(人) | 定期外(人) | 計(人) | 前年比(%) | 年度 + * Columns are located by header text. 世田谷線 only has a 全線 row (no per-station figures), which is skipped. + */ +const parsePage = (html: string, link: string): { fiscalYear: number; items: Array> } => { + const $ = load(html); + const fiscalYear = parseFiscalYear($('title').text()); + if (fiscalYear === null) { + throw new Error('tokyu: fiscal year not found in the page title'); + } + + const items = $('table.table') + .toArray() + .flatMap((table) => { + const line = cellText($(table).closest('.portal-table').prevAll('.portal-heading').first().find('h2').text()) || null; + const headers = $(table) + .find('thead th') + .toArray() + .map((th) => cellText($(th).text())); + const col = (re: RegExp): number => headers.findIndex((h) => re.test(h)); + const [iCommuter, iOther, iTotal, iYoy, iPrev] = [/^定期\(/, /^定期外/, /^計/, /^前年比/, /年度$/].map(col); + if (iTotal < 0) { + return []; + } + return $(table) + .find('tbody tr') + .toArray() + .map((tr) => { + const station = cellText($(tr).find('th').first().text()); + const tds = $(tr) + .find('td') + .toArray() + .map((td) => cellText($(td).text())); + // is column 0 of the header, so index = header index - 1. + const at = (i: number): string => (i > 0 ? (tds[i - 1] ?? '') : ''); + if (station === '' || station === '全線') { + return null; + } + const raw: RidershipExtra['raw'] = { + station, + commuter_pass: at(iCommuter), + non_commuter: at(iOther), + total: at(iTotal), + yoy: at(iYoy), + [headers[iPrev] ?? 'previous_year']: at(iPrev), + }; + const extra: RidershipExtra = { + source: 'tokyu', + operator: OPERATOR, + station, + line, + fiscal_year: fiscalYear, + daily_average: parseCount(raw.total), + unit: '人/日', + rank: null, + yoy_pct: parsePct(raw.yoy), + raw, + }; + return ridershipItem(extra, link); + }) + .filter((item): item is ReturnType => item !== null); + }); + return { fiscalYear, items }; +}; + +/** Latest year = the largest `passengers//` link on the index page. */ +const latestYear = async (): Promise => { + const html = await ofetch(BASE); + const $ = load(html); + const years = $('a[href*="/passengers/"]') + .toArray() + .map((a) => Number(($(a).attr('href') ?? '').match(/\/passengers\/(\d{4})\/?$/)?.[1])) + .filter((y) => Number.isSafeInteger(y)); + if (years.length === 0) { + throw new Error('tokyu: no fiscal-year pages linked from the index'); + } + return Math.max(...years); +}; + +export const handler = async (ctx): Promise => { + const year: string | undefined = ctx.req.param('year'); + const fy = year === undefined ? ((await cache.tryGet('tokyu/ridership:latest', latestYear)) as number) : Number(year); + if (!Number.isSafeInteger(fy)) { + throw new TypeError(`tokyu: invalid year "${year}"`); + } + const link = `${BASE}${fy}/`; + const page = (await cache.tryGet(link, async () => parsePage(await ofetch(link), link))) as ReturnType; + + return { + title: `東急電鉄 ${page.fiscalYear}年度 駅別乗降人員`, + link, + language: 'ja', + item: page.items, + }; +}; + +export const route: Route = { + path: '/ridership/:year?', + name: '駅別乗降人員', + url: 'www.tokyu.co.jp', + maintainers: ['pseudoyu'], + handler, + example: '/tokyu/ridership', + parameters: { + year: { + description: 'Fiscal year (`2014` … latest); omit for the latest year', + }, + }, + description: `Annual 駅別乗降人員 (one-day average) for every Tokyu station, from [駅別乗降人員・輸送人員](${BASE}). One item per station, line and fiscal year (渋谷, 日吉, 蒲田 … appear once per line, as on the page); \`_extra\` carries \`operator\`, \`station\`, \`line\`, \`fiscal_year\`, \`daily_average\` (計, 人/日), \`yoy_pct\` and the page's cell text in \`raw\` (定期 / 定期外 / 計 / 前年比 / previous-year figure). \`rank\` is \`null\` because the operator publishes no ranking. 世田谷線 is published as a single 全線 figure and is not included. The operator does not publish a release date, so items have no \`pubDate\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.tokyu.co.jp/railway/company/business/passengers/:year', 'www.tokyu.co.jp/railway/company/business/passengers/'], + target: '/ridership/:year', + }, + ], +}; From 8070861319bc5b5264a1c7ea9d0c3bcb75e2572a Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:06:19 +0900 Subject: [PATCH 0807/1190] feat(route): add odakyu ridership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /odakyu/ridership emitting one item per station (小田原線 / 江ノ島線 / 多摩線) from 小田急電鉄's 駅別乗降人員・輸送人員ほか page, which only publishes the current fiscal year (taken from the page's データは…年度 note). 順位 and 増減率 come from the table as printed. --- lib/routes/odakyu/namespace.ts | 12 ++++ lib/routes/odakyu/ridership.ts | 103 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 lib/routes/odakyu/namespace.ts create mode 100644 lib/routes/odakyu/ridership.ts diff --git a/lib/routes/odakyu/namespace.ts b/lib/routes/odakyu/namespace.ts new file mode 100644 index 000000000000..26de516c3471 --- /dev/null +++ b/lib/routes/odakyu/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Odakyu Electric Railway', + url: 'www.odakyu.jp', + description: '小田急電鉄株式会社', + lang: 'ja', + + ja: { + name: '小田急電鉄', + }, +}; diff --git a/lib/routes/odakyu/ridership.ts b/lib/routes/odakyu/ridership.ts new file mode 100644 index 000000000000..39a9e4c9ba50 --- /dev/null +++ b/lib/routes/odakyu/ridership.ts @@ -0,0 +1,103 @@ +import { load } from 'cheerio'; + +import type { RidershipExtra } from '@/routes/tokyometro/utils'; +import { cellText, parseCount, parseFiscalYear, parsePct, ridershipItem } from '@/routes/tokyometro/utils'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const LINK = 'https://www.odakyu.jp/company/railroad/users/'; +const OPERATOR = '小田急電鉄'; + +/** + * 鉄道部門:駅別乗降人員・輸送人員ほか — a single page for the current fiscal year (`p.note` "データは2025年度のものです。"). + * Section 1日平均駅別乗降人員 has one `table.table--s` per line, each under `div.h3 > h3#line-N` (小田原線 / 江ノ島線 / 多摩線): + * 駅名 (th) | 人 | 増減率(パーセント) | 順位 + * Other sections (輸送人員の推移 …) are images, not tables. + */ +const parsePage = (html: string): { fiscalYear: number; items: Array> } => { + const $ = load(html); + const fiscalYear = parseFiscalYear($('p.note').text()); + if (fiscalYear === null) { + throw new Error('odakyu: fiscal year not found in the page note'); + } + + const items = $('table') + .toArray() + .flatMap((table) => { + const headers = $(table) + .find('tr') + .first() + .find('th') + .toArray() + .map((th) => cellText($(th).text())); + if (headers[0] !== '駅名') { + return []; + } + const line = cellText($(table).closest('.section-s').prev().find('h3').text()) || null; + return $(table) + .find('tr') + .slice(1) + .toArray() + .map((tr) => { + const station = cellText($(tr).find('th').first().text()); + const [count = '', yoy = '', rank = ''] = $(tr) + .find('td') + .toArray() + .map((td) => cellText($(td).text())); + if (station === '') { + return null; + } + const raw: RidershipExtra['raw'] = { station, passengers: count, change_pct: yoy, rank }; + const extra: RidershipExtra = { + source: 'odakyu', + operator: OPERATOR, + station, + line, + fiscal_year: fiscalYear, + daily_average: parseCount(count), + unit: '人/日', + rank: parseCount(rank), + yoy_pct: parsePct(yoy), + raw, + }; + return ridershipItem(extra, LINK); + }) + .filter((item): item is ReturnType => item !== null); + }); + return { fiscalYear, items }; +}; + +export const handler = async (): Promise => { + const page = (await cache.tryGet(LINK, async () => parsePage(await ofetch(LINK)))) as ReturnType; + return { + title: `小田急電鉄 ${page.fiscalYear}年度 1日平均駅別乗降人員`, + link: LINK, + language: 'ja', + item: page.items, + }; +}; + +export const route: Route = { + path: '/ridership', + name: '駅別乗降人員', + url: 'www.odakyu.jp', + maintainers: ['pseudoyu'], + handler, + example: '/odakyu/ridership', + parameters: {}, + description: `Annual 1日平均駅別乗降人員 for every Odakyu station (小田原線 / 江ノ島線 / 多摩線), from [鉄道部門:駅別乗降人員・輸送人員ほか](${LINK}). The operator publishes only the current fiscal year on this page, so there is no year parameter. One item per station; \`_extra\` carries \`operator\`, \`station\`, \`line\`, \`fiscal_year\`, \`daily_average\` (人/日), \`rank\` (順位 across all lines), \`yoy_pct\` (増減率) and the page's cell text in \`raw\`. The operator does not publish a release date, so items have no \`pubDate\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.odakyu.jp/company/railroad/users/'], + target: '/ridership', + }, + ], +}; From 9e69de2898f68d26e85215ccc24a8903c7648675 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:06:19 +0900 Subject: [PATCH 0808/1190] feat(route): add keio ridership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /keio/ridership emitting one item per station and year from 京王電鉄's 駅別 一日平均乗降人員 page, which prints the latest and the previous fiscal year side by side (京王線 / 井の頭線). yoy_pct for the latest year is computed from the two columns; 明大前's (乗換) transfer row is kept in raw.note and the 全線計 row is skipped. --- lib/routes/keio/namespace.ts | 12 ++++ lib/routes/keio/ridership.ts | 128 +++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 lib/routes/keio/namespace.ts create mode 100644 lib/routes/keio/ridership.ts diff --git a/lib/routes/keio/namespace.ts b/lib/routes/keio/namespace.ts new file mode 100644 index 000000000000..11ee8cd626ac --- /dev/null +++ b/lib/routes/keio/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Keio Corporation', + url: 'www.keio.co.jp', + description: '京王電鉄株式会社', + lang: 'ja', + + ja: { + name: '京王電鉄', + }, +}; diff --git a/lib/routes/keio/ridership.ts b/lib/routes/keio/ridership.ts new file mode 100644 index 000000000000..f1a841875ebf --- /dev/null +++ b/lib/routes/keio/ridership.ts @@ -0,0 +1,128 @@ +import type { CheerioAPI } from 'cheerio'; +import { load } from 'cheerio'; +import type { Element } from 'domhandler'; + +import type { RidershipExtra } from '@/routes/tokyometro/utils'; +import { cellText, parseCount, parseFiscalYear, ridershipItem } from '@/routes/tokyometro/utils'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const LINK = 'https://www.keio.co.jp/company/corporate/corporate_manual/number-of-passengers.html'; +const OPERATOR = '京王電鉄'; + +interface StationRow { + station: string; + values: string[]; // one cell per year column, in header order + note: string; // the following (…乗換) row, e.g. '(井の頭線乗換) (151,951) (150,383)' +} + +/** + * 駅別 一日平均乗降人員 — a single page with two `table.h-b-table` (京王線 / 井の頭線): + * row 1: line name; row 2 (`tr.tb-sub-header`): 駅名 | 2025年度 | 2024年度; then one row per station. + * A row whose 駅名 is parenthesised ((井の頭線乗換) (151,951) …) is the transfer count of the station above it, not a + * station; a blank spacer row and the 全線計 row are skipped. Neither 順位 nor 前年比 is printed. + */ +const parseTable = ($: CheerioAPI, table: Element): { line: string | null; years: number[]; rows: StationRow[] } => { + const trs = $(table).find('tr').toArray(); + const line = cellText($(trs[0]).find('th').text()) || null; + const headerCells = $(trs[1]) + .find('td') + .toArray() + .map((td) => cellText($(td).text())); + const years = headerCells.slice(1).map((h) => parseFiscalYear(h)); + if (headerCells[0] !== '駅名' || years.some((y) => y === null)) { + return { line, years: [], rows: [] }; + } + + const rows: StationRow[] = []; + for (const tr of trs.slice(2)) { + const [station = '', ...values] = $(tr) + .find('td') + .toArray() + .map((td) => cellText($(td).text())); + if (station === '' || station === '全線計') { + continue; + } + const last = rows.at(-1); + if (/^[((]/.test(station) && last) { + rows[rows.length - 1] = { ...last, note: [station, ...values].join(' ').trim() }; + continue; + } + rows.push({ station, values, note: '' }); + } + return { line, years: years as number[], rows }; +}; + +const round1 = (n: number): number => Math.round(n * 10) / 10; + +const parsePage = (html: string): { years: number[]; items: Array> } => { + const $ = load(html); + const tables = $('table.h-b-table') + .toArray() + .map((table) => parseTable($, table)); + const years = [...new Set(tables.flatMap((t) => t.years))].toSorted((a, b) => b - a); + if (years.length === 0) { + throw new Error('keio: no 年度 columns found'); + } + + const items = tables.flatMap(({ line, years: cols, rows }) => + rows.flatMap((row) => + cols.map((fy, i) => { + const value = row.values[i] ?? ''; + const prev = cols.includes(fy - 1) ? (row.values[cols.indexOf(fy - 1)] ?? '') : ''; + const [n, p] = [parseCount(value), parseCount(prev)]; + const raw: RidershipExtra['raw'] = Object.fromEntries([['station', row.station], ...cols.map((y, j) => [`${y}年度`, row.values[j] ?? '']), ...(row.note ? [['note', row.note]] : [])]); + const extra: RidershipExtra = { + source: 'keio', + operator: OPERATOR, + station: row.station, + line, + fiscal_year: fy, + daily_average: n, + unit: '人/日', + rank: null, + // The page prints no 前年比; when it prints the previous year's column, the change is computed from the two cells. + yoy_pct: n !== null && p !== null && p > 0 ? round1((n / p - 1) * 100) : null, + raw, + }; + return ridershipItem(extra, LINK); + }) + ) + ); + return { years, items }; +}; + +export const handler = async (): Promise => { + const page = (await cache.tryGet(LINK, async () => parsePage(await ofetch(LINK)))) as ReturnType; + return { + title: `京王電鉄 駅別 一日平均乗降人員(${page.years.map((y) => `${y}年度`).join('・')})`, + link: LINK, + language: 'ja', + item: page.items, + }; +}; + +export const route: Route = { + path: '/ridership', + name: '駅別 一日平均乗降人員', + url: 'www.keio.co.jp', + maintainers: ['pseudoyu'], + handler, + example: '/keio/ridership', + parameters: {}, + description: `Annual 駅別 一日平均乗降人員 for every Keio station (京王線 incl. 相模原線・高尾線 etc., and 井の頭線), from [駅別 一日平均乗降人員](${LINK}). The page lists the latest fiscal year and the one before it side by side, so there is one item per station and year (two per station); there is no year parameter. \`_extra\` carries \`operator\`, \`station\`, \`line\`, \`fiscal_year\`, \`daily_average\` (人/日) and both cells in \`raw\`. \`rank\` is \`null\` (no ranking is printed); \`yoy_pct\` is computed from the two year columns for the latest year (rounded to 0.1) and \`null\` for the earlier one. 明大前's (乗換) transfer count is kept in \`raw.note\`; the 全線計 row is not a station and is skipped. The operator does not publish a release date, so items have no \`pubDate\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.keio.co.jp/company/corporate/corporate_manual/number-of-passengers.html'], + target: '/ridership', + }, + ], +}; From 723209fd0c79537abf16aaf3749719be07c4dae0 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:24:39 +0900 Subject: [PATCH 0809/1190] =?UTF-8?q?feat(route):=20lg/tokyo/food-permit?= =?UTF-8?q?=20adds=20=E5=8F=B0=E6=9D=B1=E5=8C=BA=20monthly=20=E6=96=B0?= =?UTF-8?q?=E8=A6=8F=E8=A8=B1=E5=8F=AF=20CSV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Share the CSV helpers across lg routes (lib/routes/lg/utils.ts) with UTF-8 / Shift_JIS sniffing, 和暦 date parsing and a common PermitExtra that now carries lat / lon when the publisher provides them. 台東区 reads the two newest 許可日順 monthly files linked from the 食品衛生営業施設一覧 page (CC BY via the 東京都 catalog). Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/tokyo/food-permit.ts | 102 +++++++++++++++++++-------- lib/routes/lg/tokyo/utils.ts | 57 --------------- lib/routes/lg/utils.ts | 108 +++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 86 deletions(-) delete mode 100644 lib/routes/lg/tokyo/utils.ts create mode 100644 lib/routes/lg/utils.ts diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index a53015ed4ea0..e209b7bfdf49 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -1,3 +1,5 @@ +import { type CheerioAPI, load } from 'cheerio'; + import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -5,30 +7,26 @@ import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -import { csvRecords } from './utils'; +import { csvRecords, decodeText, isoDate, type PermitExtra, type Row, warekiMonth } from '../utils'; + +export type { PermitExtra } from '../utils'; /** * 飲食店営業許可 (food business permits) newly granted in Tokyo wards, from each ward's CC BY open data. * - * Only two of the 23 wards publish a machine-readable, regularly updated 台帳: - * shibuya — 渋谷区 ArcGIS FeatureServer (complete ledger incl. 廃業; the current and previous month are queried) - * minato — 港区 自治体標準オープンデータ CSV (snapshot of valid permits, monthly, ~2 MB) - * Rows are the publisher's original columns and are exposed verbatim in `_extra.raw`. + * Six of the 23 wards publish a machine-readable, regularly updated 台帳 (surveyed 2026-09-15): + * shibuya — 渋谷区 ArcGIS FeatureServer (complete ledger incl. 廃業; the current and previous month are queried) + * minato — 港区 自治体標準オープンデータ CSV (snapshot of valid permits, monthly, ~2 MB) + * taito — 台東区 monthly 新規許可 CSV linked from the 食品衛生営業施設一覧 page (CC BY via the 東京都 catalog) + * shinagawa — 品川区 monthly 許可施設一覧 CSV linked from the 食品衛生許可施設一覧 page (CC BY 4.0; individuals' names masked with ※) + * setagaya — 世田谷区 monthly 新規許可施設一覧 CSV linked from the 食品関係施設情報 page (CC BY 4.0) + * meguro — 目黒区 monthly 飲食店 新規 CSV from the BODIK CKAN dataset (CC BY 4.0) + * For the page-linked wards the two newest monthly files are read. Rows are the publisher's original columns, + * exposed verbatim in `_extra.raw`; 中央 / 江東 / 新宿 / 中野 also have CC BY CSVs on the catalog but they are + * snapshots frozen in 2022–2023, so they are not included. */ -type Source = 'shibuya' | 'minato'; -type Row = Record; - -export interface PermitExtra { - source: Source; - ward: string; - permit_no: string; - name: string; - address: string | null; - permit_date: string | null; // YYYY-MM-DD - business_type: string | null; - raw: Row; -} +type Source = 'shibuya' | 'minato' | 'taito'; const SOURCES: Record = { shibuya: { @@ -39,28 +37,33 @@ const SOURCES: Record = { label: '港区', link: 'https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244', }, + taito: { + label: '台東区', + link: 'https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html', + }, }; +const MONTHS_BACK = 2; const SHIBUYA_QUERY = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_food_businesses_list/FeatureServer/0/query'; const SHIBUYA_DATE = '許可開始日もしくは届出受理日'; const MINATO_CSV = 'https://opendata.city.minato.tokyo.jp/dataset/54d8c582-00e2-4730-a23f-4a5befec9ae5/resource/c9d0299e-8e05-4317-877f-83055709e41f/download/food_business_all.csv'; const DEFAULT_LIMIT = 100; const MAX_LIMIT = 500; -const DATE_COLS = ['許可年月日', SHIBUYA_DATE, '許可開始日', '許可日']; +const DATE_COLS = ['許可年月日', '許可決定日', SHIBUYA_DATE, '許可開始日', '許可日']; +/** First non-empty column; `※` / `※※※※※` (品川区・台東区 mask individuals' data this way) counts as empty. */ const pick = (row: Row, cols: readonly string[]): string | null => { for (const c of cols) { const v = row[c]?.trim(); - if (v) { + if (v && !/^[※**]+$/.test(v)) { return v; } } return null; }; -/** `2024-04-08` / `2026/8/7` → `YYYY-MM-DD`; anything else (和暦 …) → null. */ -const isoDate = (raw: string | null): string | null => { - const m = raw === null ? null : /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/.exec(raw.normalize('NFKC')); - return m ? `${m[1]}-${m[2].padStart(2, '0')}-${m[3].padStart(2, '0')}` : null; +const toNumber = (v: string | null): number | null => { + const n = v === null ? NaN : Number(v.trim()); + return Number.isFinite(n) ? n : null; }; /** 渋谷区 stores dates as text (`2026/8/7`), so the current and previous month (JST) are selected with LIKE and sorted locally. */ @@ -88,27 +91,66 @@ const fetchShibuya = async (): Promise => { const fetchMinato = async (): Promise => csvRecords(await ofetch(MINATO_CSV, { responseType: 'text' })); +const fetchCsv = async (url: string): Promise => { + const buf: ArrayBuffer = await ofetch(url, { responseType: 'arrayBuffer' }); + return csvRecords(decodeText(buf)); +}; + +/** The newest `MONTHS_BACK` monthly files of a page-linked ward, in the order given (newest first), concatenated. */ +const fetchMonthly = async (urls: string[]): Promise => (await Promise.all(urls.slice(0, MONTHS_BACK).map((u) => fetchCsv(u)))).flat(); + +/** Link `href`s with a sortable key, newest first. */ +const newestLinks = ($: CheerioAPI, base: string, keyOf: (text: string, href: string) => string | null): string[] => + $('a[href$=".csv"]') + .toArray() + .map((a) => ({ href: new URL($(a).attr('href')!, base).href, key: keyOf($(a).text(), $(a).attr('href')!) })) + .filter((l): l is { href: string; key: string } => l.key !== null) + .toSorted((a, b) => b.key.localeCompare(a.key)) + .map((l) => l.href); + +/** 台東区: the 月別 table lists each month as 業種順 + 許可日順 (same rows); the file names are not regular (`2026-07-PER-CSV.csv`, `OPD08_PER.csv`), so rows are taken in table order. */ +const fetchTaito = async (): Promise => { + const html: string = await ofetch(SOURCES.taito.link, { responseType: 'text' }); + const $ = load(html); + const table = $('table').filter((_, t) => $(t).find('caption').text().includes('月別')); + const urls = table + .find('a[href$=".csv"]') + .toArray() + .filter((a) => $(a).text().includes('許可日順')) + .map((a) => new URL($(a).attr('href')!, SOURCES.taito.link).href) + .toReversed(); + return fetchMonthly(urls); +}; + +const FETCHERS: Record Promise> = { + shibuya: fetchShibuya, + minato: fetchMinato, + taito: fetchTaito, +}; + const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => { const permitNo = pick(raw, ['許可番号']) ?? ''; - const name = pick(raw, ['施設名称', '屋号']) ?? '(名称なし)'; - const address = pick(raw, ['所在地_連結表記', '施設所在地_連結表記']); + const name = pick(raw, ['施設名称', '屋号', '施設屋号', '施設の名称']) ?? '(名称なし)'; + const address = pick(raw, ['所在地_連結表記', '施設所在地_連結表記', '施設所在地', '営業所所在地', '営業施設所在地']); const permitDate = isoDate(pick(raw, DATE_COLS)); const businessType = pick(raw, ['営業の種類', '営業の種類もしくは営業の形態', '業種']); const ward = (pick(raw, ['施設所在地_市区町村', '地方公共団体名']) ?? SOURCES[source].label).replace(/^東京都/, ''); + const lat = toNumber(pick(raw, ['緯度'])); + const lon = toNumber(pick(raw, ['経度'])); return { title: `${name}(${businessType ?? '業種不明'})`, guid: `lg/tokyo/food-permit:${source}:${permitNo}`, link: SOURCES[source].link, pubDate: permitDate === null ? undefined : timezone(parseDate(permitDate, 'YYYY-MM-DD'), 9), description: [ward, address, businessType, permitDate, `許可番号 ${permitNo}`].filter(Boolean).join(' / '), - _extra: { source, ward, permit_no: permitNo, name, address, permit_date: permitDate, business_type: businessType, raw }, + _extra: { source, ward, permit_no: permitNo, name, address, permit_date: permitDate, business_type: businessType, lat, lon, raw }, }; }; /** Newest `limit` 許可 rows of one source; 届出 rows are not an opening signal and are skipped. */ const fetchSource = async (source: Source, limit: number): Promise> => { try { - const rows = source === 'shibuya' ? await fetchShibuya() : await fetchMinato(); + const rows = await FETCHERS[source](); return rows .filter((r) => (r['許可番号'] ?? '') !== '' && (r['許可あるいは届出'] ?? '許可') === '許可') .map((raw) => toItem(source, raw)) @@ -155,6 +197,7 @@ export const route: Route = { options: [ { value: 'shibuya', label: '渋谷区' }, { value: 'minato', label: '港区' }, + { value: 'taito', label: '台東区' }, ], }, }, @@ -162,8 +205,9 @@ export const route: Route = { - 渋谷区: [食品営業許可施設一覧 (ArcGIS FeatureServer)](https://city-shibuya-data.opendata.arcgis.com/items/e68f41ebfa5f4ea490ca9af701d44e02) — current and previous month - 港区: [食品営業許可一覧 (CSV)](https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244) — monthly snapshot of valid permits, newest first +- 台東区: [食品衛生営業施設一覧](https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html) — the two newest monthly 新規許可 CSVs (updated on the 10th) -Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\` and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). +Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\`, \`lat\` / \`lon\` (when the publisher gives them, else \`null\`) and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). | Query | Description | Default | | ------- | ------------------------------------- | ------- | diff --git a/lib/routes/lg/tokyo/utils.ts b/lib/routes/lg/tokyo/utils.ts deleted file mode 100644 index 560dfcce89ff..000000000000 --- a/lib/routes/lg/tokyo/utils.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** Minimal RFC 4180 CSV parser: quoted fields, `""` escapes, newlines inside quotes, CRLF. */ -const parseCsv = (text: string): string[][] => { - const rows: string[][] = []; - let row: string[] = []; - let field = ''; - let quoted = false; - for (let i = 0; i < text.length; i++) { - const c = text[i]; - if (quoted) { - if (c !== '"') { - field += c; - } else if (text[i + 1] === '"') { - field += '"'; - i++; - } else { - quoted = false; - } - continue; - } - switch (c) { - case '"': - quoted = true; - break; - case ',': - row.push(field); - field = ''; - break; - case '\r': - case '\n': - if (c === '\r' && text[i + 1] === '\n') { - i++; - } - row.push(field); - rows.push(row); - row = []; - field = ''; - break; - default: - field += c; - } - } - if (field !== '' || row.length > 0) { - row.push(field); - rows.push(row); - } - return rows; -}; - -/** CSV text → records keyed by the (trimmed) header row; a missing cell is `null`. */ -export const csvRecords = (text: string): Array> => { - const [header, ...rows] = parseCsv(text); - if (!header) { - return []; - } - const keys = header.map((h) => h.trim()); - return rows.filter((r) => r.length > 1).map((r) => Object.fromEntries(keys.map((k, i) => [k, r[i] ?? null]))); -}; diff --git a/lib/routes/lg/utils.ts b/lib/routes/lg/utils.ts new file mode 100644 index 000000000000..3b431b225ea4 --- /dev/null +++ b/lib/routes/lg/utils.ts @@ -0,0 +1,108 @@ +/** Minimal RFC 4180 CSV parser: quoted fields, `""` escapes, newlines inside quotes, CRLF. */ +const parseCsv = (text: string): string[][] => { + const rows: string[][] = []; + let row: string[] = []; + let field = ''; + let quoted = false; + for (let i = 0; i < text.length; i++) { + const c = text[i]; + if (quoted) { + if (c !== '"') { + field += c; + } else if (text[i + 1] === '"') { + field += '"'; + i++; + } else { + quoted = false; + } + continue; + } + switch (c) { + case '"': + quoted = true; + break; + case ',': + row.push(field); + field = ''; + break; + case '\r': + case '\n': + if (c === '\r' && text[i + 1] === '\n') { + i++; + } + row.push(field); + rows.push(row); + row = []; + field = ''; + break; + default: + field += c; + } + } + if (field !== '' || row.length > 0) { + row.push(field); + rows.push(row); + } + return rows; +}; + +/** CSV text → records keyed by the (trimmed) header row; a missing cell is `null`. */ +export const csvRecords = (text: string): Array> => { + const [header, ...rows] = parseCsv(text); + if (!header) { + return []; + } + const keys = header.map((h) => h.trim()); + return rows.filter((r) => r.length > 1).map((r) => Object.fromEntries(keys.map((k, i) => [k, r[i] ?? null]))); +}; + +export type Row = Record; + +/** Normalised permit fields shared by the lg food-permit routes; the publisher's original columns stay verbatim in `raw`. */ +export interface PermitExtra { + source: string; + ward: string; + permit_no: string; + name: string; + address: string | null; + permit_date: string | null; // YYYY-MM-DD + business_type: string | null; + lat: number | null; + lon: number | null; + raw: Row; +} + +const ERA_BASE: Record = { 令和: 2018, 平成: 1988, 昭和: 1925 }; + +/** `2024-04-08` / `2026/8/7` / `令和8年8月7日` → `YYYY-MM-DD`; anything else → null. */ +export const isoDate = (raw: string | null): string | null => { + if (raw === null) { + return null; + } + const s = raw.normalize('NFKC').trim(); + const m = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/.exec(s); + if (m) { + return `${m[1]}-${m[2].padStart(2, '0')}-${m[3].padStart(2, '0')}`; + } + const w = /^(令和|平成|昭和)(元|\d{1,2})年(\d{1,2})月(\d{1,2})日/.exec(s); + if (!w) { + return null; + } + const year = ERA_BASE[w[1]] + (w[2] === '元' ? 1 : Number(w[2])); + return `${year}-${w[3].padStart(2, '0')}-${w[4].padStart(2, '0')}`; +}; + +/** Ward sites serve CSV as UTF-8 (with BOM) one month and Shift_JIS the next, so sniff: strict UTF-8 first, else Shift_JIS. */ +export const decodeText = (buf: ArrayBuffer): string => { + try { + return new TextDecoder('utf-8', { fatal: true }).decode(buf); + } catch { + return new TextDecoder('shift_jis').decode(buf); + } +}; + +/** Sort key for `令和8年7月分`-style labels (full-width digits allowed): `2026-07`; null when absent. */ +export const warekiMonth = (label: string): string | null => { + const m = /(令和|平成)(元|\d{1,2})年(\d{1,2})月/.exec(label.normalize('NFKC')); + return m ? `${ERA_BASE[m[1]] + (m[2] === '元' ? 1 : Number(m[2]))}-${m[3].padStart(2, '0')}` : null; +}; From 34e1b47f90cc758d81ab01b7f4b4e2c683c38cad Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:24:43 +0900 Subject: [PATCH 0810/1190] =?UTF-8?q?feat(route):=20lg/tokyo/food-permit?= =?UTF-8?q?=20adds=20=E5=93=81=E5=B7=9D=E5=8C=BA=20monthly=20=E8=A8=B1?= =?UTF-8?q?=E5=8F=AF=E6=96=BD=E8=A8=AD=E4=B8=80=E8=A6=A7=20CSV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two newest 令和N年M月分 files from the 食品衛生許可施設一覧 page (CC BY 4.0); the publisher masks individuals' 屋号 / 所在地 with ※, which become null. Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/tokyo/food-permit.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index e209b7bfdf49..53b4d5a27f69 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -26,7 +26,7 @@ export type { PermitExtra } from '../utils'; * snapshots frozen in 2022–2023, so they are not included. */ -type Source = 'shibuya' | 'minato' | 'taito'; +type Source = 'shibuya' | 'minato' | 'taito' | 'shinagawa'; const SOURCES: Record = { shibuya: { @@ -41,6 +41,10 @@ const SOURCES: Record = { label: '台東区', link: 'https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html', }, + shinagawa: { + label: '品川区', + link: 'https://www.city.shinagawa.tokyo.jp/PC/kenkou/kenkou-eisei/kenkou-eisei-syokuhin/opendate.html', + }, }; const MONTHS_BACK = 2; const SHIBUYA_QUERY = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_food_businesses_list/FeatureServer/0/query'; @@ -122,10 +126,18 @@ const fetchTaito = async (): Promise => { return fetchMonthly(urls); }; +/** 品川区: `令和8年7月分許可施設一覧` links (the 令和6〜7年度 yearly file is skipped). */ +const fetchShinagawa = async (): Promise => { + const html: string = await ofetch(SOURCES.shinagawa.link, { responseType: 'text' }); + const $ = load(html); + return fetchMonthly(newestLinks($, SOURCES.shinagawa.link, (text) => (text.includes('月分') ? warekiMonth(text) : null))); +}; + const FETCHERS: Record Promise> = { shibuya: fetchShibuya, minato: fetchMinato, taito: fetchTaito, + shinagawa: fetchShinagawa, }; const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => { @@ -198,6 +210,7 @@ export const route: Route = { { value: 'shibuya', label: '渋谷区' }, { value: 'minato', label: '港区' }, { value: 'taito', label: '台東区' }, + { value: 'shinagawa', label: '品川区' }, ], }, }, @@ -206,6 +219,7 @@ export const route: Route = { - 渋谷区: [食品営業許可施設一覧 (ArcGIS FeatureServer)](https://city-shibuya-data.opendata.arcgis.com/items/e68f41ebfa5f4ea490ca9af701d44e02) — current and previous month - 港区: [食品営業許可一覧 (CSV)](https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244) — monthly snapshot of valid permits, newest first - 台東区: [食品衛生営業施設一覧](https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html) — the two newest monthly 新規許可 CSVs (updated on the 10th) +- 品川区: [食品衛生許可施設一覧](https://www.city.shinagawa.tokyo.jp/PC/kenkou/kenkou-eisei/kenkou-eisei-syokuhin/opendate.html) — the two newest monthly CSVs (updated on the 15th); individuals' names and addresses are masked by the publisher and come through as \`null\` Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\`, \`lat\` / \`lon\` (when the publisher gives them, else \`null\`) and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). From 962eac3d37c52e8b1dc406dd74548067d99b91e7 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:24:47 +0900 Subject: [PATCH 0811/1190] =?UTF-8?q?feat(route):=20lg/tokyo/food-permit?= =?UTF-8?q?=20adds=20=E4=B8=96=E7=94=B0=E8=B0=B7=E5=8C=BA=20=E4=BE=8B?= =?UTF-8?q?=E6=9C=88=E6=96=B0=E8=A6=8F=E8=A8=B1=E5=8F=AF=E6=96=BD=E8=A8=AD?= =?UTF-8?q?=E4=B8=80=E8=A6=A7=20CSV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two newest (RYYMMDD) files from the 食品関係施設情報の公開について page (CC BY 4.0). Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/tokyo/food-permit.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index 53b4d5a27f69..8dac8204e9fa 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -26,7 +26,7 @@ export type { PermitExtra } from '../utils'; * snapshots frozen in 2022–2023, so they are not included. */ -type Source = 'shibuya' | 'minato' | 'taito' | 'shinagawa'; +type Source = 'shibuya' | 'minato' | 'taito' | 'shinagawa' | 'setagaya'; const SOURCES: Record = { shibuya: { @@ -45,6 +45,10 @@ const SOURCES: Record = { label: '品川区', link: 'https://www.city.shinagawa.tokyo.jp/PC/kenkou/kenkou-eisei/kenkou-eisei-syokuhin/opendate.html', }, + setagaya: { + label: '世田谷区', + link: 'https://www.city.setagaya.lg.jp/02245/online_tetsuzuki/3246.html', + }, }; const MONTHS_BACK = 2; const SHIBUYA_QUERY = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_food_businesses_list/FeatureServer/0/query'; @@ -133,11 +137,19 @@ const fetchShinagawa = async (): Promise => { return fetchMonthly(newestLinks($, SOURCES.shinagawa.link, (text) => (text.includes('月分') ? warekiMonth(text) : null))); }; +/** 世田谷区: `例月新規許可施設一覧(R080831)` links, keyed by the R+YYMMDD code (the yearly 全件 file has none). */ +const fetchSetagaya = async (): Promise => { + const html: string = await ofetch(SOURCES.setagaya.link, { responseType: 'text' }); + const $ = load(html); + return fetchMonthly(newestLinks($, SOURCES.setagaya.link, (text) => /例月新規許可施設一覧\(R(\d{6})\)/.exec(text)?.[1] ?? null)); +}; + const FETCHERS: Record Promise> = { shibuya: fetchShibuya, minato: fetchMinato, taito: fetchTaito, shinagawa: fetchShinagawa, + setagaya: fetchSetagaya, }; const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => { @@ -211,6 +223,7 @@ export const route: Route = { { value: 'minato', label: '港区' }, { value: 'taito', label: '台東区' }, { value: 'shinagawa', label: '品川区' }, + { value: 'setagaya', label: '世田谷区' }, ], }, }, @@ -220,6 +233,7 @@ export const route: Route = { - 港区: [食品営業許可一覧 (CSV)](https://catalog.data.metro.tokyo.lg.jp/dataset/t131032d0000000244) — monthly snapshot of valid permits, newest first - 台東区: [食品衛生営業施設一覧](https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html) — the two newest monthly 新規許可 CSVs (updated on the 10th) - 品川区: [食品衛生許可施設一覧](https://www.city.shinagawa.tokyo.jp/PC/kenkou/kenkou-eisei/kenkou-eisei-syokuhin/opendate.html) — the two newest monthly CSVs (updated on the 15th); individuals' names and addresses are masked by the publisher and come through as \`null\` +- 世田谷区: [食品関係施設情報の公開について](https://www.city.setagaya.lg.jp/02245/online_tetsuzuki/3246.html) — the two newest 例月新規許可施設一覧 CSVs (updated on the 15th) Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\`, \`lat\` / \`lon\` (when the publisher gives them, else \`null\`) and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). From 75c58624f654c89462fc3ff03d71ded50bfbcf8c Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:24:51 +0900 Subject: [PATCH 0812/1190] =?UTF-8?q?feat(route):=20lg/tokyo/food-permit?= =?UTF-8?q?=20adds=20=E7=9B=AE=E9=BB=92=E5=8C=BA=20=E9=A3=B2=E9=A3=9F?= =?UTF-8?q?=E5=BA=97=20=E6=96=B0=E8=A6=8F=20monthly=20CSV?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two newest 飲食店 新規 resources of the BODIK CKAN dataset 131105_food_business (CC BY 4.0), found via package_show. Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/tokyo/food-permit.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index 8dac8204e9fa..c8717e54af5d 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -26,7 +26,7 @@ export type { PermitExtra } from '../utils'; * snapshots frozen in 2022–2023, so they are not included. */ -type Source = 'shibuya' | 'minato' | 'taito' | 'shinagawa' | 'setagaya'; +type Source = 'shibuya' | 'minato' | 'taito' | 'shinagawa' | 'setagaya' | 'meguro'; const SOURCES: Record = { shibuya: { @@ -49,7 +49,12 @@ const SOURCES: Record = { label: '世田谷区', link: 'https://www.city.setagaya.lg.jp/02245/online_tetsuzuki/3246.html', }, + meguro: { + label: '目黒区', + link: 'https://data.bodik.jp/dataset/131105_food_business', + }, }; +const MEGURO_API = 'https://data.bodik.jp/api/3/action/package_show?id=131105_food_business'; const MONTHS_BACK = 2; const SHIBUYA_QUERY = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_food_businesses_list/FeatureServer/0/query'; const SHIBUYA_DATE = '許可開始日もしくは届出受理日'; @@ -144,12 +149,26 @@ const fetchSetagaya = async (): Promise => { return fetchMonthly(newestLinks($, SOURCES.setagaya.link, (text) => /例月新規許可施設一覧\(R(\d{6})\)/.exec(text)?.[1] ?? null)); }; +/** 目黒区: CKAN resources named `飲食店 新規 令和7年10月分` (or `新規飲食店施設一覧 …`); 更新 / 届出 / 廃業 files are skipped. */ +const fetchMeguro = async (): Promise => { + const body = await ofetch(MEGURO_API); + const resources: Array<{ name: string; url: string; format: string }> = body?.result?.resources ?? []; + const urls = resources + .filter((r) => r.format.toUpperCase() === 'CSV' && r.name.includes('飲食店') && r.name.includes('新規')) + .map((r) => ({ url: r.url, key: warekiMonth(r.name) })) + .filter((r): r is { url: string; key: string } => r.key !== null) + .toSorted((a, b) => b.key.localeCompare(a.key)) + .map((r) => r.url); + return fetchMonthly(urls); +}; + const FETCHERS: Record Promise> = { shibuya: fetchShibuya, minato: fetchMinato, taito: fetchTaito, shinagawa: fetchShinagawa, setagaya: fetchSetagaya, + meguro: fetchMeguro, }; const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => { @@ -224,6 +243,7 @@ export const route: Route = { { value: 'taito', label: '台東区' }, { value: 'shinagawa', label: '品川区' }, { value: 'setagaya', label: '世田谷区' }, + { value: 'meguro', label: '目黒区' }, ], }, }, @@ -234,6 +254,7 @@ export const route: Route = { - 台東区: [食品衛生営業施設一覧](https://www.city.taito.lg.jp/kenkohukusi/kenkokikikanrieisei/food/syokuhin-sisetu/index.html) — the two newest monthly 新規許可 CSVs (updated on the 10th) - 品川区: [食品衛生許可施設一覧](https://www.city.shinagawa.tokyo.jp/PC/kenkou/kenkou-eisei/kenkou-eisei-syokuhin/opendate.html) — the two newest monthly CSVs (updated on the 15th); individuals' names and addresses are masked by the publisher and come through as \`null\` - 世田谷区: [食品関係施設情報の公開について](https://www.city.setagaya.lg.jp/02245/online_tetsuzuki/3246.html) — the two newest 例月新規許可施設一覧 CSVs (updated on the 15th) +- 目黒区: [飲食店等 (BODIK CKAN)](https://data.bodik.jp/dataset/131105_food_business) — the two newest 飲食店 新規 monthly CSVs (updated by the 10th) Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\`, \`lat\` / \`lon\` (when the publisher gives them, else \`null\`) and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). From 5262341a3ac79e160ae8ac5f8c47981043ca0a5b Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 01:24:51 +0900 Subject: [PATCH 0813/1190] feat(route): add lg/osaka/food-permit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 大阪市 食品営業許可施設一覧 CSV (CC BY 4.0, quarterly snapshot with 緯度経度) scraped from the city page because the file name carries its extraction date. No permit date exists in the data, so items carry no pubDate and are ordered by 指令番号 (年度-連番); 更新 rows are skipped. The 経度 / 緯度 columns are swapped in the current release and are ordered by value. Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/osaka/food-permit.ts | 117 +++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 lib/routes/lg/osaka/food-permit.ts diff --git a/lib/routes/lg/osaka/food-permit.ts b/lib/routes/lg/osaka/food-permit.ts new file mode 100644 index 000000000000..999e06cc8050 --- /dev/null +++ b/lib/routes/lg/osaka/food-permit.ts @@ -0,0 +1,117 @@ +import { load } from 'cheerio'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +import { csvRecords, decodeText, type PermitExtra, type Row } from '../utils'; + +/** + * 食品営業許可 facilities of 大阪市, from the CC BY 4.0 CSV linked on the city's 食品営業許可施設一覧 page + * (https://www.city.osaka.lg.jp/kenko/page/0000575579.html). The file is a quarterly snapshot of every valid permit + * (~64k rows, ~10 MB, Shift_JIS) with 屋号 / 業種分類 / 営業所所在地 / 緯度経度 / 指令番号 / 申請区分 / 許可満了日 — there is + * no permit date, so items carry no `pubDate` and are ordered by 指令番号 (`大 保食第<年度>-<連番>号`, newest first). + * The file name carries its extraction date (`260630zenku.csv`), so the page is scraped for the current link. + */ + +const PAGE = 'https://www.city.osaka.lg.jp/kenko/page/0000575579.html'; +const DEFAULT_LIMIT = 100; +const MAX_LIMIT = 500; + +/** `大 保食第24-8092号` → `[24, 8092]`; anything else → null. */ +const permitKey = (no: string): [number, number] | null => { + const m = /第(\d+)[--](\d+)号?$/.exec(no.normalize('NFKC').replaceAll(/\s/g, '')); + return m ? [Number(m[1]), Number(m[2])] : null; +}; + +const toNumber = (v: string | null | undefined): number | null => { + const n = v ? Number(v.trim()) : NaN; + return Number.isFinite(n) ? n : null; +}; + +/** The publisher's 経度 / 緯度 columns have been swapped in past releases, so the pair is ordered by value (in Japan lon > lat). */ +const coords = (raw: Row): { lat: number | null; lon: number | null } => { + const a = toNumber(raw['緯度']); + const b = toNumber(raw['経度']); + if (a === null || b === null) { + return { lat: null, lon: null }; + } + return a > b ? { lat: b, lon: a } : { lat: a, lon: b }; +}; + +const fetchRows = async (): Promise => { + const html: string = await ofetch(PAGE, { responseType: 'text' }); + const $ = load(html); + const href = $('a[href$=".csv"]') + .toArray() + .map((a) => $(a).attr('href')!) + .find((h) => h.includes('city.osaka.lg.jp')); + if (!href) { + throw new Error('lg/osaka/food-permit: no CSV link on the 食品営業許可施設一覧 page'); + } + const buf: ArrayBuffer = await ofetch(new URL(href, PAGE).href, { responseType: 'arrayBuffer' }); + return csvRecords(decodeText(buf)); +}; + +const toItem = (raw: Row): DataItem & { _extra: PermitExtra } => { + const permitNo = raw['指令番号']!.trim(); + const name = raw['屋号']?.trim() || '(名称なし)'; + const address = raw['営業所所在地']?.trim() || null; + const businessType = raw['業種分類']?.trim() || null; + const ward = /^(\S+?区)/.exec(address ?? '')?.[1] ?? '大阪市'; + const { lat, lon } = coords(raw); + return { + title: `${name}(${businessType ?? '業種不明'})`, + guid: `lg/osaka/food-permit:${permitNo}`, + link: PAGE, + description: [ward, address, businessType, `指令番号 ${permitNo}`, raw['許可満了日'] ? `許可満了日 ${raw['許可満了日']}` : null].filter(Boolean).join(' / '), + _extra: { source: 'osaka', ward, permit_no: permitNo, name, address, permit_date: null, business_type: businessType, lat, lon, raw }, + }; +}; + +/** Newest `limit` 新規 permits (更新 rows are renewals, not openings), by 指令番号. */ +const fetchItems = async (limit: number): Promise> => { + const rows = await fetchRows(); + return rows + .filter((r) => (r['指令番号'] ?? '').trim() !== '' && (r['申請区分'] ?? '新規') === '新規') + .map((raw) => ({ raw, key: permitKey(raw['指令番号']!) })) + .filter((r): r is { raw: Row; key: [number, number] } => r.key !== null) + .toSorted((a, b) => b.key[0] - a.key[0] || b.key[1] - a.key[1]) + .slice(0, limit) + .map((r) => toItem(r.raw)); +}; + +export const handler = async (ctx): Promise => { + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, MAX_LIMIT); + const items = (await cache.tryGet(`lg/osaka/food-permit:${limit}`, () => fetchItems(limit))) as Array; + return { + title: '大阪市 食品営業許可 新規', + link: PAGE, + language: 'ja', + item: items, + allowEmpty: true, + }; +}; + +export const route: Route = { + path: '/osaka/food-permit', + name: '大阪市 食品営業許可 新規', + url: 'www.city.osaka.lg.jp', + maintainers: ['pseudoyu'], + handler, + example: '/lg/osaka/food-permit', + description: `Newest food business permits (食品営業許可) in 大阪市, from the CC BY 4.0 [食品営業許可施設一覧 CSV](https://www.city.osaka.lg.jp/kenko/page/0000575579.html) — a quarterly snapshot of all valid permits with 緯度経度. + +The dataset has no permit date, only 許可満了日, so items have no \`pubDate\`; they are ordered by 指令番号 (\`大 保食第<年度>-<連番>号\`), newest first, and only 申請区分 = 新規 rows are included. \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\` (always \`null\`), \`business_type\`, \`lat\`, \`lon\` and the publisher's original columns in \`raw\`. + +| Query | Description | Default | +| ------- | --------------------------- | ------- | +| \`limit\` | Number of permits, max 500 | 100 |`, + categories: ['government'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: false, + }, +}; From 92f21c3649383923c3cb06b2851ab15ee0dfc755 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 16:35:01 +0000 Subject: [PATCH 0814/1190] style: auto format --- lib/routes/lg/osaka/food-permit.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/routes/lg/osaka/food-permit.ts b/lib/routes/lg/osaka/food-permit.ts index 999e06cc8050..23f69eadc68d 100644 --- a/lib/routes/lg/osaka/food-permit.ts +++ b/lib/routes/lg/osaka/food-permit.ts @@ -100,12 +100,12 @@ export const route: Route = { maintainers: ['pseudoyu'], handler, example: '/lg/osaka/food-permit', - description: `Newest food business permits (食品営業許可) in 大阪市, from the CC BY 4.0 [食品営業許可施設一覧 CSV](https://www.city.osaka.lg.jp/kenko/page/0000575579.html) — a quarterly snapshot of all valid permits with 緯度経度. + description: `Newest food business permits (食品営業許可) in 大阪市,from the CC BY 4.0 [食品営業許可施設一覧 CSV](https://www.city.osaka.lg.jp/kenko/page/0000575579.html) — a quarterly snapshot of all valid permits with 緯度経度. -The dataset has no permit date, only 許可満了日, so items have no \`pubDate\`; they are ordered by 指令番号 (\`大 保食第<年度>-<連番>号\`), newest first, and only 申請区分 = 新規 rows are included. \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\` (always \`null\`), \`business_type\`, \`lat\`, \`lon\` and the publisher's original columns in \`raw\`. +The dataset has no permit date, only 許可満了日,so items have no \`pubDate\`; they are ordered by 指令番号 (\`大 保食第<年度>-<連番>号\`), newest first, and only 申請区分 = 新規 rows are included. \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\` (always \`null\`), \`business_type\`, \`lat\`, \`lon\` and the publisher's original columns in \`raw\`. -| Query | Description | Default | -| ------- | --------------------------- | ------- | +| Query | Description | Default | +| ------- | -------------------------- | ------- | | \`limit\` | Number of permits, max 500 | 100 |`, categories: ['government'], features: { From 10ed0dbf6816e5686c0179e24faa14a46c86dd8c Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 02:27:08 +0900 Subject: [PATCH 0815/1190] =?UTF-8?q?fix(route):=20dedupe=20lg/tokyo/food-?= =?UTF-8?q?permit=20by=20=E8=A8=B1=E5=8F=AF=E7=95=AA=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 世田谷区's page lists every monthly CSV link twice (once plainly, once with target="_self"), so newestLinks returned the newest file twice and slice(0, 2) fetched it twice while never reading the previous month: the feed of 100 items carried only 50 distinct 許可番号. - newestLinks: make hrefs unique, so the two newest *distinct* files are read (setagaya now yields 122 Aug + 99 Jul rows, all distinct) - fetchSource: keep the first item per 許可番号 after the date-desc sort (i.e. the latest permit date), before slicing, for every ward Co-Authored-By: Claude Fable 5.1 --- lib/routes/lg/tokyo/food-permit.ts | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index c8717e54af5d..498fd5f6822d 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -112,14 +112,16 @@ const fetchCsv = async (url: string): Promise => { /** The newest `MONTHS_BACK` monthly files of a page-linked ward, in the order given (newest first), concatenated. */ const fetchMonthly = async (urls: string[]): Promise => (await Promise.all(urls.slice(0, MONTHS_BACK).map((u) => fetchCsv(u)))).flat(); -/** Link `href`s with a sortable key, newest first. */ -const newestLinks = ($: CheerioAPI, base: string, keyOf: (text: string, href: string) => string | null): string[] => - $('a[href$=".csv"]') +/** Link `href`s with a sortable key, newest first. A ward page may list the same file twice (世田谷区 renders every link once plainly and once with `target="_self"`), so hrefs are made unique. */ +const newestLinks = ($: CheerioAPI, base: string, keyOf: (text: string, href: string) => string | null): string[] => { + const links = $('a[href$=".csv"]') .toArray() .map((a) => ({ href: new URL($(a).attr('href')!, base).href, key: keyOf($(a).text(), $(a).attr('href')!) })) .filter((l): l is { href: string; key: string } => l.key !== null) .toSorted((a, b) => b.key.localeCompare(a.key)) .map((l) => l.href); + return [...new Set(links)]; +}; /** 台東区: the 月別 table lists each month as 業種順 + 許可日順 (same rows); the file names are not regular (`2026-07-PER-CSV.csv`, `OPD08_PER.csv`), so rows are taken in table order. */ const fetchTaito = async (): Promise => { @@ -190,16 +192,28 @@ const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => }; }; -/** Newest `limit` 許可 rows of one source; 届出 rows are not an opening signal and are skipped. */ +/** First item per 許可番号, order kept; called after the date-desc sort, so the surviving row is the one with the latest permit date. */ +const uniqueByPermitNo = (items: T[]): T[] => { + const seen = new Set(); + return items.filter((it) => { + if (seen.has(it._extra.permit_no)) { + return false; + } + seen.add(it._extra.permit_no); + return true; + }); +}; + +/** Newest `limit` 許可 rows of one source; 届出 rows are not an opening signal and are skipped. Publishers may repeat a permit (a file linked twice, or a 変更 row re-listed), so 許可番号 is deduplicated. */ const fetchSource = async (source: Source, limit: number): Promise> => { try { const rows = await FETCHERS[source](); - return rows + const items = rows .filter((r) => (r['許可番号'] ?? '') !== '' && (r['許可あるいは届出'] ?? '許可') === '許可') .map((raw) => toItem(source, raw)) .filter((it) => it._extra.permit_date !== null) - .toSorted((a, b) => b._extra.permit_date!.localeCompare(a._extra.permit_date!)) - .slice(0, limit); + .toSorted((a, b) => b._extra.permit_date!.localeCompare(a._extra.permit_date!)); + return uniqueByPermitNo(items).slice(0, limit); } catch (error) { // One failing publisher must not take the whole feed down. logger.warn(`lg/tokyo/food-permit: ${source} failed: ${String(error)}`); From e65d29f02f09c70afb81a0fbb43814a23f88f216 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Tue, 15 Sep 2026 16:59:44 +0800 Subject: [PATCH 0816/1190] docs: add RapidProxy sponsor to README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 56ed1459fa91..ac3ef5a52689 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,14 @@ RSSHub pairs especially well with Folo, an AI RSS RSSHub keeps me informed, and StarDesk lets me fix routes or feeds remotely, even run terminal commands from my phone. It’s fast, convenient, and free. + + +RapidProxy +
    +RapidProxy offers reliable residential proxies for scraping and global data access, with 90M+ IPs across 200+ countries. Traffic never expires, starting at just $0.55/GB. try free now + + + ## Introduction From 1339a5bb8a8af6b4202eab4fd6eba880c176ac91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 18:22:34 +0800 Subject: [PATCH 0817/1190] chore(deps): bump devenv from `fce7cf3` to `6e32383` in the nix group (#23299) Bumps the nix group with 1 update: [devenv](https://github.com/cachix/devenv). Updates `devenv` from `fce7cf3` to `6e32383` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/fce7cf37d531acca06c68d0b0ee5c6579cedf200...6e323832a55097670dbd8eed04b1e63efc841dfa) --- updated-dependencies: - dependency-name: devenv dependency-version: 6e323832a55097670dbd8eed04b1e63efc841dfa dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 9416c82d295c..2bc57d186eef 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789034488, - "narHash": "sha256-1q5wOw1YfOFGBQ6mh8bDqRJlYgHAKSW7eIsNhU97a7o=", + "lastModified": 1789152246, + "narHash": "sha256-8ESHEGygXS5lmDepm5cMbmVd8DjcwgwzsmjHc9ffY1c=", "owner": "cachix", "repo": "devenv", - "rev": "fce7cf37d531acca06c68d0b0ee5c6579cedf200", + "rev": "6e323832a55097670dbd8eed04b1e63efc841dfa", "type": "github" }, "original": { From a4f632dc38a15cca09679db5e334cc394c18f845 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 18:07:24 +0900 Subject: [PATCH 0818/1190] feat(route): add jreast and toei ridership, jfnet industry report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /jreast/ridership/:year? — JR東日本 各駅の乗車人員 (ベスト100 + 101位以下 pages, boarding only). The site's ご利用にあたって reserves all reproduction; the clause is quoted in the route description. - /toei/ridership — 都営地下鉄 各駅乗降人員 from the Tokyo open-data catalog CSVs (CC BY 4.0), one item per station and line. - /jfnet/industry-report — 外食産業市場動向調査 monthly releases with PDF/Excel links; figures are not extracted (files only, terms). The shared ridership _extra gains a required `measure` field ('boarding' | 'boarding_alighting'); tokyometro, tokyu, odakyu and keio set 'boarding_alighting'. --- lib/routes/jfnet/industry-report.ts | 107 +++++++++++++++++++ lib/routes/jfnet/namespace.ts | 12 +++ lib/routes/jreast/namespace.ts | 12 +++ lib/routes/jreast/ridership.ts | 160 ++++++++++++++++++++++++++++ lib/routes/keio/ridership.ts | 1 + lib/routes/odakyu/ridership.ts | 1 + lib/routes/toei/namespace.ts | 12 +++ lib/routes/toei/ridership.ts | 112 +++++++++++++++++++ lib/routes/tokyometro/ridership.ts | 1 + lib/routes/tokyometro/utils.ts | 8 +- lib/routes/tokyu/ridership.ts | 1 + 11 files changed, 425 insertions(+), 2 deletions(-) create mode 100644 lib/routes/jfnet/industry-report.ts create mode 100644 lib/routes/jfnet/namespace.ts create mode 100644 lib/routes/jreast/namespace.ts create mode 100644 lib/routes/jreast/ridership.ts create mode 100644 lib/routes/toei/namespace.ts create mode 100644 lib/routes/toei/ridership.ts diff --git a/lib/routes/jfnet/industry-report.ts b/lib/routes/jfnet/industry-report.ts new file mode 100644 index 000000000000..3322c5576187 --- /dev/null +++ b/lib/routes/jfnet/industry-report.ts @@ -0,0 +1,107 @@ +import { load } from 'cheerio'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const HOST = 'https://www.jfnet.or.jp'; +const INDEX = `${HOST}/industry_report/?c=29&y=0`; // c=29 → 月次レポート only +const MEDIA_API = `${HOST}/wp-json/wp/v2/media?search=getujidata&per_page=100&_fields=date,source_url`; + +export interface MarketReportExtra { + source: 'jfnet'; + survey: '外食産業市場動向調査'; + month: string; // 'YYYY-MM' (survey month, 〜月度) + pdf: string; + xls: string | null; + released_at: string | null; // upload date of the PDF, ISO 8601 (local time as returned by WordPress) +} + +/** + * Index rows (`div.flex.items-center.border-b`): [title, period '2026年7月', PDF link, Excel link]. + * The monthly figures exist only inside the PDF / XLS (no HTML table), so the item carries the files. + */ +const parseIndex = (html: string): Array & { title: string }> => { + const $ = load(html); + return $('div.flex.items-center.border-b') + .toArray() + .map((row) => { + const cols = $(row).children('div'); + const title = cols.eq(0).text().trim(); + const period = cols + .eq(1) + .text() + .trim() + .match(/(\d{4})年(\d{1,2})月/); + const pdf = $(row).find('a[href$=".pdf"]').attr('href'); + const xls = $(row).find('a[href$=".xls"], a[href$=".xlsx"]').attr('href'); + if (!title || !period || !pdf) { + return null; + } + return { + title, + source: 'jfnet' as const, + survey: '外食産業市場動向調査' as const, + month: `${period[1]}-${period[2].padStart(2, '0')}`, + pdf: new URL(pdf, HOST).href, + xls: xls ? new URL(xls, HOST).href : null, + }; + }) + .filter((r): r is NonNullable => r !== null); +}; + +export const handler = async (): Promise => { + const [entries, media] = await Promise.all([ + cache.tryGet(INDEX, async () => parseIndex(await ofetch(INDEX))) as Promise>, + cache.tryGet(MEDIA_API, async () => { + const list: Array<{ date: string; source_url: string }> = await ofetch(MEDIA_API); + return Object.fromEntries(list.map((m) => [m.source_url, m.date])); + }) as Promise>, + ]); + + const items: DataItem[] = entries.map((e) => { + const releasedAt = media[e.pdf] ?? null; + const [year, month] = e.month.split('-', 2); + const label = `${year}年${Number(month)}月度`; + return { + title: `${e.survey} ${label}`, + link: e.pdf, + guid: e.pdf, + pubDate: releasedAt === null ? undefined : parseDate(releasedAt), + description: `${e.survey}(月次レポート) ${label} — PDF${e.xls ? ` / Excel` : ''}`, + _extra: { ...e, title: undefined, released_at: releasedAt } as unknown as MarketReportExtra, + }; + }); + + return { + title: '日本フードサービス協会 外食産業市場動向調査(月次)', + link: INDEX, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/industry-report', + name: '外食産業市場動向調査(月次)', + url: 'www.jfnet.or.jp', + maintainers: ['pseudoyu'], + handler, + example: '/jfnet/industry-report', + parameters: {}, + description: `Monthly releases of the 外食産業市場動向調査 from [業界データ](${HOST}/industry_report/). One item per survey month linking the PDF and Excel files; \`_extra\` carries \`month\` (YYYY-MM), \`pdf\`, \`xls\` and \`released_at\` (the file's upload date from the site's WordPress media API). The segment figures (売上高・客数・客単価 前年同月比) are only published inside the files and are not extracted: the site's 利用規約 reserves reproduction of its 資料 to prior permission.`, + categories: ['finance'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.jfnet.or.jp/industry_report'], + target: '/industry-report', + }, + ], +}; diff --git a/lib/routes/jfnet/namespace.ts b/lib/routes/jfnet/namespace.ts new file mode 100644 index 000000000000..c905ac316717 --- /dev/null +++ b/lib/routes/jfnet/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Japan Foodservice Association', + url: 'www.jfnet.or.jp', + description: '一般社団法人日本フードサービス協会', + lang: 'ja', + + ja: { + name: '日本フードサービス協会', + }, +}; diff --git a/lib/routes/jreast/namespace.ts b/lib/routes/jreast/namespace.ts new file mode 100644 index 000000000000..a643c466e88c --- /dev/null +++ b/lib/routes/jreast/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'JR East', + url: 'www.jreast.co.jp', + description: '東日本旅客鉄道', + lang: 'ja', + + ja: { + name: 'JR東日本', + }, +}; diff --git a/lib/routes/jreast/ridership.ts b/lib/routes/jreast/ridership.ts new file mode 100644 index 000000000000..4f8f1cdf5fd7 --- /dev/null +++ b/lib/routes/jreast/ridership.ts @@ -0,0 +1,160 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { RidershipExtra } from '@/routes/tokyometro/utils'; +import { cellText, parseCount, parseFiscalYear, parsePct, ridershipItem } from '@/routes/tokyometro/utils'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.jreast.co.jp'; +const LATEST = `${HOST}/company/data/passenger/`; +const OPERATOR = 'JR東日本'; +const MAX_SUBPAGES = 9; + +/** The site's WAF rejects requests that do not look like a browser navigation. */ +const BROWSER_HEADERS = { + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'accept-language': 'ja,en;q=0.8', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'none', + 'sec-fetch-user': '?1', +}; + +const fetchPage = (url: string): Promise => ofetch(url, { headers: BROWSER_HEADERS, responseType: 'text' }); + +/** null on 404 (the 101位以下 sequence and older-year URLs end that way). */ +const fetchOptional = async (url: string): Promise => { + try { + return await fetchPage(url); + } catch { + return null; + } +}; + +/** + * `table.passengerTable` rows. ベスト100 page: 順位 | 駅名 | 定期外 | 定期 | 合計 | 前年度比; 101位以下 pages + * (`{year}_01.html` …): 駅名 | 定期外 | 定期 | 合計 with no rank, so the rank is computed from the position. + * 前年度比 is a ratio (102.5) from FY2023 on and a % change (△ 38.5) before that. + */ +const parseStations = (html: string, fiscalYear: number, firstRank: number): Array> => { + const $ = load(html); + const rows = $('table.passengerTable tr') + .toArray() + .map((tr) => ({ + cells: $(tr) + .find('td') + .toArray() + .map((td) => cellText($(td).text())), + link: $(tr).find('td.stationName a').attr('href'), + })) + .filter((r) => r.cells.length >= 4); + return rows.map((r, index) => { + const ranked = r.cells.length >= 6; + const [rankText, station, nonCommuter, commuter, total, yoyText] = ranked ? r.cells : ['', ...r.cells, '']; + const yoy = parsePct(yoyText); + const raw: RidershipExtra['raw'] = { rank: rankText, station, non_commuter: nonCommuter, commuter, total, yoy: yoyText }; + const extra: RidershipExtra = { + source: 'jreast', + operator: OPERATOR, + station, + line: null, + fiscal_year: fiscalYear, + daily_average: parseCount(total), + unit: '人/日', + measure: 'boarding', + rank: ranked ? parseCount(rankText) : firstRank + index, + // A ratio above 50 (e.g. 102.5) means "% of the previous year"; anything else is already a % change. + yoy_pct: yoy === null ? null : yoy > 50 ? Math.round((yoy - 100) * 10) / 10 : yoy, + raw, + }; + return ridershipItem(extra, r.link ?? `${HOST}/company/data/passenger/`); + }); +}; + +const fetchYear = async (indexHtml: string, indexUrl: string) => { + const heading = load(indexHtml)('h1').first().text(); + const fiscalYear = parseFiscalYear(heading); + if (fiscalYear === null) { + throw new Error('jreast: fiscal year not found in the page heading'); + } + const year = fiscalYear; + const items = parseStations(indexHtml, fiscalYear, 1); + // 101位以下: {year}_01.html … in the same directory; a missing page (404) or an empty one ends the sequence. + const dir = indexUrl.slice(0, indexUrl.lastIndexOf('/') + 1); + const subpages = await pMap( + Array.from({ length: MAX_SUBPAGES }, (_, i) => `${dir}${year}_${String(i + 1).padStart(2, '0')}.html`), + (url) => fetchOptional(url), + { concurrency: 2 } + ); + for (const html of subpages) { + const page = html === null ? [] : parseStations(html, fiscalYear, items.length + 1); + if (page.length === 0) { + break; + } + items.push(...page); + } + return { fiscalYear, items }; +}; + +/** Latest year lives at `/company/data/passenger/`; earlier years at `{year}.html` there (FY2023) or under `/passenger/` (FY2022 and before). */ +const resolveYearPage = async (year: string | undefined): Promise<{ link: string; html: string }> => { + const candidates = year === undefined ? [LATEST] : [`${LATEST}${year}.html`, `${HOST}/passenger/${year}.html`]; + for (const link of candidates) { + // eslint-disable-next-line no-await-in-loop -- the second URL is only tried when the first is missing + const html = await fetchOptional(link); + if (html !== null) { + return { link, html }; + } + } + throw new Error(`jreast: FY${year} is not published`); +}; + +export const handler = async (ctx): Promise => { + const yearParam: string | undefined = ctx.req.param('year'); + const page = (await cache.tryGet(`jreast/ridership:${yearParam ?? 'latest'}`, async () => { + const { link, html } = await resolveYearPage(yearParam); + return { link, ...(await fetchYear(html, link)) }; + })) as { link: string; fiscalYear: number; items: Array> }; + const link = page.link; + + return { + title: `JR東日本 各駅の乗車人員(${page.fiscalYear}年度一日平均)`, + link, + language: 'ja', + item: page.items, + }; +}; + +export const route: Route = { + path: '/ridership/:year?', + name: '各駅の乗車人員', + url: 'www.jreast.co.jp', + maintainers: ['pseudoyu'], + handler, + example: '/jreast/ridership', + parameters: { + year: { + description: 'Fiscal year (`2019` … latest); omit for the latest year', + }, + }, + description: `Annual 各駅の乗車人員 (one-day average of boarding passengers) for every JR東日本 station, from [各駅の乗車人員](${HOST}/company/data/passenger/) (ベスト100 plus the 101位以下 pages). One item per station and fiscal year; \`_extra\` follows the shared ridership shape with \`measure: 'boarding'\` — JR東日本 counts 乗車 only, so the figures are not comparable with the 乗降 figures of other operators. \`yoy_pct\` is normalised to a % change (the site prints a ratio such as 102.5 from FY2023 on). The operator does not publish a release date, so items have no \`pubDate\`. + +::: warning +The site's [ご利用にあたって](${HOST}/site/rules.html) states: 「当サイト上に掲載されている全ての写真、社名ロゴ、画像、文章等のデータ等の利用については、複製・転用・転載・電磁的加工・送信・頒布・二次的使用・その他これらに類する全ての行為も含め、一切お断りいたします。」 Use the figures accordingly. +:::`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportRadar: true, + }, + radar: [ + { + source: ['www.jreast.co.jp/company/data/passenger/', 'www.jreast.co.jp/passenger/'], + target: '/ridership', + }, + ], +}; diff --git a/lib/routes/keio/ridership.ts b/lib/routes/keio/ridership.ts index f1a841875ebf..3d68ca5628b5 100644 --- a/lib/routes/keio/ridership.ts +++ b/lib/routes/keio/ridership.ts @@ -81,6 +81,7 @@ const parsePage = (html: string): { years: number[]; items: Array 0 ? round1((n / p - 1) * 100) : null, diff --git a/lib/routes/odakyu/ridership.ts b/lib/routes/odakyu/ridership.ts index 39a9e4c9ba50..61d3a0feb114 100644 --- a/lib/routes/odakyu/ridership.ts +++ b/lib/routes/odakyu/ridership.ts @@ -57,6 +57,7 @@ const parsePage = (html: string): { fiscalYear: number; items: Array { + const line = r.description.match(/\]\s*(\S+線)/)?.[1]; + const year = r.description.match(/(\d{4})年4月/)?.[1]; + return line && year ? { line, fiscalYear: Number(year) } : null; +}; + +/** + * One Shift_JIS CSV per line: `駅名,一日平均乗車人員,一日平均降車人員`, plain digits, a final `計NN駅` row. + * The publisher's 浅草線 file labels its first row (西馬込) as 馬込; the operator's own table shows 西馬込, so that row is relabelled. + */ +const parseCsv = (buf: ArrayBuffer, line: string, fiscalYear: number, link: string) => { + const text = new TextDecoder('shift_jis').decode(buf); + const rows = text + .split(/\r?\n/) + .slice(1) + .map((l) => l.split(',').map((c) => c.trim())) + .filter((cells) => cells.length >= 3 && cells[0] !== '' && !cells[0].startsWith('計')); + return rows.map((cells, index) => { + const [label, boarding, alighting] = cells; + const station = line === '浅草線' && index === 0 && label === '馬込' && rows[1]?.[0] === '馬込' ? '西馬込' : label; + const on = /^\d+$/.test(boarding) ? Number(boarding) : null; + const off = /^\d+$/.test(alighting) ? Number(alighting) : null; + const extra: RidershipExtra = { + source: 'toei', + operator: OPERATOR, + station, + line, + fiscal_year: fiscalYear, + daily_average: on === null || off === null ? null : on + off, + unit: '人/日', + measure: 'boarding_alighting', + rank: null, + yoy_pct: null, + raw: { station: label, boarding, alighting }, + }; + return ridershipItem(extra, link); + }); +}; + +export const handler = async (): Promise => { + const pkg = (await cache.tryGet(PACKAGE_API, async () => { + const body = await ofetch(PACKAGE_API); + const resources: Resource[] = body?.result?.resources ?? []; + return resources.map((r) => ({ ...r, meta: parseResource(r) })).filter((r) => r.meta !== null); + })) as Array; + + const items = ( + await Promise.all( + pkg.map( + (r) => + cache.tryGet(`${r.url}#${r.last_modified ?? ''}`, async () => { + const buf: ArrayBuffer = await ofetch(r.url, { responseType: 'arrayBuffer' }); + return parseCsv(buf, r.meta.line, r.meta.fiscalYear, CATALOG_PAGE).map((item) => ({ + ...item, + pubDate: r.last_modified === null ? undefined : parseDate(r.last_modified), + })); + }) as Promise>> + ) + ) + ).flat(); + const fiscalYear = pkg[0]?.meta.fiscalYear; + + return { + title: `都営地下鉄 各駅乗降人員${fiscalYear === undefined ? '' : `(${fiscalYear}年度一日平均)`}`, + link: CATALOG_PAGE, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/ridership', + name: '都営地下鉄 各駅乗降人員', + url: 'www.kotsu.metro.tokyo.jp', + maintainers: ['pseudoyu'], + handler, + example: '/toei/ridership', + parameters: {}, + description: `Annual 各駅乗降人員 (one-day average of boarding + alighting passengers) for every 都営地下鉄 station, read from the Tokyo open-data catalog dataset [地下鉄関連情報 各駅乗降人員一覧](${CATALOG_PAGE}) (CC BY 4.0, one Shift_JIS CSV per line) — the operator's own [各駅乗降人員一覧](https://www.kotsu.metro.tokyo.jp/subway/kanren/passengers.html) page carries the same figures behind a browser challenge. One item per station and line (stations shared by two lines appear once per line with that line's figures); \`_extra\` follows the shared ridership shape with \`daily_average\` = 乗車 + 降車 and \`measure: 'boarding_alighting'\`. Only the current fiscal year is published and the files are overwritten in place; \`pubDate\` is the resource's last-modified date. Credit: 地下鉄関連情報 各駅乗降人員一覧、東京都・東京都交通局、CC BY 4.0.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.kotsu.metro.tokyo.jp/subway/kanren/passengers.html', `catalog.data.metro.tokyo.lg.jp/dataset/${DATASET}`], + target: '/ridership', + }, + ], +}; diff --git a/lib/routes/tokyometro/ridership.ts b/lib/routes/tokyometro/ridership.ts index 96de5ad064b3..71ce4da11737 100644 --- a/lib/routes/tokyometro/ridership.ts +++ b/lib/routes/tokyometro/ridership.ts @@ -73,6 +73,7 @@ const parsePage = (html: string, link: string): { fiscalYear: number; items: Arr fiscal_year: fiscalYear, daily_average: parseCount(raw.passengers), unit: '人/日', + measure: 'boarding_alighting', rank: rankCell ? parseCount(raw.rank) : null, yoy_pct: parsePct(raw.yoy), raw, diff --git a/lib/routes/tokyometro/utils.ts b/lib/routes/tokyometro/utils.ts index fbbedb182b9e..6c5109394740 100644 --- a/lib/routes/tokyometro/utils.ts +++ b/lib/routes/tokyometro/utils.ts @@ -1,12 +1,15 @@ /** * Shared shape + helpers for the 駅別乗降人員 (station ridership) routes of Tokyo private railways - * (tokyometro / tokyu / odakyu / keio). One item per station per fiscal year; unknown = `null`, never `0`; + * (tokyometro / tokyu / odakyu / keio / toei) and JR東日本. One item per station per fiscal year; unknown = `null`, never `0`; * the operator's cell text is kept verbatim in `raw`. */ import type { DataItem } from '@/types'; -export type RidershipSource = 'tokyometro' | 'tokyu' | 'odakyu' | 'keio'; +export type RidershipSource = 'tokyometro' | 'tokyu' | 'odakyu' | 'keio' | 'jreast' | 'toei'; + +/** What the operator counts: JR東日本 publishes 乗車 (boarding) only, the others 乗降 (boarding + alighting). */ +export type RidershipMeasure = 'boarding' | 'boarding_alighting'; export interface RidershipExtra { source: RidershipSource; @@ -16,6 +19,7 @@ export interface RidershipExtra { fiscal_year: number; // 2025 = FY2025 (2025-04 … 2026-03) daily_average: number | null; // 一日平均乗降人員 unit: '人/日'; + measure: RidershipMeasure; rank: number | null; // only when the operator publishes a 順位 yoy_pct: number | null; // 前年比 / 増減率 (%); negative when the operator prints ▲ or a minus raw: Record; diff --git a/lib/routes/tokyu/ridership.ts b/lib/routes/tokyu/ridership.ts index 337680c1125b..e54b211319f4 100644 --- a/lib/routes/tokyu/ridership.ts +++ b/lib/routes/tokyu/ridership.ts @@ -65,6 +65,7 @@ const parsePage = (html: string, link: string): { fiscalYear: number; items: Arr fiscal_year: fiscalYear, daily_average: parseCount(raw.total), unit: '人/日', + measure: 'boarding_alighting', rank: null, yoy_pct: parsePct(raw.yoy), raw, From 4cf494653c56fdbbbd90ad8a061feb71feede7f8 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 15 Sep 2026 20:49:09 +0800 Subject: [PATCH 0819/1190] perf: replace acorn with oxc-parser (#23302) * perf: speed up js parsing * fix: drop parseScriptData for dongqiudi * refactor: rename evaluate-script * refactor: rename exports * chore: move oxc-parser to dependencies --- .github/dependabot.yml | 1 + lib/routes/dongqiudi/special.ts | 2 +- lib/routes/dongqiudi/top-news.ts | 2 +- lib/routes/dongqiudi/utils.ts | 86 ++-- lib/routes/hotukdeals/hottest.ts | 4 +- lib/routes/lofter/tag.ts | 4 +- lib/routes/nintendo/utils.ts | 6 +- lib/routes/pixiv/novel-api/content/nsfw.ts | 4 +- lib/routes/qq/kg/cache.ts | 4 +- lib/routes/qq/kg/user.ts | 4 +- lib/routes/xueqiu/column.ts | 4 +- lib/routes/zhihu/activities.ts | 3 +- lib/routes/zhihu/answers.ts | 3 +- lib/routes/zhihu/jsdom.ts | 197 --------- lib/routes/zhihu/jsdom.worker.ts | 6 - lib/routes/zhihu/posts.ts | 3 +- lib/routes/zhihu/question.ts | 3 +- lib/routes/zhihu/topic.ts | 3 +- lib/routes/zhihu/utils.ts | 202 ++++++++- lib/routes/zhihu/zhuanlan.ts | 3 +- lib/shims/jsdom.ts | 5 + lib/shims/vm2.ts | 3 + lib/utils/evaluate-script.test.ts | 133 ++++++ ...arse-script-data.ts => evaluate-script.ts} | 247 +++++------ lib/utils/evaluate-script.worker.test.ts | 1 + .../parse-date-in-timezone.worker.test.ts | 1 + lib/utils/parse-js.ts | 11 + lib/utils/parse-js.worker.ts | 21 + lib/utils/parse-script-data.test.ts | 134 ------ .../utils/playwright.worker.test.ts | 8 +- package.json | 4 +- pnpm-lock.yaml | 140 ++++--- scripts/workflow/check-orphan-files.ts | 2 +- tests/lofter-tag.test.ts | 63 --- tests/nuxt-routes.test.ts | 92 ----- tests/page-script-data-routes.test.ts | 208 ---------- tests/yamibo-browser.test.ts | 72 ---- tests/zhihu-browser.test.ts | 382 ------------------ tests/zhihu-jsdom.test.ts | 241 ----------- tests/zhihu-session.worker.test.ts | 64 --- tsdown-worker.config.ts | 3 + vitest.workers.config.ts | 4 +- 42 files changed, 655 insertions(+), 1728 deletions(-) delete mode 100644 lib/routes/zhihu/jsdom.ts delete mode 100644 lib/routes/zhihu/jsdom.worker.ts create mode 100644 lib/shims/jsdom.ts create mode 100644 lib/shims/vm2.ts create mode 100644 lib/utils/evaluate-script.test.ts rename lib/utils/{parse-script-data.ts => evaluate-script.ts} (62%) create mode 100644 lib/utils/evaluate-script.worker.test.ts create mode 100644 lib/utils/parse-date-in-timezone.worker.test.ts create mode 100644 lib/utils/parse-js.ts create mode 100644 lib/utils/parse-js.worker.ts delete mode 100644 lib/utils/parse-script-data.test.ts rename tests/playwright-lifecycle.worker.test.ts => lib/utils/playwright.worker.test.ts (93%) delete mode 100644 tests/lofter-tag.test.ts delete mode 100644 tests/nuxt-routes.test.ts delete mode 100644 tests/page-script-data-routes.test.ts delete mode 100644 tests/yamibo-browser.test.ts delete mode 100644 tests/zhihu-browser.test.ts delete mode 100644 tests/zhihu-jsdom.test.ts delete mode 100644 tests/zhihu-session.worker.test.ts diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 47bdcff37e09..c8e0b9a00185 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -43,6 +43,7 @@ updates: - '@opentelemetry/*' oxc: patterns: + - '@oxc-parser/*' - '@oxlint/*' - 'oxc-parser' - 'oxfmt' diff --git a/lib/routes/dongqiudi/special.ts b/lib/routes/dongqiudi/special.ts index 5315385cc316..b42d2b6441fa 100644 --- a/lib/routes/dongqiudi/special.ts +++ b/lib/routes/dongqiudi/special.ts @@ -42,7 +42,7 @@ async function handler(ctx) { cache.tryGet(item.link, async () => { try { const { data: response } = await got(item.link); - processFeedType2(item, response); + await processFeedType2(item, response); } catch (error) { if (!(error instanceof FetchError) || error.statusCode !== 404) { throw error; diff --git a/lib/routes/dongqiudi/top-news.ts b/lib/routes/dongqiudi/top-news.ts index 68ad93675adf..f5d9d81726a7 100644 --- a/lib/routes/dongqiudi/top-news.ts +++ b/lib/routes/dongqiudi/top-news.ts @@ -49,7 +49,7 @@ async function handler(ctx) { list.map((item) => cache.tryGet(item.link, async () => { const { data: response } = await got(item.link); - processFeedType2(item, response); + await processFeedType2(item, response); return item; }) ) diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index 23f4fa8b4c44..1d498b21a9d9 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -3,34 +3,24 @@ import { load } from 'cheerio'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptData } from '@/utils/parse-script-data'; import timezone from '@/utils/timezone'; -const readNuxtData = (response: string) => { - const $ = load(response); - const script = $('script') - .toArray() - .map((element) => $(element).text()) - .find((source) => /\b__NUXT__\s*=/.test(source)); - return script ? parseScriptData<{ data?: any[] } | undefined>(script, '__NUXT__')?.data?.[0] : undefined; -}; - -const processVideo = (content) => { - content('div.video').each((i, v) => { +const processVideo = ($, scope) => { + scope.find('div.video').each((i, v) => { let link = new URL(v.attribs.src); if (link.host === 'm.miguvideo.com') { - content(` ▶️ 观看视频
    `).insertAfter(v); - content(v).remove(); + $(` ▶️ 观看视频
    `).insertAfter(v); + $(v).remove(); } else { link = v.attribs.src; switch (v.attribs.site) { case 'qiniu': - content(``).insertAfter(v); - content(v).remove(); + $(``).insertAfter(v); + $(v).remove(); break; case 'youku': - content(``).insertAfter(v); - content(v).remove(); + $(``).insertAfter(v); + $(v).remove(); break; default: break; @@ -39,16 +29,14 @@ const processVideo = (content) => { }); // Process iframes - content('iframe.media-iframe, .edui-faked-video').each((i, v) => { + scope.find('iframe.media-iframe, .edui-faked-video').each((i, v) => { const link = v.attribs.src; if (link.startsWith('http://ssports.iqiyi.com/')) { - content(` ▶️ 观看视频
    `).insertAfter(v); + $(` ▶️ 观看视频
    `).insertAfter(v); } - content(v).remove(); + $(v).remove(); }); - - return content; }; const processHref = (content) => { @@ -77,19 +65,16 @@ export const processFeed = async (type, id) => { const link = `https://www.dongqiudi.com/${type}/${id}.html`; const apiUrl = 'https://api.dongqiudi.com/v3/archive/app/channel/feeds'; const { data: response } = await got(link); + const $ = load(response); - const nuxtData = readNuxtData(response); - if (!nuxtData) { - throw new Error('Unable to extract Dongqiudi page data'); - } let name; let image; if (type === 'team') { - name = nuxtData.teamInfo.name; - image = nuxtData.teamInfo.logo; + name = $('h1.tp-hero__name').text(); + image = $('.tp-hero__logo').attr('src'); } else { - name = nuxtData.detail.base_info.person_name; - image = nuxtData.detail.base_info.person_logo; + name = $('h1.pp-hero__name').text().trim(); + image = $('.pp-hero__avatar').attr('src'); } const { data } = await got(apiUrl, { @@ -110,12 +95,17 @@ export const processFeed = async (type, id) => { })); if (type === 'team' && list.length === 0) { - list = nuxtData.newsList.map((news) => ({ - title: news.title, - link: `https://www.dongqiudi.com/articles/${news.id}.html`, - category: [news.category], - pubDate: timezone(parseDate(news.time), 8), - })); + list = $('.tp-news-item') + .toArray() + .map((element) => { + const news = $(element); + return { + title: news.find('.tp-news-item__title').text(), + link: new URL(news.attr('href')!, link).href, + category: [news.find('.tp-news-item__tag').text()], + pubDate: timezone(parseDate(news.find('.tp-news-item__time').text(), 'YYYY-MM-DD HH:mm'), 8), + }; + }); } const out = await Promise.all( @@ -123,7 +113,7 @@ export const processFeed = async (type, id) => { cache.tryGet(item.link, async () => { const { data: response } = await got(item.link); - processFeedType2(item, response); + await processFeedType2(item, response); return item; }) @@ -139,16 +129,18 @@ export const processFeed = async (type, id) => { }; export const processFeedType2 = (item, response) => { - const data = readNuxtData(response)?.article; - - // filter out undefined item - if (!data) { + const $ = load(response); + const articleBody = $('.article-body'); + if (!articleBody.length) { return; } - const body = processVideo(load(data.rawBody, null, false)); - processHref(body('a')); - processImg(body('img')); - item.description = body.html(); - item.author = data.author; + processVideo($, articleBody); + processHref(articleBody.find('a')); + processImg(articleBody.find('img')); + item.description = articleBody.html(); + const author = $('.article-head__author-name').text(); + if (author) { + item.author = author; + } }; diff --git a/lib/routes/hotukdeals/hottest.ts b/lib/routes/hotukdeals/hottest.ts index cda2e96386d0..0bce0a5e5648 100644 --- a/lib/routes/hotukdeals/hottest.ts +++ b/lib/routes/hotukdeals/hottest.ts @@ -1,8 +1,8 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import got from '@/utils/got'; -import { parseScriptData } from '@/utils/parse-script-data'; export const route: Route = { path: '/hottest', @@ -37,7 +37,7 @@ async function handler() { .map((element) => $(element).text()) .filter((source) => source.includes('__INITIAL_STATE__')) .join('\n'); - const { widgets } = parseScriptData<{ + const { widgets } = await evaluateScriptData<{ widgets: { hottestWidget: { threads: Array<{ title: string; mainImage: { path: string; name: string }; temperature: number; displayPrice: string; url: string }>; diff --git a/lib/routes/lofter/tag.ts b/lib/routes/lofter/tag.ts index 5d294971a0b2..a348f3644ad7 100644 --- a/lib/routes/lofter/tag.ts +++ b/lib/routes/lofter/tag.ts @@ -3,9 +3,9 @@ import { load } from 'cheerio'; import { config } from '@/config'; import ConfigNotFoundError from '@/errors/types/config-not-found'; import type { Route } from '@/types'; +import { evaluateScriptCallback } from '@/utils/evaluate-script'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptCallback } from '@/utils/parse-script-data'; export const route: Route = { path: '/tag/:name?/:type?', @@ -83,7 +83,7 @@ async function handler(ctx) { }, }); - const data = parseScriptCallback(response.data, 'dwr.engine._remoteHandleCallback'); + const data = await evaluateScriptCallback(response.data, 'dwr.engine._remoteHandleCallback'); const title = { diff --git a/lib/routes/nintendo/utils.ts b/lib/routes/nintendo/utils.ts index 22be8f0966e9..32dcc2471f58 100644 --- a/lib/routes/nintendo/utils.ts +++ b/lib/routes/nintendo/utils.ts @@ -4,15 +4,15 @@ import { load } from 'cheerio'; import dayjs from 'dayjs'; import localizedFormat from 'dayjs/plugin/localizedFormat.js'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptData } from '@/utils/parse-script-data'; import { renderEshopCnDescription } from './templates/eshop-cn'; dayjs.extend(localizedFormat); -function nuxtReader(data) { +async function nuxtReader(data) { let nuxt; try { const $ = load(data); @@ -20,7 +20,7 @@ function nuxtReader(data) { .toArray() .map((element) => $(element).text()) .find((source) => /\b__NUXT__\s*=/.test(source)); - nuxt = parseScriptData<{ data: any[] }>(script ?? '', '__NUXT__').data[0]; + nuxt = (await evaluateScriptData<{ data: any[] }>(script ?? '', '__NUXT__')).data[0]; } catch { throw new Error('Nuxt 框架信息提取失败,请报告这个问题'); } diff --git a/lib/routes/pixiv/novel-api/content/nsfw.ts b/lib/routes/pixiv/novel-api/content/nsfw.ts index c482debd7b30..ab7e30efaf49 100644 --- a/lib/routes/pixiv/novel-api/content/nsfw.ts +++ b/lib/routes/pixiv/novel-api/content/nsfw.ts @@ -2,8 +2,8 @@ import { load } from 'cheerio'; import queryString from 'query-string'; import cache from '@/utils/cache'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptData } from '@/utils/parse-script-data'; import { maskHeader } from '../../constants'; import got from '../../pixiv-got'; @@ -32,7 +32,7 @@ export async function getNSFWNovelContent(novelId: string, token: string): Promi if (!script) { throw new Error('No novel data found'); } - const novelDetail = parseScriptData(script, 'window.pixiv.novel'); + const novelDetail = await evaluateScriptData(script, 'window.pixiv.novel'); if (!novelDetail) { throw new Error('No novel data found'); diff --git a/lib/routes/qq/kg/cache.ts b/lib/routes/qq/kg/cache.ts index a440b54c7407..e48b29fd2009 100644 --- a/lib/routes/qq/kg/cache.ts +++ b/lib/routes/qq/kg/cache.ts @@ -1,8 +1,8 @@ import { load } from 'cheerio'; import cache from '@/utils/cache'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import got from '@/utils/got'; -import { parseScriptData } from '@/utils/parse-script-data'; export default { getPlayInfo: async (ctx, shareId, ksong_mid = '') => { @@ -16,7 +16,7 @@ export default { .map((element) => $(element).text()) .filter((source) => source.includes('__DATA__')) .join('\n'); - const data = parseScriptData<{ + const data = await evaluateScriptData<{ detail: { song_name: string; content: string; diff --git a/lib/routes/qq/kg/user.ts b/lib/routes/qq/kg/user.ts index 4f2d6e9e333e..d12578057bf4 100644 --- a/lib/routes/qq/kg/user.ts +++ b/lib/routes/qq/kg/user.ts @@ -1,9 +1,9 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptData } from '@/utils/parse-script-data'; import cache from './cache'; @@ -36,7 +36,7 @@ async function handler(ctx) { .map((element) => $(element).text()) .filter((source) => source.includes('__DATA__')) .join('\n'); - const data = parseScriptData<{ + const data = await evaluateScriptData<{ data: { nickname: string; head_img_url: string; diff --git a/lib/routes/xueqiu/column.ts b/lib/routes/xueqiu/column.ts index 22823cf7b64e..67e2d5b87dd6 100644 --- a/lib/routes/xueqiu/column.ts +++ b/lib/routes/xueqiu/column.ts @@ -2,9 +2,9 @@ import { load } from 'cheerio'; import { CookieJar } from 'tough-cookie'; import type { Route } from '@/types'; +import { evaluateScriptData } from '@/utils/evaluate-script'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import { parseScriptData } from '@/utils/parse-script-data'; const cookieJar = new CookieJar(); const baseUrl = 'https://xueqiu.com'; @@ -50,7 +50,7 @@ async function handler(ctx) { .map((element) => $(element).text()) .filter((source) => source.includes('SNOWMAN_TARGET')) .join('\n'); - const snowmanTarget = parseScriptData<{ screen_name: string; description: string }>(script, 'SNOWMAN_TARGET'); + const snowmanTarget = await evaluateScriptData<{ screen_name: string; description: string }>(script, 'SNOWMAN_TARGET'); const { data } = await got(`${baseUrl}/statuses/original/timeline.json`, { cookieJar, diff --git a/lib/routes/zhihu/activities.ts b/lib/routes/zhihu/activities.ts index e7e83238b76c..0bb615ff5be4 100644 --- a/lib/routes/zhihu/activities.ts +++ b/lib/routes/zhihu/activities.ts @@ -2,7 +2,6 @@ import sanitizeHtml from 'sanitize-html'; import type { Route } from '@/types'; import { ViewType } from '@/types'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -21,7 +20,7 @@ export const route: Route = { optional: true, }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/answers.ts b/lib/routes/zhihu/answers.ts index 157ec11b352e..3e45feba2377 100644 --- a/lib/routes/zhihu/answers.ts +++ b/lib/routes/zhihu/answers.ts @@ -1,5 +1,4 @@ import type { Route } from '@/types'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -17,7 +16,7 @@ export const route: Route = { optional: true, }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/jsdom.ts b/lib/routes/zhihu/jsdom.ts deleted file mode 100644 index 2e3b1046213a..000000000000 --- a/lib/routes/zhihu/jsdom.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { JSDOM, VirtualConsole } from 'jsdom'; -import { VM } from 'vm2'; - -import { config } from '@/config'; -import cache from '@/utils/cache'; -import { generateHeaders } from '@/utils/header-generator'; -import md5 from '@/utils/md5'; -import ofetch from '@/utils/ofetch'; - -import { getSignedHeaders } from './sign'; -import type { ZhihuClient } from './utils'; - -let isUnreachableRuntimeErrorGuarded = false; -const pendingZseCredentials = new Map>(); - -const preventUnreachableRuntimeError = () => { - if (isUnreachableRuntimeErrorGuarded) { - return; - } - isUnreachableRuntimeErrorGuarded = true; - process.on('unhandledRejection', (reason) => { - const error = reason as { name?: string; message?: string } | undefined; - if (error?.name === 'RuntimeError' && error.message === 'unreachable') { - return; - } - throw reason; - }); -}; - -const generateZseCk = async (url: string, apiPath: string, configuredDc0: string) => { - preventUnreachableRuntimeError(); - - // `__zse_ck` is checked against the user-agent that generated it. - const ua = generateHeaders()['user-agent']; - const headers = { 'user-agent': ua }; - - let dc0 = configuredDc0; - if (!dc0) { - const seed = await ofetch.raw('https://www.zhihu.com/explore', { - headers, - redirect: 'manual', - ignoreResponseError: true, - }); - dc0 = - (seed.headers.getSetCookie?.() ?? []) - .find((line) => line.startsWith('d_c0=')) - ?.split(';', 1)[0] - .slice('d_c0='.length) - .split('|', 1)[0] - .replace(/=+$/, '') || ''; - } - if (!dc0) { - throw new Error('zhihu: failed to obtain a guest d_c0 cookie'); - } - - const challenge = await ofetch.raw(`https://www.zhihu.com${apiPath}`, { - headers: { - ...headers, - cookie: `d_c0=${dc0}; __zse_ck=005_x-x`, - referer: url, - 'x-requested-with': 'fetch', - }, - ignoreResponseError: true, - }); - const html = challenge._data ?? ''; - const meta = html.match(/id="zh-zse-ck"[^>]*content="([^"]*)"/)?.[1]; - const hash = html.match(/zse-ck\/v4\/([a-f0-9]+)\.js/)?.[1]; - if (!meta || !hash) { - throw new Error('zhihu: challenge page did not contain an URL to __zse_ck meta/script'); - } - - const vmScript = await ofetch(`https://static.zhihu.com/zse-ck/v4/${hash}.js`, { - headers, - parseResponse: (text) => text, - }); - const dom = new JSDOM(``, { - url, - referrer: 'https://www.zhihu.com/', - runScripts: 'outside-only', - pretendToBeVisual: true, - virtualConsole: new VirtualConsole(), - }); - const { window } = dom; - Object.defineProperties(window.navigator, { - userAgent: { value: ua, configurable: true }, - webdriver: { value: false, configurable: true }, - }); - window.TextEncoder = TextEncoder; - window.TextDecoder = TextDecoder as typeof window.TextDecoder; - window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary'); - window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64'); - Object.assign(window, { __g: {} }); - - const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie'); - const setCookie = cookieDescriptor?.set; - if (!cookieDescriptor?.get || !setCookie) { - window.close(); - throw new Error('zhihu: JSDOM did not provide document.cookie accessors'); - } - const tokenPromise = new Promise((resolve) => { - Object.defineProperty(window.document, 'cookie', { - configurable: true, - get: cookieDescriptor.get, - set(value: string) { - setCookie.call(window.document, value); - const token = value.match(/__zse_ck=([^;]+)/)?.[1]; - if (token?.includes('-')) { - resolve(token); - } - }, - }); - }); - - let zseCk: string | undefined; - let timeoutId: ReturnType | number | undefined; - try { - // Zhihu's challenge is intentionally delivered as executable JavaScript. - new VM({ timeout: 3000, sandbox: window }).run(vmScript); - const timeout = new Promise((resolve) => { - timeoutId = setTimeout(resolve, 3000); - }); - zseCk = await Promise.race([tokenPromise, timeout]); - } finally { - clearTimeout(timeoutId); - window.close(); - } - if (!zseCk) { - throw new Error('zhihu: WASM VM did not produce a __zse_ck'); - } - return { dc0, zseCk, ua }; -}; - -const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: string) => { - const cacheKey = `zhihu:zse-ck:v4:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; - const pending = pendingZseCredentials.get(cacheKey); - if (pending) { - return pending; - } - - const created = (async () => { - try { - return await cache.tryGet(cacheKey, () => generateZseCk(url, apiPath, configuredDc0), config.cache.contentExpire, false); - } finally { - pendingZseCredentials.delete(cacheKey); - } - })(); - pendingZseCredentials.set(cacheKey, created); - return created; -}; - -const mergeGeneratedCookies = (configured: string, dc0: string, zseCk: string) => { - const remaining = configured - .split(';') - .map((pair) => pair.trim()) - .filter((pair) => { - const name = pair.split('=', 1)[0]; - return name && name !== 'd_c0' && name !== '__zse_ck'; - }); - return [`__zse_ck=${zseCk}`, `d_c0=${dc0}`, ...remaining].join('; '); -}; - -export const createJSDOMClient = (pageUrl: string, configured: string, configuredDc0: string): ZhihuClient => { - let sessionPromise: Promise<{ dc0: string; headers: { cookie: string; 'user-agent': string } }> | undefined; - const startSession = async (apiPath: string) => { - try { - const { dc0, zseCk, ua } = await getGeneratedZseCredentials(pageUrl, apiPath, configuredDc0); - return { - dc0, - headers: { - // An isolated login cookie must not be mixed with a new guest identity. - cookie: configuredDc0 ? mergeGeneratedCookies(configured, dc0, zseCk) : `__zse_ck=${zseCk}; d_c0=${dc0}`, - 'user-agent': ua, - }, - }; - } catch (error) { - sessionPromise = undefined; - throw error; - } - }; - const getSession = (apiPath = new URL(pageUrl).pathname + new URL(pageUrl).search) => (sessionPromise ??= startSession(apiPath)); - - return { - get: async (apiPath: string): Promise => { - if (!apiPath.startsWith('/api/')) { - throw new Error('zhihu: expected an API path'); - } - const { dc0, headers } = await getSession(apiPath); - return ofetch(`https://www.zhihu.com${apiPath}`, { - headers: { ...getSignedHeaders(apiPath, dc0), ...headers, Referer: pageUrl }, - }); - }, - getPage: async () => { - const { headers } = await getSession(); - return ofetch(pageUrl, { headers: { ...headers, Referer: pageUrl }, parseResponse: (text) => text }); - }, - }; -}; diff --git a/lib/routes/zhihu/jsdom.worker.ts b/lib/routes/zhihu/jsdom.worker.ts deleted file mode 100644 index 053cb52b9f12..000000000000 --- a/lib/routes/zhihu/jsdom.worker.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { createJSDOMClient as createNodeJSDOMClient } from './jsdom'; - -// Exclude JSDOM from Worker bundles, including unreachable dynamic import chunks. -export const createJSDOMClient: typeof createNodeJSDOMClient = () => { - throw new Error('zhihu: JSDOM session initialization is unavailable on Workers; use a browser session'); -}; diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 9cd1a9938f64..5d89cd6d0373 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -1,6 +1,5 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import type { Articles } from './types'; @@ -19,7 +18,7 @@ export const route: Route = { optional: true, }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index abf4811235ef..e0784172d62b 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.ts @@ -1,5 +1,4 @@ import type { Route } from '@/types'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -17,7 +16,7 @@ export const route: Route = { optional: true, }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/topic.ts b/lib/routes/zhihu/topic.ts index b8b056c7b532..0edd93774372 100644 --- a/lib/routes/zhihu/topic.ts +++ b/lib/routes/zhihu/topic.ts @@ -1,6 +1,5 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -17,7 +16,7 @@ export const route: Route = { description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index a3b31b39896c..7110741c254f 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -1,10 +1,15 @@ import { load } from 'cheerio'; +import { JSDOM, VirtualConsole } from 'jsdom'; +import { VM } from 'vm2'; import { config } from '@/config'; +import cache from '@/utils/cache'; +import { generateHeaders } from '@/utils/header-generator'; import { isWorker } from '@/utils/is-worker'; +import md5 from '@/utils/md5'; import ofetch from '@/utils/ofetch'; -import type { createBrowserClient } from './browser'; +import { createBrowserClient } from './browser'; import { getSignedHeaders } from './sign'; export const header = { @@ -72,22 +77,203 @@ export type ZhihuClient = { getPage(): Promise; }; +let isUnreachableRuntimeErrorGuarded = false; +const pendingZseCredentials = new Map>(); + +const preventUnreachableRuntimeError = () => { + if (isUnreachableRuntimeErrorGuarded) { + return; + } + isUnreachableRuntimeErrorGuarded = true; + process.on('unhandledRejection', (reason) => { + const error = reason as { name?: string; message?: string } | undefined; + if (error?.name === 'RuntimeError' && error.message === 'unreachable') { + return; + } + throw reason; + }); +}; + +const generateZseCk = async (url: string, apiPath: string, configuredDc0: string) => { + preventUnreachableRuntimeError(); + + // `__zse_ck` is checked against the user-agent that generated it. + const ua = generateHeaders()['user-agent']; + const headers = { 'user-agent': ua }; + + let dc0 = configuredDc0; + if (!dc0) { + const seed = await ofetch.raw('https://www.zhihu.com/explore', { + headers, + redirect: 'manual', + ignoreResponseError: true, + }); + dc0 = + (seed.headers.getSetCookie?.() ?? []) + .find((line) => line.startsWith('d_c0=')) + ?.split(';', 1)[0] + .slice('d_c0='.length) + .split('|', 1)[0] + .replace(/=+$/, '') || ''; + } + if (!dc0) { + throw new Error('zhihu: failed to obtain a guest d_c0 cookie'); + } + + const challenge = await ofetch.raw(`https://www.zhihu.com${apiPath}`, { + headers: { + ...headers, + cookie: `d_c0=${dc0}; __zse_ck=005_x-x`, + referer: url, + 'x-requested-with': 'fetch', + }, + ignoreResponseError: true, + }); + const html = challenge._data ?? ''; + const meta = html.match(/id="zh-zse-ck"[^>]*content="([^"]*)"/)?.[1]; + const hash = html.match(/zse-ck\/v4\/([a-f0-9]+)\.js/)?.[1]; + if (!meta || !hash) { + throw new Error('zhihu: challenge page did not contain an URL to __zse_ck meta/script'); + } + + const vmScript = await ofetch(`https://static.zhihu.com/zse-ck/v4/${hash}.js`, { + headers, + parseResponse: (text) => text, + }); + const dom = new JSDOM(``, { + url, + referrer: 'https://www.zhihu.com/', + runScripts: 'outside-only', + pretendToBeVisual: true, + virtualConsole: new VirtualConsole(), + }); + const { window } = dom; + Object.defineProperties(window.navigator, { + userAgent: { value: ua, configurable: true }, + webdriver: { value: false, configurable: true }, + }); + window.TextEncoder = TextEncoder; + window.TextDecoder = TextDecoder as typeof window.TextDecoder; + window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary'); + window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64'); + Object.assign(window, { __g: {} }); + + const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie'); + const setCookie = cookieDescriptor?.set; + if (!cookieDescriptor?.get || !setCookie) { + window.close(); + throw new Error('zhihu: JSDOM did not provide document.cookie accessors'); + } + const tokenPromise = new Promise((resolve) => { + Object.defineProperty(window.document, 'cookie', { + configurable: true, + get: cookieDescriptor.get, + set(value: string) { + setCookie.call(window.document, value); + const token = value.match(/__zse_ck=([^;]+)/)?.[1]; + if (token?.includes('-')) { + resolve(token); + } + }, + }); + }); + + let zseCk: string | undefined; + let timeoutId: ReturnType | number | undefined; + try { + // Zhihu's challenge is intentionally delivered as executable JavaScript. + new VM({ timeout: 3000, sandbox: window }).run(vmScript); + const timeout = new Promise((resolve) => { + timeoutId = setTimeout(resolve, 3000); + }); + zseCk = await Promise.race([tokenPromise, timeout]); + } finally { + clearTimeout(timeoutId); + window.close(); + } + if (!zseCk) { + throw new Error('zhihu: WASM VM did not produce a __zse_ck'); + } + return { dc0, zseCk, ua }; +}; + +const getGeneratedZseCredentials = (url: string, apiPath: string, configuredDc0: string) => { + const cacheKey = `zhihu:zse-ck:v4:${configuredDc0 ? md5(configuredDc0) : 'guest'}`; + const pending = pendingZseCredentials.get(cacheKey); + if (pending) { + return pending; + } + + const created = (async () => { + try { + return await cache.tryGet(cacheKey, () => generateZseCk(url, apiPath, configuredDc0), config.cache.contentExpire, false); + } finally { + pendingZseCredentials.delete(cacheKey); + } + })(); + pendingZseCredentials.set(cacheKey, created); + return created; +}; + +const mergeGeneratedCookies = (configured: string, dc0: string, zseCk: string) => { + const remaining = configured + .split(';') + .map((pair) => pair.trim()) + .filter((pair) => { + const name = pair.split('=', 1)[0]; + return name && name !== 'd_c0' && name !== '__zse_ck'; + }); + return [`__zse_ck=${zseCk}`, `d_c0=${dc0}`, ...remaining].join('; '); +}; + +const createJSDOMClient = (pageUrl: string, configured: string, configuredDc0: string): ZhihuClient => { + let sessionPromise: Promise<{ dc0: string; headers: { cookie: string; 'user-agent': string } }> | undefined; + const startSession = async (apiPath: string) => { + try { + const { dc0, zseCk, ua } = await getGeneratedZseCredentials(pageUrl, apiPath, configuredDc0); + return { + dc0, + headers: { + // An isolated login cookie must not be mixed with a new guest identity. + cookie: configuredDc0 ? mergeGeneratedCookies(configured, dc0, zseCk) : `__zse_ck=${zseCk}; d_c0=${dc0}`, + 'user-agent': ua, + }, + }; + } catch (error) { + sessionPromise = undefined; + throw error; + } + }; + const getSession = (apiPath = new URL(pageUrl).pathname + new URL(pageUrl).search) => (sessionPromise ??= startSession(apiPath)); + + return { + get: async (apiPath: string): Promise => { + if (!apiPath.startsWith('/api/')) { + throw new Error('zhihu: expected an API path'); + } + const { dc0, headers } = await getSession(apiPath); + return ofetch(`https://www.zhihu.com${apiPath}`, { + headers: { ...getSignedHeaders(apiPath, dc0), ...headers, Referer: pageUrl }, + }); + }, + getPage: async () => { + const { headers } = await getSession(); + return ofetch(pageUrl, { headers: { ...headers, Referer: pageUrl }, parseResponse: (text) => text }); + }, + }; +}; + export const withZhihuClient = async (pageUrl: string, callback: (client: ZhihuClient) => Promise): Promise => { const configured = config.zhihu.cookies || ''; const dc0 = getCookieValueFrom(configured, 'd_c0'); const hasConfiguredSession = !!dc0 && !!getCookieValueFrom(configured, '__zse_ck'); if (!isWorker && !hasConfiguredSession) { - const { createJSDOMClient } = await import('./jsdom'); return callback(createJSDOMClient(pageUrl, configured, dc0)); } let browserPromise: ReturnType | undefined; - const startBrowser = async (apiPath?: string) => { - const { createBrowserClient } = await import('./browser'); - // Column APIs can initialize directly; other routes need their page session. - return createBrowserClient(pageUrl, dc0 ? configured : '', apiPath?.startsWith('/api/v4/columns/') ? apiPath : undefined); - }; - const getBrowser = (apiPath?: string) => (browserPromise ??= startBrowser(apiPath)); + // Column APIs can initialize directly; other routes need their page session. + const getBrowser = (apiPath?: string) => (browserPromise ??= createBrowserClient(pageUrl, dc0 ? configured : '', apiPath?.startsWith('/api/v4/columns/') ? apiPath : undefined)); try { return await callback({ diff --git a/lib/routes/zhihu/zhuanlan.ts b/lib/routes/zhihu/zhuanlan.ts index dfa9ac16adcd..9614ea561947 100644 --- a/lib/routes/zhihu/zhuanlan.ts +++ b/lib/routes/zhihu/zhuanlan.ts @@ -1,7 +1,6 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import { isWorker } from '@/utils/is-worker'; import { parseDate } from '@/utils/parse-date'; import { processImage, withZhihuClient } from './utils'; @@ -18,7 +17,7 @@ export const route: Route = { description: 'A complete d_c0 and __zse_ck cookie pair skips session initialization. Otherwise Workers use a Playwright browser session; Docker and Vercel generate credentials with JSDOM.', }, ], - requirePuppeteer: isWorker || process.env.WORKER_BUILD === 'true', + requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, diff --git a/lib/shims/jsdom.ts b/lib/shims/jsdom.ts new file mode 100644 index 000000000000..de178b98d4ad --- /dev/null +++ b/lib/shims/jsdom.ts @@ -0,0 +1,5 @@ +const unavailable = () => { + throw new Error('jsdom is unavailable on Workers'); +}; +export const JSDOM = unavailable; +export const VirtualConsole = unavailable; diff --git a/lib/shims/vm2.ts b/lib/shims/vm2.ts new file mode 100644 index 000000000000..db70b4e40591 --- /dev/null +++ b/lib/shims/vm2.ts @@ -0,0 +1,3 @@ +export const VM = () => { + throw new Error('vm2 is unavailable on Workers'); +}; diff --git a/lib/utils/evaluate-script.test.ts b/lib/utils/evaluate-script.test.ts new file mode 100644 index 000000000000..ec6727f070e7 --- /dev/null +++ b/lib/utils/evaluate-script.test.ts @@ -0,0 +1,133 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { evaluateScriptCallback, evaluateScriptData } from './evaluate-script'; + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe('serialized script data', () => { + it.each(['__DATA__', 'window.__DATA__', 'globalThis.__DATA__', 'self.__DATA__'])('normalizes the global target %s', async (target) => { + await expect(evaluateScriptData(String.raw`window.__DATA__ = { title: "Hello\u0020世界", tags: ["a", "b"] };`, target)).resolves.toEqual({ title: 'Hello 世界', tags: ['a', 'b'] }); + }); + + it('resolves variables, nested assignments, and data-only unary and logical expressions', async () => { + const source = 'var title="A & B", unused=analytics(); window.pixiv=window.pixiv||{}; pixiv.novel={title, count:-2, available:!0, absent:void 0};'; + await expect(evaluateScriptData(source, 'window.pixiv.novel')).resolves.toEqual({ title: 'A & B', count: -2, available: true, absent: undefined }); + }); + + it('reads parent-object assignments and static bracket targets', async () => { + await expect(evaluateScriptData('window.pixiv={novel:{title:"Book"}};', 'self["pixiv"].novel')).resolves.toEqual({ title: 'Book' }); + }); + + it('reads Nuxt IIFE parameters and object mutations without changing outer bindings', async () => { + const source = 'var a="outer";window.__NUXT__=(function(a,b,c){a.title=b;a.tags=c;return {data:[a,a],error:null}}({},"News",["tag"]));'; + const value = await evaluateScriptData<{ data: Array<{ title: string; tags: string[] }>; error: null }>(source, '__NUXT__'); + expect(value).toEqual({ + data: [ + { title: 'News', tags: ['tag'] }, + { title: 'News', tags: ['tag'] }, + ], + error: null, + }); + expect(value.data[0]).toBe(value.data[1]); + await expect(evaluateScriptData(source, 'a')).resolves.toBe('outer'); + }); + + it('preserves repeated var declarations and evaluates unused IIFE arguments in order', async () => { + const source = 'var a={value:1};var a;window.__DATA__=(function(x){return x})(a,a.value=2);'; + await expect(evaluateScriptData(source, '__DATA__')).resolves.toEqual({ value: 2 }); + }); + + it('does not call unrelated application code or access the host global object', async () => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + + const value = await evaluateScriptData('globalThis.__scriptDataSideEffect="changed";fetch("https://example.invalid/");window.__DATA__={ok:true};', '__DATA__'); + + expect(value).toEqual({ ok: true }); + expect(fetchSpy).not.toHaveBeenCalled(); + expect('__scriptDataSideEffect' in globalThis).toBe(false); + }); + + it.each([ + 'window.__DATA__ = fetch("https://example.invalid/");', + 'window.__DATA__ = Function("return 42")();', + 'window.__DATA__ = { get secret() { return fetch("https://example.invalid/"); } };', + 'window.__DATA__ = (function(){ while(true){} return {}; })();', + 'window.__DATA__ = (function(){ return {}; })(fetch("https://example.invalid/"));', + ])('rejects unsupported expressions required by the target without executing them', async (source) => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + await expect(evaluateScriptData(source, '__DATA__')).rejects.toThrow(/Unsupported|ordinary/); + expect(fetchSpy).not.toHaveBeenCalled(); + }); + + it.each(['__proto__', 'constructor', 'prototype'])('rejects the unsafe property %s', async (key) => { + await expect(evaluateScriptData(`window.__DATA__={"${key}":{polluted:true}};`, '__DATA__')).rejects.toThrow('Unsafe script data property'); + await expect(evaluateScriptData(`var value={};value["${key}"]={polluted:true};window.__DATA__=value;`, '__DATA__')).rejects.toThrow('Unsafe script data property'); + expect(Object.hasOwn(Object.prototype, 'polluted')).toBe(false); + }); + + it.each(['window.__DATA__()', 'window[computed]', 'window.__DATA__; unrelated()', 'window.__proto__'])('rejects unsupported target paths: %s', async (target) => { + await expect(evaluateScriptData('window.__DATA__={};', target)).rejects.toThrow(); + }); + + it('reports missing targets and unresolved variable dependencies', async () => { + await expect(evaluateScriptData('unrelated();', '__DATA__')).rejects.toThrow('target was not found'); + await expect(evaluateScriptData('var data=unrelated();window.__DATA__=data;', '__DATA__')).rejects.toThrow('Unsupported script data expression'); + }); + + it.each(['if(true){window.__DATA__.value=2;}', 'Object.assign(window.__DATA__,{value:2});', 'window.__DATA__.value++;', 'window.__DATA__[unknown()]={};'])( + 'rejects unsupported code that could change extracted data: %s', + async (suffix) => { + await expect(evaluateScriptData(`window.__DATA__={value:1};${suffix}`, '__DATA__')).rejects.toThrow('Unsupported script data'); + } + ); + + it.each(['if(true){window.__DATA__.newField=2;}', 'var ignored=Object.assign(window.__DATA__,{value:2});', 'window.ignored=Object.assign(window.__DATA__,{value:2});'])( + 'rejects mutations hidden in new properties and unused assignments: %s', + async (suffix) => { + await expect(evaluateScriptData(`window.__DATA__={};${suffix}`, '__DATA__')).rejects.toThrow('Unsupported script data'); + } + ); + + it('rejects cyclic data and excessively large sources or sparse arrays', async () => { + await expect(evaluateScriptData('var a={};a.self=a;window.__DATA__=a;', '__DATA__')).rejects.toThrow('Cyclic script data'); + await expect(evaluateScriptData(' '.repeat(2_000_001), '__DATA__')).rejects.toThrow('size limit'); + await expect(evaluateScriptData('window.__DATA__=(function(){var a=[];a[100000]={};return a})();', '__DATA__')).rejects.toThrow('bounded numeric indexes'); + }); +}); + +describe('serialized callback data', () => { + it('extracts a DWR callback argument without invoking the callback', async () => { + const callback = vi.fn(); + vi.stubGlobal('dwr', { engine: { _remoteHandleCallback: callback } }); + const source = '(function(){if(!window.dwr)return;var s0=[],s1={};s1.post={title:"Entry"};s0[0]=s1;dwr.engine._remoteHandleCallback("493053","0",s0);})();'; + + await expect(evaluateScriptCallback(source, 'dwr.engine._remoteHandleCallback')).resolves.toEqual([{ post: { title: 'Entry' } }]); + expect(callback).not.toHaveBeenCalled(); + }); + + it('supports empty public DWR responses and explicit argument positions', async () => { + const source = '//#DWR-INSERT\n//#DWR-REPLY\ndwr.engine._remoteHandleCallback("493053","0",[]);'; + await expect(evaluateScriptCallback(source, 'window.dwr.engine._remoteHandleCallback')).resolves.toEqual([]); + await expect(evaluateScriptCallback(source, 'dwr.engine._remoteHandleCallback', 0)).resolves.toBe('493053'); + }); + + it('evaluates all callback arguments in order and does not keep an earlier successful capture after an invalid one', async () => { + const source = 'var a={value:1};dwr.engine._remoteHandleCallback(a.value=2,"0",a);'; + await expect(evaluateScriptCallback(source, 'dwr.engine._remoteHandleCallback')).resolves.toEqual({ value: 2 }); + await expect(evaluateScriptCallback(`${source}dwr.engine._remoteHandleCallback("0","0",unknown());`, 'dwr.engine._remoteHandleCallback')).rejects.toThrow(); + }); + + it('rejects missing callbacks and external calls used as the data argument', async () => { + const fetchSpy = vi.fn(); + vi.stubGlobal('fetch', fetchSpy); + await expect(evaluateScriptCallback('unrelated();', 'dwr.engine._remoteHandleCallback')).rejects.toThrow('callback was not found'); + await expect(evaluateScriptCallback('dwr.engine._remoteHandleCallback("0","0",fetch("https://example.invalid/"));', 'dwr.engine._remoteHandleCallback')).rejects.toThrow(); + await expect(evaluateScriptCallback('', 'callback', -1)).rejects.toThrow('non-negative integer'); + await expect(evaluateScriptCallback('dwr["engine._remoteHandleCallback"]("0","0",[]);', 'dwr.engine._remoteHandleCallback')).rejects.toThrow('callback was not found'); + expect(fetchSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/utils/parse-script-data.ts b/lib/utils/evaluate-script.ts similarity index 62% rename from lib/utils/parse-script-data.ts rename to lib/utils/evaluate-script.ts index 883b74630a8f..fa950efcc9ed 100644 --- a/lib/utils/parse-script-data.ts +++ b/lib/utils/evaluate-script.ts @@ -1,21 +1,27 @@ -import { type AnyNode, parse } from 'acorn'; +import { type Node } from 'oxc-parser'; + +import { parseScriptSource } from './parse-js'; type DataValue = string | number | boolean | null | undefined | DataValue[] | DataObject | ScriptDataError; type DataObject = { [key: string]: DataValue }; type Scope = { values: DataObject; parent?: Scope }; -type Reference = { object: DataObject | DataValue[]; key: string }; +type Reference = { object: DataObject; key: string }; const globalNames = new Set(['window', 'globalThis', 'self']); const unsafeKeys = new Set(['__proto__', 'prototype', 'constructor']); const maxScriptLength = 2_000_000; -const maxSteps = 1e5; +const maxSteps = 10000; const maxDepth = 100; class ScriptDataError extends Error {} class UnsafePropertyError extends ScriptDataError {} -const propertyKey = (value: unknown): string => { - if (typeof value !== 'string' && typeof value !== 'number') { +const isKey = (value: DataValue): value is string | number => typeof value === 'string' || typeof value === 'number'; +const isNumber = (value: DataValue): value is number => typeof value === 'number'; +const isObject = (value: DataValue): value is DataObject | DataValue[] => typeof value === 'object' && value !== null && !(value instanceof ScriptDataError); + +const propertyKey = (value: DataValue): string => { + if (!isKey(value)) { throw new ScriptDataError('Script data property keys must be strings or numbers'); } const key = String(value); @@ -25,12 +31,12 @@ const propertyKey = (value: unknown): string => { return key; }; -const staticPath = (node: AnyNode): string[] => { +const staticPath = (node: Node): string[] => { if (node.type === 'Identifier') { return [propertyKey(node.name)]; } if (node.type === 'MemberExpression' && !node.optional) { - const key = !node.computed && node.property.type === 'Identifier' ? node.property.name : node.property.type === 'Literal' ? node.property.value : undefined; + const key = !node.computed && node.property.type === 'Identifier' ? node.property.name : node.property.type === 'Literal' && !('regex' in node.property) && !('bigint' in node.property) ? node.property.value : undefined; return [...staticPath(node.object), propertyKey(key)]; } throw new ScriptDataError('Script data targets must be static property paths'); @@ -38,8 +44,16 @@ const staticPath = (node: AnyNode): string[] => { const normalizePath = (path: string[]) => (globalNames.has(path[0]) ? path.slice(1) : path); -const targetPath = (target: string): string[] => { - const program = parse(target, { ecmaVersion: 'latest' }); +const parseProgram = async (source: string, errorMessage: string) => { + const { program, errors } = await parseScriptSource(source); + if (errors.length) { + throw new ScriptDataError(errorMessage); + } + return program; +}; + +const targetPath = async (target: string): Promise => { + const program = await parseProgram(target, 'Script data targets must be a single static property path'); if (program.body.length !== 1 || program.body[0].type !== 'ExpressionStatement') { throw new ScriptDataError('Script data targets must be a single static property path'); } @@ -50,8 +64,6 @@ const targetPath = (target: string): string[] => { return path; }; -const isObject = (value: DataValue): value is DataObject | DataValue[] => typeof value === 'object' && value !== null && !(value instanceof ScriptDataError); - const readProperty = (object: DataValue, key: string): DataValue => { if (object instanceof ScriptDataError) { throw object; @@ -63,25 +75,20 @@ const readProperty = (object: DataValue, key: string): DataValue => { }; /** Reads serialized data with a limited AST vocabulary; it never executes JavaScript. */ -class ScriptDataReader { - private root: Scope = { values: Object.create(null) }; - private steps = 0; - private poisonedAssignments = new WeakSet(); - private captured = false; - private callbackValue: DataValue; +const readScriptData = async (source: string, target: string[], argumentIndex?: number): Promise => { + const root: Scope = { values: Object.create(null) }; + const poisonedAssignments = new WeakSet(); + let steps = 0; + let captured = false; + let callbackValue: DataValue; - constructor( - private target: string[], - private argumentIndex?: number - ) {} - - private step(depth: number) { - if (++this.steps > maxSteps || depth > maxDepth) { + const step = (depth: number) => { + if (++steps > maxSteps || depth > maxDepth) { throw new UnsafePropertyError('Script data exceeds the parsing complexity limit'); } - } + }; - private identifier(name: string, scope: Scope): DataValue { + const identifier = (name: string, scope: Scope): DataValue => { propertyKey(name); for (let current: Scope | undefined = scope; current; current = current.parent) { if (Object.hasOwn(current.values, name)) { @@ -93,15 +100,15 @@ class ScriptDataReader { } } if (globalNames.has(name)) { - return this.root.values; + return root.values; } if (name === 'undefined') { return undefined; } throw new ScriptDataError(`Unknown script data variable: ${name}`); - } + }; - private reference(node: AnyNode, scope: Scope, depth: number): Reference { + const reference = (node: Node, scope: Scope, depth: number): Reference => { if (node.type === 'Identifier') { const key = propertyKey(node.name); if (globalNames.has(key)) { @@ -116,77 +123,77 @@ class ScriptDataReader { if (node.type !== 'MemberExpression' || node.optional) { throw new ScriptDataError('Unsupported script data assignment target'); } - const object = this.evaluate(node.object, scope, depth + 1); - const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : this.evaluate(node.property, scope, depth + 1)); + const object = evaluate(node.object, scope, depth + 1); + const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : evaluate(node.property, scope, depth + 1)); if (!isObject(object)) { throw new ScriptDataError('Cannot assign a property of non-object script data'); } if (Array.isArray(object) && (!/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= maxSteps)) { throw new ScriptDataError('Script data arrays require bounded numeric indexes'); } - return { object, key }; - } + return { object: object as DataObject, key }; + }; - private evaluate(node: AnyNode, scope: Scope, depth: number): DataValue { - this.step(depth); + const evaluate = (node: Node, scope: Scope, depth: number): DataValue => { + step(depth); switch (node.type) { case 'Literal': - if (node.regex || node.bigint) { + if ('regex' in node || 'bigint' in node) { break; } - return node.value as string | number | boolean | null; + return node.value; case 'Identifier': - return this.identifier(node.name, scope); + return identifier(node.name, scope); case 'ArrayExpression': - return node.elements.map((element) => (element ? this.evaluate(element, scope, depth + 1) : undefined)); + return node.elements.map((element) => (element ? evaluate(element, scope, depth + 1) : undefined)); case 'ObjectExpression': { const value: DataObject = Object.create(null); for (const property of node.properties) { if (property.type !== 'Property' || property.kind !== 'init' || property.method) { throw new ScriptDataError('Only ordinary script data object properties are supported'); } - const key = propertyKey(!property.computed && property.key.type === 'Identifier' ? property.key.name : this.evaluate(property.key, scope, depth + 1)); - value[key] = this.evaluate(property.value, scope, depth + 1); + const key = propertyKey(!property.computed && property.key.type === 'Identifier' ? property.key.name : evaluate(property.key, scope, depth + 1)); + value[key] = evaluate(property.value, scope, depth + 1); } return value; } case 'MemberExpression': { - const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : this.evaluate(node.property, scope, depth + 1)); - return readProperty(this.evaluate(node.object, scope, depth + 1), key); + const key = propertyKey(!node.computed && node.property.type === 'Identifier' ? node.property.name : evaluate(node.property, scope, depth + 1)); + return readProperty(evaluate(node.object, scope, depth + 1), key); } case 'UnaryExpression': { - const value = this.evaluate(node.argument, scope, depth + 1); + const value = evaluate(node.argument, scope, depth + 1); if (node.operator === 'void') { return undefined; } if (node.operator === '!') { return !value; } - if (typeof value === 'number' && (node.operator === '-' || node.operator === '+')) { + if (isNumber(value) && (node.operator === '-' || node.operator === '+')) { return node.operator === '-' ? -value : value; } break; } case 'LogicalExpression': { - const left = this.evaluate(node.left, scope, depth + 1); + const left = evaluate(node.left, scope, depth + 1); if ((node.operator === '||' && left) || (node.operator === '&&' && !left) || (node.operator === '??' && left !== undefined && left !== null)) { return left; } - return this.evaluate(node.right, scope, depth + 1); + return evaluate(node.right, scope, depth + 1); } case 'AssignmentExpression': { - const { object, key } = this.reference(node.left, scope, depth + 1); + const { object, key } = reference(node.left, scope, depth + 1); try { if (node.operator !== '=') { throw new ScriptDataError('Only simple script data assignments are supported'); } - const value = this.evaluate(node.right, scope, depth + 1); - (object as DataObject)[key] = value; + const value = evaluate(node.right, scope, depth + 1); + object[key] = value; return value; } catch (error) { if (error instanceof ScriptDataError) { - (object as DataObject)[key] = error; - this.poisonedAssignments.add(node); + object[key] = error; + poisonedAssignments.add(node); } throw error; } @@ -194,27 +201,27 @@ class ScriptDataReader { case 'SequenceExpression': { let value: DataValue; for (const expression of node.expressions) { - value = this.evaluate(expression, scope, depth + 1); + value = evaluate(expression, scope, depth + 1); } return value; } case 'CallExpression': { - if (this.argumentIndex !== undefined && this.matchesCallback(node.callee)) { - this.captured = false; - this.callbackValue = undefined; - const args = node.arguments.map((argument) => this.evaluate(argument, scope, depth + 1)); - if (this.argumentIndex >= args.length) { + if (argumentIndex !== undefined && matchesCallback(node.callee)) { + captured = false; + callbackValue = undefined; + const args = node.arguments.map((argument) => evaluate(argument, scope, depth + 1)); + if (argumentIndex >= args.length) { throw new ScriptDataError('Script data callback is missing its data argument'); } - this.callbackValue = args[this.argumentIndex]; - this.captured = true; + callbackValue = args[argumentIndex]; + captured = true; return undefined; } const fn = node.callee; if ((fn.type !== 'FunctionExpression' && fn.type !== 'ArrowFunctionExpression') || fn.async || fn.generator) { break; } - const args = node.arguments.map((argument) => this.evaluate(argument, scope, depth + 1)); + const args = node.arguments.map((argument) => evaluate(argument, scope, depth + 1)); const local: Scope = { values: Object.create(null), parent: scope }; for (const [index, parameter] of fn.params.entries()) { if (parameter.type !== 'Identifier' || globalNames.has(parameter.name)) { @@ -223,28 +230,31 @@ class ScriptDataReader { const key = propertyKey(parameter.name); local.values[key] = args[index]; } - return fn.body.type === 'BlockStatement' ? this.statements(fn.body.body, local, depth + 1, true)?.value : this.evaluate(fn.body, local, depth + 1); + if (!fn.body) { + break; + } + return fn.body.type === 'BlockStatement' ? statements(fn.body.body, local, depth + 1, true)?.value : evaluate(fn.body, local, depth + 1); } default: break; } throw new ScriptDataError(`Unsupported script data expression: ${node.type}`); - } + }; - private matchesCallback(node: AnyNode): boolean { + const matchesCallback = (node: Node): boolean => { try { const path = normalizePath(staticPath(node)); - return path.length === this.target.length && path.every((key, index) => key === this.target[index]); + return path.length === target.length && path.every((key, index) => key === target[index]); } catch (error) { if (error instanceof UnsafePropertyError) { throw error; } return false; } - } + }; - private isCallbackGuard(node: AnyNode): boolean { - if (this.argumentIndex === undefined || node.type !== 'IfStatement' || node.alternate || node.test.type !== 'UnaryExpression' || node.test.operator !== '!') { + const isCallbackGuard = (node: Node): boolean => { + if (argumentIndex === undefined || node.type !== 'IfStatement' || node.alternate || node.test.type !== 'UnaryExpression' || node.test.operator !== '!') { return false; } const consequent = node.consequent.type === 'BlockStatement' && node.consequent.body.length === 1 ? node.consequent.body[0] : node.consequent; @@ -252,18 +262,18 @@ class ScriptDataReader { return false; } const path = normalizePath(staticPath(node.test.argument)); - return path.length > 0 && path.length < this.target.length && path.every((key, index) => key === this.target[index]); - } + return path.length > 0 && path.length < target.length && path.every((key, index) => key === target[index]); + }; - private referencesKnownData(node: AnyNode, scope: Scope, depth: number): boolean { - this.step(depth); + const referencesKnownData = (node: Node, scope: Scope, depth: number): boolean => { + step(depth); if (node.type === 'Identifier' || node.type === 'MemberExpression') { try { const path = staticPath(node); - let value = this.identifier(path[0], scope); + let value = identifier(path[0], scope); for (const key of path.slice(1)) { if (!isObject(value) || !Object.hasOwn(value, key)) { - return value !== this.root.values; + return value !== root.values; } value = readProperty(value, key); } @@ -277,15 +287,18 @@ class ScriptDataReader { } } } - return Object.values(node).some((value) => { + return Object.entries(node).some(([key, value]) => { + if (key === 'parent') { + return false; + } const children = Array.isArray(value) ? value : [value]; - return children.some((child) => child && typeof child === 'object' && typeof child.type === 'string' && this.referencesKnownData(child, scope, depth + 1)); + return children.some((child) => child?.type && referencesKnownData(child, scope, depth + 1)); }); - } + }; - private statements(nodes: AnyNode[], scope: Scope, depth: number, strict: boolean): { value: DataValue } | undefined { + const statements = (nodes: Node[], scope: Scope, depth: number, strict: boolean): { value: DataValue } | undefined => { for (const node of nodes) { - this.step(depth); + step(depth); try { switch (node.type) { case 'VariableDeclaration': @@ -296,10 +309,10 @@ class ScriptDataReader { const key = propertyKey(declaration.id.name); try { if (declaration.init || !Object.hasOwn(scope.values, key)) { - scope.values[key] = declaration.init ? this.evaluate(declaration.init, scope, depth + 1) : undefined; + scope.values[key] = declaration.init ? evaluate(declaration.init, scope, depth + 1) : undefined; } } catch (error) { - if (strict || !(error instanceof ScriptDataError) || error instanceof UnsafePropertyError || (declaration.init && this.referencesKnownData(declaration.init, scope, depth + 1))) { + if (strict || !(error instanceof ScriptDataError) || error instanceof UnsafePropertyError || (declaration.init && referencesKnownData(declaration.init, scope, depth + 1))) { throw error; } scope.values[key] = error; @@ -307,14 +320,14 @@ class ScriptDataReader { } break; case 'ExpressionStatement': - this.evaluate(node.expression, scope, depth + 1); + evaluate(node.expression, scope, depth + 1); break; case 'ReturnStatement': - return { value: node.argument ? this.evaluate(node.argument, scope, depth + 1) : undefined }; + return { value: node.argument ? evaluate(node.argument, scope, depth + 1) : undefined }; case 'EmptyStatement': break; default: - if (!this.isCallbackGuard(node)) { + if (!isCallbackGuard(node)) { throw new ScriptDataError(`Unsupported script data statement: ${node.type}`); } } @@ -326,17 +339,17 @@ class ScriptDataReader { // Failed simple assignments already poison their destination. Other unsupported code // must not silently leave stale data when it references a known serialized value. const isPoisonedAssignment = - node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' && this.poisonedAssignments.has(node.expression) && !this.referencesKnownData(node.expression.right, scope, depth + 1); - if (!isPoisonedAssignment && this.referencesKnownData(node, scope, depth + 1)) { + node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' && poisonedAssignments.has(node.expression) && !referencesKnownData(node.expression.right, scope, depth + 1); + if (!isPoisonedAssignment && referencesKnownData(node, scope, depth + 1)) { throw error; } } } return undefined; - } + }; - private validate(value: DataValue, ancestors = new Set(), depth = 0): void { - this.step(depth); + const validate = (value: DataValue, ancestors = new Set(), depth = 0): void => { + step(depth); if (value instanceof ScriptDataError) { throw value; } @@ -346,45 +359,47 @@ class ScriptDataReader { } ancestors.add(value); for (const child of Object.values(value)) { - this.validate(child, ancestors, depth + 1); + validate(child, ancestors, depth + 1); } ancestors.delete(value); } - } + }; - read(source: string): DataValue { - if (source.length > maxScriptLength) { - throw new ScriptDataError('Script data source exceeds the size limit'); - } - this.statements(parse(source, { ecmaVersion: 'latest' }).body, this.root, 0, false); - let value: DataValue = this.root.values; - if (this.argumentIndex === undefined) { - for (const key of this.target) { - if (!isObject(value) || !Object.hasOwn(value, key)) { - if (value instanceof ScriptDataError) { - throw value; - } - throw new ScriptDataError('Script data target was not found'); + if (source.length > maxScriptLength) { + throw new ScriptDataError('Script data source exceeds the size limit'); + } + statements((await parseProgram(source, 'Script data source could not be parsed')).body, root, 0, false); + let value: DataValue = root.values; + if (argumentIndex === undefined) { + for (const key of target) { + if (!isObject(value) || !Object.hasOwn(value, key)) { + if (value instanceof ScriptDataError) { + throw value; } - value = readProperty(value, key); - } - } else { - if (!this.captured) { - throw new ScriptDataError('Script data callback was not found or could not be parsed'); + throw new ScriptDataError('Script data target was not found'); } - value = this.callbackValue; + value = readProperty(value, key); } - this.validate(value); - return value; + } else { + if (!captured) { + throw new ScriptDataError('Script data callback was not found or could not be parsed'); + } + value = callbackValue; } -} + validate(value); + return value as T; +}; -export const parseScriptData = (source: string, target: string): T => new ScriptDataReader(targetPath(target)).read(source) as T; +export const evaluateScriptData = async (source: string, target: string): Promise => { + const path = await targetPath(target); + return readScriptData(source, path); +}; /** Extracts a serialized callback argument without invoking the callback or any external function. */ -export const parseScriptCallback = (source: string, callbackPath: string, argumentIndex = 2): T => { +export const evaluateScriptCallback = async (source: string, callbackPath: string, argumentIndex = 2): Promise => { if (!Number.isSafeInteger(argumentIndex) || argumentIndex < 0) { throw new ScriptDataError('Script data callback argument index must be a non-negative integer'); } - return new ScriptDataReader(targetPath(callbackPath), argumentIndex).read(source) as T; + const path = await targetPath(callbackPath); + return readScriptData(source, path, argumentIndex); }; diff --git a/lib/utils/evaluate-script.worker.test.ts b/lib/utils/evaluate-script.worker.test.ts new file mode 100644 index 000000000000..d17d7ed91f1d --- /dev/null +++ b/lib/utils/evaluate-script.worker.test.ts @@ -0,0 +1 @@ +import './evaluate-script.test'; diff --git a/lib/utils/parse-date-in-timezone.worker.test.ts b/lib/utils/parse-date-in-timezone.worker.test.ts new file mode 100644 index 000000000000..92365784ff5e --- /dev/null +++ b/lib/utils/parse-date-in-timezone.worker.test.ts @@ -0,0 +1 @@ +import './parse-date-in-timezone.test'; diff --git a/lib/utils/parse-js.ts b/lib/utils/parse-js.ts new file mode 100644 index 000000000000..6d2e06d588ca --- /dev/null +++ b/lib/utils/parse-js.ts @@ -0,0 +1,11 @@ +import { type OxcError, parseSync, type Program, rawTransferSupported } from 'oxc-parser'; + +export const parseScriptSource = (source: string): { program: Program; errors: OxcError[] } => { + const { program, errors } = parseSync('script.js', source, { + lang: 'js', + sourceType: 'script', + preserveParens: false, + experimentalRawTransfer: rawTransferSupported(), + } as never); + return { program, errors }; +}; diff --git a/lib/utils/parse-js.worker.ts b/lib/utils/parse-js.worker.ts new file mode 100644 index 000000000000..bf57235e1d68 --- /dev/null +++ b/lib/utils/parse-js.worker.ts @@ -0,0 +1,21 @@ +import parserWasm from '@oxc-parser/binding-wasm32-wasip1/wasm.wasm'; +import { instantiate } from '@oxc-parser/binding-wasm32-wasip1/workerd'; +import { type OxcError, type Program } from 'oxc-parser'; +import { wrap } from 'oxc-parser/src-js/wrap.js'; + +let bindingPromise: ReturnType | undefined; +/** + * @see https://github.com/oxc-project/oxc/pull/25442 still bleeding edge + * */ +export const parseScriptSource = async (source: string): Promise<{ program: Program; errors: OxcError[] }> => { + bindingPromise ??= instantiate(parserWasm); + const binding = await bindingPromise; + const { program, errors } = wrap( + binding.parseSync('script.js', source, { + lang: 'js', + sourceType: 'script', + preserveParens: false, + }) as never + ); + return { program, errors }; +}; diff --git a/lib/utils/parse-script-data.test.ts b/lib/utils/parse-script-data.test.ts deleted file mode 100644 index da79f3a03ea8..000000000000 --- a/lib/utils/parse-script-data.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; - -import { parseScriptCallback, parseScriptData } from './parse-script-data'; - -afterEach(() => { - vi.unstubAllGlobals(); -}); - -describe('serialized script data', () => { - it.each(['__DATA__', 'window.__DATA__', 'globalThis.__DATA__', 'self.__DATA__'])('normalizes the global target %s', (target) => { - expect(parseScriptData(String.raw`window.__DATA__ = { title: "Hello\u0020世界", tags: ["a", "b"] };`, target)).toEqual({ title: 'Hello 世界', tags: ['a', 'b'] }); - }); - - it('resolves variables, nested assignments, and data-only unary and logical expressions', () => { - const source = 'var title="A & B", unused=analytics(); window.pixiv=window.pixiv||{}; pixiv.novel={title, count:-2, available:!0, absent:void 0};'; - expect(parseScriptData(source, 'window.pixiv.novel')).toEqual({ title: 'A & B', count: -2, available: true, absent: undefined }); - }); - - it('reads parent-object assignments and static bracket targets', () => { - expect(parseScriptData('window.pixiv={novel:{title:"Book"}};', 'self["pixiv"].novel')).toEqual({ title: 'Book' }); - }); - - it('reads Nuxt IIFE parameters and object mutations without changing outer bindings', () => { - const source = 'var a="outer";window.__NUXT__=(function(a,b,c){a.title=b;a.tags=c;return {data:[a,a],error:null}}({},"News",["tag"]));'; - const value = parseScriptData<{ data: Array<{ title: string; tags: string[] }>; error: null }>(source, '__NUXT__'); - expect(value).toEqual({ - data: [ - { title: 'News', tags: ['tag'] }, - { title: 'News', tags: ['tag'] }, - ], - error: null, - }); - expect(value.data[0]).toBe(value.data[1]); - expect(parseScriptData(source, 'a')).toBe('outer'); - }); - - it('preserves repeated var declarations and evaluates unused IIFE arguments in order', () => { - const source = 'var a={value:1};var a;window.__DATA__=(function(x){return x})(a,a.value=2);'; - expect(parseScriptData(source, '__DATA__')).toEqual({ value: 2 }); - }); - - it('does not call unrelated application code or access the host global object', () => { - const fetchSpy = vi.fn(); - vi.stubGlobal('fetch', fetchSpy); - vi.stubGlobal('__scriptDataSideEffect', 'unchanged'); - - const value = parseScriptData('globalThis.__scriptDataSideEffect="changed";fetch("https://example.invalid/");window.__DATA__={ok:true};', '__DATA__'); - - expect(value).toEqual({ ok: true }); - expect(fetchSpy).not.toHaveBeenCalled(); - expect(Reflect.get(globalThis, '__scriptDataSideEffect')).toBe('unchanged'); - }); - - it.each([ - 'window.__DATA__ = fetch("https://example.invalid/");', - 'window.__DATA__ = Function("return 42")();', - 'window.__DATA__ = { get secret() { return fetch("https://example.invalid/"); } };', - 'window.__DATA__ = (function(){ while(true){} return {}; })();', - 'window.__DATA__ = (function(){ return {}; })(fetch("https://example.invalid/"));', - ])('rejects unsupported expressions required by the target without executing them', (source) => { - const fetchSpy = vi.fn(); - vi.stubGlobal('fetch', fetchSpy); - expect(() => parseScriptData(source, '__DATA__')).toThrow(/Unsupported|ordinary/); - expect(fetchSpy).not.toHaveBeenCalled(); - }); - - it.each(['__proto__', 'constructor', 'prototype'])('rejects the unsafe property %s', (key) => { - expect(() => parseScriptData(`window.__DATA__={"${key}":{polluted:true}};`, '__DATA__')).toThrow('Unsafe script data property'); - expect(() => parseScriptData(`var value={};value["${key}"]={polluted:true};window.__DATA__=value;`, '__DATA__')).toThrow('Unsafe script data property'); - expect(Object.hasOwn(Object.prototype, 'polluted')).toBe(false); - }); - - it.each(['window.__DATA__()', 'window[computed]', 'window.__DATA__; unrelated()', 'window.__proto__'])('rejects unsupported target paths: %s', (target) => { - expect(() => parseScriptData('window.__DATA__={};', target)).toThrow(); - }); - - it('reports missing targets and unresolved variable dependencies', () => { - expect(() => parseScriptData('unrelated();', '__DATA__')).toThrow('target was not found'); - expect(() => parseScriptData('var data=unrelated();window.__DATA__=data;', '__DATA__')).toThrow('Unsupported script data expression'); - }); - - it.each(['if(true){window.__DATA__.value=2;}', 'Object.assign(window.__DATA__,{value:2});', 'window.__DATA__.value++;', 'window.__DATA__[unknown()]={};'])( - 'rejects unsupported code that could change extracted data: %s', - (suffix) => { - expect(() => parseScriptData(`window.__DATA__={value:1};${suffix}`, '__DATA__')).toThrow('Unsupported script data'); - } - ); - - it.each(['if(true){window.__DATA__.newField=2;}', 'var ignored=Object.assign(window.__DATA__,{value:2});', 'window.ignored=Object.assign(window.__DATA__,{value:2});'])( - 'rejects mutations hidden in new properties and unused assignments: %s', - (suffix) => { - expect(() => parseScriptData(`window.__DATA__={};${suffix}`, '__DATA__')).toThrow('Unsupported script data'); - } - ); - - it('rejects cyclic data and excessively large sources or sparse arrays', () => { - expect(() => parseScriptData('var a={};a.self=a;window.__DATA__=a;', '__DATA__')).toThrow('Cyclic script data'); - expect(() => parseScriptData(' '.repeat(2_000_001), '__DATA__')).toThrow('size limit'); - expect(() => parseScriptData('window.__DATA__=(function(){var a=[];a[100000]={};return a})();', '__DATA__')).toThrow('bounded numeric indexes'); - }); -}); - -describe('serialized callback data', () => { - it('extracts a DWR callback argument without invoking the callback', () => { - const callback = vi.fn(); - vi.stubGlobal('dwr', { engine: { _remoteHandleCallback: callback } }); - const source = '(function(){if(!window.dwr)return;var s0=[],s1={};s1.post={title:"Entry"};s0[0]=s1;dwr.engine._remoteHandleCallback("493053","0",s0);})();'; - - expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback')).toEqual([{ post: { title: 'Entry' } }]); - expect(callback).not.toHaveBeenCalled(); - }); - - it('supports empty public DWR responses and explicit argument positions', () => { - const source = '//#DWR-INSERT\n//#DWR-REPLY\ndwr.engine._remoteHandleCallback("493053","0",[]);'; - expect(parseScriptCallback(source, 'window.dwr.engine._remoteHandleCallback')).toEqual([]); - expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback', 0)).toBe('493053'); - }); - - it('evaluates all callback arguments in order and does not keep an earlier successful capture after an invalid one', () => { - const source = 'var a={value:1};dwr.engine._remoteHandleCallback(a.value=2,"0",a);'; - expect(parseScriptCallback(source, 'dwr.engine._remoteHandleCallback')).toEqual({ value: 2 }); - expect(() => parseScriptCallback(`${source}dwr.engine._remoteHandleCallback("0","0",unknown());`, 'dwr.engine._remoteHandleCallback')).toThrow(); - }); - - it('rejects missing callbacks and external calls used as the data argument', () => { - const fetchSpy = vi.fn(); - vi.stubGlobal('fetch', fetchSpy); - expect(() => parseScriptCallback('unrelated();', 'dwr.engine._remoteHandleCallback')).toThrow('callback was not found'); - expect(() => parseScriptCallback('dwr.engine._remoteHandleCallback("0","0",fetch("https://example.invalid/"));', 'dwr.engine._remoteHandleCallback')).toThrow(); - expect(() => parseScriptCallback('', 'callback', -1)).toThrow('non-negative integer'); - expect(() => parseScriptCallback('dwr["engine._remoteHandleCallback"]("0","0",[]);', 'dwr.engine._remoteHandleCallback')).toThrow('callback was not found'); - expect(fetchSpy).not.toHaveBeenCalled(); - }); -}); diff --git a/tests/playwright-lifecycle.worker.test.ts b/lib/utils/playwright.worker.test.ts similarity index 93% rename from tests/playwright-lifecycle.worker.test.ts rename to lib/utils/playwright.worker.test.ts index 0fc45a53c6b6..ae759d848106 100644 --- a/tests/playwright-lifecycle.worker.test.ts +++ b/lib/utils/playwright.worker.test.ts @@ -1,18 +1,18 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { getPlaywrightPage, setBrowserBinding } from '../lib/utils/playwright.worker'; +import { getPlaywrightPage, setBrowserBinding } from './playwright.worker'; const mocks = vi.hoisted(() => ({ endpoint: undefined as string | undefined, launch: vi.fn(), newContext: vi.fn(), newPage: vi.fn(), goto: vi.fn(), close: vi.fn(), contextClose: vi.fn(), connect: vi.fn() })); vi.mock('@cloudflare/playwright', () => ({ launch: mocks.launch })); -vi.mock('../lib/utils/playwright-remote.worker', () => ({ connectRemotePlaywright: mocks.connect, setPlaywrightServiceBinding: vi.fn() })); -vi.mock('../lib/config', () => ({ +vi.mock('./playwright-remote.worker', () => ({ connectRemotePlaywright: mocks.connect, setPlaywrightServiceBinding: vi.fn() })); +vi.mock('@/config', () => ({ config: { get playwrightWSEndpoint() { return mocks.endpoint; }, }, })); -vi.mock('../lib/utils/logger', () => ({ default: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() } })); +vi.mock('./logger', () => ({ default: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() } })); beforeEach(() => { vi.useFakeTimers(); diff --git a/package.json b/package.json index fe60aae7bed2..64154c2331b1 100644 --- a/package.json +++ b/package.json @@ -76,10 +76,10 @@ "@opentelemetry/sdk-metrics": "2.11.0", "@opentelemetry/sdk-trace-base": "2.11.0", "@opentelemetry/semantic-conventions": "1.43.0", + "@oxc-parser/binding-wasm32-wasip1": "0.0.1", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.12.1", "@sentry/node": "10.74.0", - "acorn": "8.18.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", @@ -114,6 +114,7 @@ "oauth-1.0a": "2.2.6", "ofetch": "1.5.1", "otplib": "13.5.0", + "oxc-parser": "0.149.0", "p-map": "7.0.7", "pac-proxy-agent": "9.1.0", "patchright": "1.63.0", @@ -190,7 +191,6 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxc-parser": "0.149.0", "oxfmt": "0.67.0", "oxlint": "1.82.0", "oxlint-plugin-eslint": "1.82.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d14ae4be2e4a..5225b9a3005f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -82,6 +82,9 @@ importers: '@opentelemetry/semantic-conventions': specifier: 1.43.0 version: 1.43.0 + '@oxc-parser/binding-wasm32-wasip1': + specifier: 0.0.1 + version: 0.0.1 '@rss3/sdk': specifier: 0.0.25 version: 0.0.25 @@ -91,9 +94,6 @@ importers: '@sentry/node': specifier: 10.74.0 version: 10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) - acorn: - specifier: 8.18.0 - version: 8.18.0 cheerio: specifier: 1.2.0 version: 1.2.0 @@ -196,6 +196,9 @@ importers: otplib: specifier: 13.5.0 version: 13.5.0 + oxc-parser: + specifier: 0.149.0 + version: 0.149.0 p-map: specifier: 7.0.7 version: 7.0.7 @@ -419,9 +422,6 @@ importers: node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) - oxc-parser: - specifier: 0.149.0 - version: 0.149.0 oxfmt: specifier: 0.67.0 version: 0.67.0 @@ -687,15 +687,24 @@ packages: '@emnapi/core@1.11.2': resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/core@2.0.0-alpha.3': + resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} + '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} '@emnapi/runtime@1.11.3': resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/runtime@2.0.0-alpha.3': + resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} + '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@emnapi/wasi-threads@2.0.1': + resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} + '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -1601,6 +1610,13 @@ packages: '@emnapi/core': ^2.0.0-alpha.3 '@emnapi/runtime': ^2.0.0-alpha.3 + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} @@ -1906,6 +1922,10 @@ packages: cpu: [arm64] os: [openharmony] + '@oxc-parser/binding-wasm32-wasip1@0.0.1': + resolution: {integrity: sha512-p2X0okYbIkav6eB5zwwVc6eFRycAO4GbGpBCf9IJCI2fUzwoxysWaMXIyZzrq6Pg4/LsEqFb0Vp6TKovK541vA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + '@oxc-parser/binding-win32-arm64-msvc@0.149.0': resolution: {integrity: sha512-9hMvs3GZESAcbUGU3mqXFWRLgBd8TLeosNYfHusHp/hrBnk5lIP2nJtark3hZ6qT+lqSdS79sH8NoWHj1x9iUw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2724,8 +2744,8 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/aws-lambda@8.10.163': - resolution: {integrity: sha512-+4zuoEB3S8RIhimtOFT7zAEk2SbpwrKjjGl9CyYnQR6k08uynxfauIIB0pDU1ssr05F8oyGiETwpn+8eXZMqPw==} + '@types/aws-lambda@8.10.162': + resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} '@types/bluebird@3.5.42': resolution: {integrity: sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==} @@ -3274,8 +3294,8 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@3.0.2: - resolution: {integrity: sha512-mFdDM6WqWKraGLsVb+C9CahPnzTXOefAOLq3jYcca2YZ8bEWpr++Tzj+zSaKW9+X9L5uSxcm1AZ3Y6aZJ09OhQ==} + argparse@3.0.0: + resolution: {integrity: sha512-BOp5NMrHqKxmq/OLr+clzzrRxgOKSLkcjmkWuChp7Irqwn4s74WjOBPIgWfA/HMcBnVkZ5XEuf9uUqzlpfCQ6A==} asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -3818,9 +3838,6 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - es-module-lexer@3.0.2: - resolution: {integrity: sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==} - es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} engines: {node: '>=0.10'} @@ -4344,8 +4361,8 @@ packages: resolution: {integrity: sha512-jrWWecWFifN3Feum2MqaTUupNeYSAdPEltKuwGdr8IPdxW72NZXf7ObVUfTWoUH2MPrli0q7SZm9pRt8ob/9XQ==} engines: {node: '>=20.0.0'} - import-in-the-middle@3.5.0: - resolution: {integrity: sha512-O6wo8l0lhBuytKz3x8S65TvacFtoopji+gCVeEBbjCaRy75gkk4bCBQoVmY7OEZ63cbFK+yf77pDCTWaadV7hQ==} + import-in-the-middle@3.4.0: + resolution: {integrity: sha512-Xfjwfarhe+LGmoaof+sexeNo3sGRysb5x56WrZLwHtmqT0OilSKtVWkv0lrCcMgSKyKP9oBogBAisHZvtJH0aw==} engines: {node: '>=18'} import-without-cache@0.4.0: @@ -4970,11 +4987,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.19: - resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5775,8 +5787,8 @@ packages: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true - tldts-core@7.4.12: - resolution: {integrity: sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==} + tldts-core@7.4.13: + resolution: {integrity: sha512-mbYsrih5FRtGxs3Usvl/PqwJsNpp+jsmrdFviiK02teHDG0/HebBG/pqCylje3kzgXYzuLoHJF/0mz9W53t8Xg==} tldts@7.4.12: resolution: {integrity: sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==} @@ -5920,10 +5932,6 @@ packages: resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} engines: {node: '>=20'} - type-fest@5.9.0: - resolution: {integrity: sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==} - engines: {node: '>=20'} - type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -5955,18 +5963,14 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@6.28.1: - resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} engines: {node: '>=18.17'} undici@7.29.0: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} - undici@7.29.1: - resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} - engines: {node: '>=20.18.1'} - undici@8.10.2: resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} engines: {node: '>=22.19.0'} @@ -6413,17 +6417,17 @@ snapshots: '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 - undici: 6.28.1 + undici: 6.28.0 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.28.1 + undici: 6.28.0 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.28.1 + undici: 6.28.0 '@actions/io@3.0.2': {} @@ -6610,6 +6614,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@2.0.0-alpha.3': + dependencies: + '@emnapi/wasi-threads': 2.0.1 + tslib: 2.8.1 + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 @@ -6620,11 +6629,19 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@2.0.0-alpha.3': + dependencies: + tslib: 2.8.1 + '@emnapi/wasi-threads@1.2.2': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@2.0.1': + dependencies: + tslib: 2.8.1 + '@epic-web/invariant@1.0.0': {} '@esbuild/aix-ppc64@0.27.7': @@ -6923,7 +6940,7 @@ snapshots: '@honeybadger-io/js@6.16.3': dependencies: '@honeybadger-io/core': 6.11.1 - '@types/aws-lambda': 8.10.163 + '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 '@hono/node-server@2.1.1(hono@4.13.7)': @@ -7221,6 +7238,12 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)': + dependencies: + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@tybys/wasm-util': 0.10.3 + '@noble/ciphers@1.3.0': {} '@noble/curves@1.9.7': @@ -7342,7 +7365,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 - import-in-the-middle: 3.5.0 + import-in-the-middle: 3.4.0 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -7477,6 +7500,12 @@ snapshots: '@oxc-parser/binding-openharmony-arm64@0.149.0': optional: true + '@oxc-parser/binding-wasm32-wasip1@0.0.1': + dependencies: + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) + '@oxc-parser/binding-win32-arm64-msvc@0.149.0': optional: true @@ -7905,7 +7934,7 @@ snapshots: dependencies: '@scalar/helpers': 0.11.3 nanoid: 5.1.16 - type-fest: 5.9.0 + type-fest: 5.8.0 zod: 4.6.2 '@scalar/validation@0.6.3': {} @@ -7931,7 +7960,7 @@ snapshots: '@sentry/conventions': 0.16.0 '@sentry/core': 10.74.0 '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - import-in-the-middle: 3.5.0 + import-in-the-middle: 3.4.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) @@ -7949,7 +7978,7 @@ snapshots: '@sentry/node-core': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) '@sentry/server-utils': 10.74.0 - import-in-the-middle: 3.5.0 + import-in-the-middle: 3.4.0 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' @@ -7994,9 +8023,8 @@ snapshots: '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 - optional: true - '@types/aws-lambda@8.10.163': {} + '@types/aws-lambda@8.10.162': {} '@types/bluebird@3.5.42': {} @@ -8488,7 +8516,7 @@ snapshots: arg@5.0.2: {} - argparse@3.0.2: {} + argparse@3.0.0: {} asn1@0.2.6: dependencies: @@ -8656,7 +8684,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.29.1 + undici: 7.29.0 whatwg-mimetype: 4.0.0 chownr@3.0.0: {} @@ -8961,8 +8989,6 @@ snapshots: es-module-lexer@2.3.2: {} - es-module-lexer@3.0.2: {} - es5-ext@0.10.64: dependencies: es6-iterator: 2.0.3 @@ -9680,10 +9706,10 @@ snapshots: pino: 10.3.1 socks: 2.8.10 - import-in-the-middle@3.5.0: + import-in-the-middle@3.4.0: dependencies: cjs-module-lexer: 2.2.1 - es-module-lexer: 3.0.2 + es-module-lexer: 2.3.2 module-details-from-path: 1.0.4 import-without-cache@0.4.0: {} @@ -10070,7 +10096,7 @@ snapshots: markdown-it@15.0.2: dependencies: - argparse: 3.0.2 + argparse: 3.0.0 entities: 8.1.0 linkify-it: 6.1.0 mdurl: 2.1.0 @@ -10475,8 +10501,6 @@ snapshots: nanoid@3.3.18: {} - nanoid@3.3.19: {} - nanoid@5.1.16: {} narou@2.0.1: {} @@ -10844,7 +10868,7 @@ snapshots: postcss@8.5.28: dependencies: - nanoid: 3.3.19 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -11423,11 +11447,11 @@ snapshots: tlds@1.261.0: {} - tldts-core@7.4.12: {} + tldts-core@7.4.13: {} tldts@7.4.12: dependencies: - tldts-core: 7.4.12 + tldts-core: 7.4.13 to-no-case@1.0.2: {} @@ -11536,10 +11560,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-fest@5.9.0: - dependencies: - tagged-tag: 1.0.0 - type@2.7.3: {} typescript@6.0.3: {} @@ -11582,12 +11602,10 @@ snapshots: undici-types@8.9.0: {} - undici@6.28.1: {} + undici@6.28.0: {} undici@7.29.0: {} - undici@7.29.1: {} - undici@8.10.2: {} unenv@2.0.0-rc.24: diff --git a/scripts/workflow/check-orphan-files.ts b/scripts/workflow/check-orphan-files.ts index 54917e936952..ea5d85c6ee39 100644 --- a/scripts/workflow/check-orphan-files.ts +++ b/scripts/workflow/check-orphan-files.ts @@ -22,7 +22,7 @@ export const findOrphanFiles = async (): Promise => { return relative; } const dir = path.dirname(absolute); - const base = path.basename(absolute).replace(/\.(?:spec|test)\.[cm]?[jt]sx?$/, ''); + const base = path.basename(absolute).replace(/(?:\.worker)?\.(?:spec|test)\.[cm]?[jt]sx?$/, ''); const exists = entries.some((entry) => entry.parentPath === dir && entry.isFile() && new RegExp(`^${base.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)}\\.[cm]?[jt]sx?$`).test(entry.name)); return exists ? null : relative; }) diff --git a/tests/lofter-tag.test.ts b/tests/lofter-tag.test.ts deleted file mode 100644 index a3308d1c1fa7..000000000000 --- a/tests/lofter-tag.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { route } from '../lib/routes/lofter/tag'; -import got from '../lib/utils/got'; - -vi.mock('../lib/config', () => ({ config: { lofter: { cookies: 'test-cookie' } } })); -vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); - -beforeEach(() => { - vi.clearAllMocks(); -}); - -const context = { req: { param: (name: string) => ({ name: 'Photography', type: 'date' })[name] } }; - -describe('Lofter tag DWR response', () => { - it('reads DWR object references and preserves feed content without executing the response', async () => { - // A representative DWR protocol response; no private feed data is included. - vi.mocked(got).mockResolvedValue({ - data: `//#DWR-INSERT -//#DWR-REPLY -var s0=[],s1={},s2={},s3={}; -s0[0]=s1;s1.post=s2;s2.blogInfo=s3; -s3.blogNickName="Photographer"; -s2.blogPageUrl="https://example.lofter.com/post/1"; -s2.title=""; -s2.digest="

    A & B

    "; -s2.publishTime=1704067200000; -s2.tagList=["Photography"]; -s2.photoLinks='[{"orign":"https://example.com/photo.jpg"}]'; -s2.embed='{"h256Url":"https://example.com/video.mp4","video_img_url":"https://example.com/poster.jpg"}'; -dwr.engine._remoteHandleCallback('493053','0',s0);`, - }); - - const feed = await (route.handler as (ctx: typeof context) => Promise)(context); - - expect(feed.title).toBe('Photography - 日榜 | LOFTER'); - expect(feed.item).toEqual([ - { - author: 'Photographer', - link: 'https://example.lofter.com/post/1', - title: 'Photographer:A & B', - pubDate: new Date('2024-01-01T00:00:00.000Z'), - description: - '

    A & B

    ', - category: ['Photography'], - }, - ]); - expect(got).toHaveBeenCalledWith(expect.objectContaining({ headers: { Referer: 'https://www.lofter.com/tag/Photography', Cookie: 'test-cookie' } })); - }); - - it('reads the empty callback response returned by the public endpoint', async () => { - vi.mocked(got).mockResolvedValue({ data: "//#DWR-INSERT\n//#DWR-REPLY\ndwr.engine._remoteHandleCallback('493053','0',[]);" }); - - const feed = await (route.handler as (ctx: typeof context) => Promise)(context); - expect(feed.item).toEqual([]); - }); - - it('rejects executable callback arguments', async () => { - vi.mocked(got).mockResolvedValue({ data: "dwr.engine._remoteHandleCallback('493053','0',fetch('https://example.com'));" }); - - await expect((route.handler as (ctx: typeof context) => Promise)(context)).rejects.toThrow(); - }); -}); diff --git a/tests/nuxt-routes.test.ts b/tests/nuxt-routes.test.ts deleted file mode 100644 index 676e1612d912..000000000000 --- a/tests/nuxt-routes.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { processFeed, processFeedType2 } from '../lib/routes/dongqiudi/utils'; -import nintendo from '../lib/routes/nintendo/utils'; -import cache from '../lib/utils/cache'; -import got from '../lib/utils/got'; - -vi.mock('../lib/utils/cache', () => ({ - default: { tryGet: vi.fn((_key, getValue) => getValue()) }, -})); -vi.mock('../lib/utils/got', () => ({ default: vi.fn() })); - -beforeEach(() => { - vi.clearAllMocks(); -}); - -describe('Nintendo Nuxt data', () => { - it('reads serialized object references and escaped content without executing page scripts', () => { - const html = ` - `; - - expect(nintendo.nuxtReader(html)).toEqual({ - newsList: [{ title: 'News 中文', content: '

    A&B

    ' }], - newsData: { title: 'News 中文', content: '

    A&B

    ' }, - }); - }); - - it('preserves cached article content, category, and publication time', async () => { - const link = 'https://www.nintendoswitch.com.cn/topics/test'; - vi.mocked(got).mockResolvedValue({ - data: '', - }); - - const items = await nintendo.ProcessNewsChina([{ link, description: '' }], cache); - - expect(cache.tryGet).toHaveBeenCalledWith(link, expect.any(Function)); - expect(items[0]).toEqual({ - link, - description: '

    Article

    ', - category: ['News'], - pubDate: new Date('2024-01-01T00:00:00.000Z'), - }); - }); - - it.each(['No page data', ''])('rejects missing or executable page data', (html) => { - expect(() => nintendo.nuxtReader(html)).toThrow('Nuxt 框架信息提取失败'); - }); -}); - -describe('Dongqiudi Nuxt data', () => { - const articleHtml = ` - `; - - it('preserves article content cleanup and the author', () => { - const item: { title: string; description?: string; author?: string } = { title: 'Match' }; - - processFeedType2(item, articleHtml); - - expect(item).toEqual({ - title: 'Match', - description: '

    Match report

    Related', - author: 'Reporter', - }); - }); - - it.each(['No Nuxt data', '', ''])('leaves an item unchanged when article data is absent', (html) => { - const item = { title: 'Match' }; - processFeedType2(item, html); - expect(item).toEqual({ title: 'Match' }); - }); - - it.each([ - ['team', 'teamInfo:{name:"Club",logo:"club.png"}', 'Club', 'club.png'], - ['player', 'detail:{base_info:{person_name:"Player",person_logo:"player.png"}}', 'Player', 'player.png'], - ])('keeps the %s metadata, API request, and article cache', async (type, metadata, name, image) => { - vi.mocked(got) - .mockResolvedValueOnce({ data: `` }) - .mockResolvedValueOnce({ data: { data: { articles: [{ id: 42, title: 'Match', category: 'Football', show_time: 1_704_067_200 }] } } }) - .mockResolvedValueOnce({ data: articleHtml }); - - const feed = await processFeed(type, '123'); - - expect(feed.title).toBe(`${name} - 相关新闻`); - expect(feed.image).toBe(image); - expect(feed.item).toHaveLength(1); - expect(feed.item[0]).toMatchObject({ author: 'Reporter', category: ['Football'], pubDate: new Date('2024-01-01T00:00:00.000Z') }); - expect(cache.tryGet).toHaveBeenCalledWith('https://www.dongqiudi.com/articles/42.html', expect.any(Function)); - expect(got).toHaveBeenNthCalledWith(2, 'https://api.dongqiudi.com/v3/archive/app/channel/feeds', { - searchParams: { id: '123', type, size: 20, platform: 'web', version: '' }, - }); - }); -}); diff --git a/tests/page-script-data-routes.test.ts b/tests/page-script-data-routes.test.ts deleted file mode 100644 index 5fe5a9d9ebc5..000000000000 --- a/tests/page-script-data-routes.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -import type { Context } from 'hono'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -import { route as hotukdealsRoute } from '../lib/routes/hotukdeals/hottest'; -import { getNSFWNovelContent } from '../lib/routes/pixiv/novel-api/content/nsfw'; -import { parseNovelContent } from '../lib/routes/pixiv/novel-api/content/utils'; -import playCache from '../lib/routes/qq/kg/cache'; -import { route as qqRoute } from '../lib/routes/qq/kg/user'; -import { route as xueqiuRoute } from '../lib/routes/xueqiu/column'; -import type { Data, Route } from '../lib/types'; - -const mocks = vi.hoisted(() => ({ - request: Object.assign(vi.fn(), { get: vi.fn() }), - pixivRequest: vi.fn(), - tryGet: vi.fn(), -})); - -vi.mock('../lib/utils/got', () => ({ default: mocks.request })); -vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); -vi.mock('../lib/routes/pixiv/pixiv-got', () => ({ default: mocks.pixivRequest })); -vi.mock('../lib/routes/pixiv/novel-api/content/utils', () => ({ parseNovelContent: vi.fn() })); - -const invoke = async (route: Route, params: Record = {}) => (await route.handler({ req: { param: (name: string) => params[name] } } as unknown as Context)) as Data; -const page = (source: string) => ``; - -// QQ and Xueqiu use the JSON-assignment format observed in public responses on -// 2026-09-06. Keep only the consumed fields and replace public account data. -const playDetail = { - song_name: 'Song & title', - content: 'Line one
    Line two & three', - nick: 'Singer', - cover: 'https://example.com/cover.jpg', - playurl: 'https://example.com/song.m4a', - ksong_mid: 'song-1', - ctime: 1_700_000_000, - comments: [{ nick: 'Listener', content: 'Nice song', ctime: 1_700_000_001, comment_id: 'comment-1' }], -}; - -// Pixiv requires authentication and HotUKDeals returned 403 during inspection. -// Their fixtures cover the existing consumed-data contract, not live success. -const novelDetail = { - id: '42', - title: 'Novel title', - caption: '

    Caption & summary

    ', - text: 'A line\n[uploadedimage:7]', - userId: 'author-1', - rating: { bookmark: 3, view: 12, like: 2 }, - cdate: '2026-09-01T10:00:00+09:00', - isOriginal: true, - aiType: 0, - tags: ['fiction'], - coverUrl: 'https://example.com/novel.jpg', - images: { '7': { urls: { original: 'https://example.com/image.jpg' } }, '8': {} }, - seriesId: 'series-1', - seriesTitle: 'Series title', -}; - -beforeEach(() => { - vi.resetAllMocks(); - const entries = new Map(); - mocks.tryGet.mockImplementation(async (key, getValue) => { - if (!entries.has(key)) { - entries.set(key, await getValue()); - } - return entries.get(key); - }); - mocks.request.mockRejectedValue(new Error('Unexpected upstream request')); - mocks.request.get.mockRejectedValue(new Error('Unexpected upstream request')); - mocks.pixivRequest.mockRejectedValue(new Error('Unexpected Pixiv request')); - vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('External network access is disabled'))); -}); - -afterEach(() => { - vi.unstubAllGlobals(); -}); - -describe('routes that parse embedded page data', () => { - it('preserves QQ user feed metadata, audio attachments and play-detail cache keys', async () => { - const userData = { - data: { - nickname: 'Singer', - head_img_url: 'https://example.com/avatar.jpg', - ugclist: [{ shareid: 'share-1', ksong_mid: 'song-1', title: 'Song & title', ctime: 1_600_000_000, avatar: 'https://example.com/fallback.jpg' }], - }, - share: { content: 'Singer profile' }, - }; - mocks.request.mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify(userData)};`) }).mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify({ detail: playDetail })};`) }); - - const result = await invoke(qqRoute, { userId: 'singer-1' }); - - expect(result).toMatchObject({ - title: 'Singer - 全民K歌', - link: 'https://node.kg.qq.com/personal?uid=singer-1', - description: 'Singer profile', - image: 'https://example.com/avatar.jpg', - allowEmpty: true, - itunes_author: 'Singer', - itunes_category: '全民K歌', - item: [ - { - title: 'Song & title', - description: playDetail.content, - link: 'https://node.kg.qq.com/play?s=share-1', - guid: 'ksong:song-1', - author: 'Singer', - pubDate: new Date(1_700_000_000_000), - itunes_item_image: playDetail.cover, - enclosure_url: playDetail.playurl, - enclosure_type: 'audio/x-m4a', - }, - ], - }); - expect(mocks.tryGet).toHaveBeenCalledWith('ksong:song-1', expect.any(Function)); - expect(mocks.request.mock.calls.map(([url]) => url)).toEqual(['https://node.kg.qq.com/personal?uid=singer-1', 'https://node.kg.qq.com/play?s=share-1']); - }); - - it('preserves QQ comments and reuses a play-detail cache entry', async () => { - mocks.request.mockResolvedValueOnce({ data: page(`window.__DATA__ = ${JSON.stringify({ detail: playDetail })};`) }); - - const first = await playCache.getPlayInfo(undefined, 'share-1', 'song-1'); - const second = await playCache.getPlayInfo(undefined, 'share-1', 'song-1'); - - expect(first).toMatchObject({ name: playDetail.song_name, description: playDetail.content, author: 'Singer', ksong_mid: 'song-1', comments: playDetail.comments }); - expect(second).toBe(first); - expect(mocks.request).toHaveBeenCalledExactlyOnceWith('https://node.kg.qq.com/play?s=share-1'); - }); - - it('preserves the Xueqiu cookie jar, first-page request and author data', async () => { - mocks.request - .mockResolvedValueOnce({ data: '' }) - .mockResolvedValueOnce({ data: page('window.SNOWMAN_TARGET = {"screen_name":"Author & name","description":"A profile & text"};') }) - .mockResolvedValueOnce({ data: { list: [{ title: 'Article', description: '

    Body

    ', created_at: 1_700_000_000_000, target: '/author/123' }] } }); - - const result = await invoke(xueqiuRoute, { id: 'author-1' }); - - expect(result).toEqual({ - title: 'Author & name - 雪球', - link: 'https://xueqiu.com/author-1/column', - description: 'A profile & text', - item: [{ title: 'Article', description: '

    Body

    ', pubDate: new Date(1_700_000_000_000), link: 'https://xueqiu.com/author/123', author: 'Author & name' }], - }); - const cookieJar = mocks.request.mock.calls[0][1].cookieJar; - expect(mocks.request).toHaveBeenNthCalledWith(2, 'https://xueqiu.com/author-1/column', { cookieJar }); - expect(mocks.request).toHaveBeenNthCalledWith(3, 'https://xueqiu.com/statuses/original/timeline.json', { cookieJar, searchParams: { user_id: 'author-1', page: 1 } }); - }); - - it('preserves HotUKDeals titles, image URLs, prices and temperatures', async () => { - const state = { widgets: { hottestWidget: { threads: [{ title: 'A & B deal', mainImage: { path: 'images', name: 'deal' }, temperature: 123, displayPrice: '£12.99', url: 'https://example.com/deal' }] } } }; - mocks.request.get.mockResolvedValueOnce({ data: page(`window.__INITIAL_STATE__ = ${JSON.stringify(state)};`) }); - - expect(await invoke(hotukdealsRoute)).toEqual({ - title: 'hotukdeals hottest', - link: 'https://www.hotukdeals.com/', - item: [{ title: 'A & B deal', description: '
    123° A & B deal
    £12.99', link: 'https://example.com/deal' }], - }); - expect(mocks.request.get).toHaveBeenCalledExactlyOnceWith('https://www.hotukdeals.com/'); - }); - - it.each(['parent object', 'nested assignment'])('preserves authenticated Pixiv novel data from a %s', async (form) => { - const source = form === 'parent object' ? `window.pixiv = {novel: ${JSON.stringify(novelDetail)}};` : `window.pixiv = {};'); - mocks.content.mockResolvedValue('
    Thread content
    '); - mocks.getPlaywrightPage.mockResolvedValue({ - page: { goto: mocks.goto, route: mocks.route, waitForSelector: mocks.waitForSelector, content: mocks.content }, - context: { addCookies: mocks.addCookies }, - destroy: mocks.destroy, - }); -}); - -describe('Yamibo browser fallback', () => { - it('keeps ordinary HTML requests free of browser work', async () => { - mocks.ofetch.mockResolvedValue('
    Ordinary HTML
    '); - const result = await fetchThread('123', { ordertype: '1' }); - expect(result).toEqual({ link: 'https://bbs.yamibo.com/forum.php?mod=viewthread&tid=123&ordertype=1', data: '
    Ordinary HTML
    ' }); - expect(mocks.ofetch).toHaveBeenCalledWith(result.link, { headers: { cookie: 'EeqY_2132_saltkey=test-salt; EeqY_2132_auth=test-auth' } }); - expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); - }); - - it('reuses one browser and serializes navigation for concurrent article requests', async () => { - const fetcher = new ThreadFetcher(); - let active = 0; - let maximum = 0; - mocks.goto.mockImplementation(async () => { - active++; - maximum = Math.max(maximum, active); - await Promise.resolve(); - active--; - }); - try { - const results = await Promise.all([fetcher.fetchThread('123'), fetcher.fetchThread('456')]); - expect(results.map((result) => result.data)).toEqual(Array.from({ length: 2 }, () => '
    Thread content
    ')); - expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); - expect(mocks.goto).toHaveBeenCalledTimes(2); - expect(maximum).toBe(1); - expect(mocks.addCookies).toHaveBeenCalledWith([ - { name: 'EeqY_2132_saltkey', value: 'test-salt', url: 'https://bbs.yamibo.com' }, - { name: 'EeqY_2132_auth', value: 'test-auth', url: 'https://bbs.yamibo.com' }, - ]); - } finally { - await fetcher.close(); - } - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('closes the browser when the expected thread content never appears', async () => { - mocks.waitForSelector.mockRejectedValue(new Error('Page timeout')); - await expect(fetchThread('123')).rejects.toThrow('browser access did not return thread content'); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); -}); diff --git a/tests/zhihu-browser.test.ts b/tests/zhihu-browser.test.ts deleted file mode 100644 index 3eaf8fcbadc4..000000000000 --- a/tests/zhihu-browser.test.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -const mocks = vi.hoisted(() => ({ - cookies: '', - ofetch: vi.fn(), - getPlaywrightPage: vi.fn(), - destroy: vi.fn(), - addCookies: vi.fn(), - browserCookies: vi.fn(), - goto: vi.fn(), - route: vi.fn(), - waitForFunction: vi.fn(), - evaluate: vi.fn(), - content: vi.fn(), - url: vi.fn(), -})); - -vi.mock('../lib/config', () => ({ - config: { - zhihu: { - get cookies() { - return mocks.cookies; - }, - }, - }, -})); -vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); -vi.mock('../lib/utils/is-worker', () => ({ isWorker: true })); -vi.mock('../lib/utils/playwright', () => ({ getPlaywrightPage: mocks.getPlaywrightPage })); - -const origin = 'https://www.zhihu.com'; -const pageUrl = `${origin}/people/example`; -const apiPath = '/api/v4/members/example/activities?limit=20'; -const columnPageUrl = 'https://zhuanlan.zhihu.com/example'; -const columnApiPath = '/api/v4/columns/example/items'; -const feedData = { data: [{ id: 'api-item' }] }; -const pageHtml = 'Profile content'; -const nativeFetch = vi.fn(); -const navigationResponse = (contentType = 'text/html', status = 200, body = pageHtml) => ({ - headers: () => ({ 'content-type': contentType }), - status: () => status, - text: () => Promise.resolve(body), -}); - -beforeEach(() => { - vi.resetModules(); - vi.resetAllMocks(); - mocks.cookies = ''; - mocks.goto.mockImplementation((url) => { - mocks.url.mockReturnValue(url); - return Promise.resolve(navigationResponse()); - }); - mocks.waitForFunction.mockResolvedValue(undefined); - mocks.browserCookies.mockResolvedValue([ - { name: 'd_c0', value: 'browser-dc0' }, - { name: '__zse_ck', value: 'browser-zse' }, - ]); - mocks.evaluate.mockImplementation((callback, argument) => callback(argument)); - mocks.content.mockResolvedValue(pageHtml); - mocks.url.mockReturnValue(pageUrl); - mocks.getPlaywrightPage.mockResolvedValue({ - page: { goto: mocks.goto, route: mocks.route, waitForFunction: mocks.waitForFunction, evaluate: mocks.evaluate, content: mocks.content, url: mocks.url }, - context: { addCookies: mocks.addCookies, cookies: mocks.browserCookies }, - destroy: mocks.destroy, - }); - nativeFetch.mockImplementation(() => Promise.resolve(Response.json(feedData))); - vi.stubGlobal('fetch', nativeFetch); - vi.stubGlobal('window', { AbortSignal }); -}); - -afterEach(() => { - vi.unstubAllGlobals(); - vi.restoreAllMocks(); -}); - -describe('Zhihu client session', () => { - it('uses complete configured credentials for API and HTML requests without starting a browser', async () => { - mocks.cookies = 'd_c0=configured==; __zse_ck=existing; z_c0=login=value'; - mocks.ofetch.mockResolvedValueOnce(feedData).mockResolvedValueOnce(pageHtml); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - const result = await withZhihuClient(pageUrl, async (client) => ({ data: await client.get(apiPath), html: await client.getPage() })); - - expect(result).toEqual({ data: feedData, html: pageHtml }); - expect(mocks.ofetch).toHaveBeenNthCalledWith(1, `${origin}${apiPath}`, { - headers: expect.objectContaining({ cookie: mocks.cookies, Referer: pageUrl, 'x-zse-96': expect.stringMatching(/^2\.0_/) }), - }); - expect(mocks.ofetch).toHaveBeenNthCalledWith(2, pageUrl, { - headers: { cookie: mocks.cookies, Referer: pageUrl }, - parseResponse: expect.any(Function), - }); - expect(mocks.ofetch.mock.calls[1][1].parseResponse(pageHtml)).toBe(pageHtml); - expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); - expect(nativeFetch).not.toHaveBeenCalled(); - }); - - it.each(['', 'z_c0=isolated-login', 'd_c0=; z_c0=isolated-login'])('initializes a guest session before the target without transferring an unrelated login: %s', async (cookies) => { - mocks.cookies = cookies; - mocks.browserCookies.mockResolvedValueOnce([{ name: 'd_c0', value: 'browser-dc0' }]); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); - - expect(mocks.addCookies).not.toHaveBeenCalled(); - expect(mocks.goto.mock.calls).toEqual([ - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [pageUrl, { waitUntil: 'domcontentloaded' }], - ]); - expect(mocks.browserCookies).toHaveBeenCalledTimes(3); - expect(mocks.browserCookies).toHaveBeenCalledWith(origin); - expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(pageUrl, { noGoto: true, closeTimeout: 0 }); - expect(mocks.destroy).toHaveBeenCalledOnce(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - }); - - it('keeps a configured identity and login intact and requests the API through the browser native fetch', async () => { - mocks.cookies = 'd_c0=seed==;z_c0=login=value'; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - expect(mocks.addCookies).toHaveBeenCalledWith([ - { name: 'd_c0', value: 'seed==', domain: '.zhihu.com', path: '/' }, - { name: 'z_c0', value: 'login=value', domain: '.zhihu.com', path: '/' }, - ]); - expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(pageUrl, { waitUntil: 'domcontentloaded' }); - expect(mocks.evaluate).toHaveBeenCalledOnce(); - expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, { - headers: expect.objectContaining({ 'x-zse-96': expect.stringMatching(/^2\.0_/) }), - credentials: 'include', - signal: expect.any(AbortSignal), - }); - const headers = new Headers(nativeFetch.mock.calls[0][1]?.headers); - expect(headers.has('cookie')).toBe(false); - expect(headers.has('user-agent')).toBe(false); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - const handler = mocks.route.mock.calls[0][1]; - await Promise.all( - ['document', 'script', 'xhr', 'fetch', 'image'].map(async (resourceType) => { - const continueRequest = vi.fn(); - const abortRequest = vi.fn(); - await handler({ request: () => ({ resourceType: () => resourceType }), continue: continueRequest, abort: abortRequest }); - expect(continueRequest).toHaveBeenCalledTimes(resourceType === 'image' ? 0 : 1); - expect(abortRequest).toHaveBeenCalledTimes(resourceType === 'image' ? 1 : 0); - }) - ); - }); - - it('reuses one browser for API and HTML reads and signs each API request with its current identity', async () => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - const signature = vi.spyOn(await import('../lib/routes/zhihu/sign'), 'getSignedHeaders'); - mocks.browserCookies - .mockResolvedValueOnce([{ name: 'd_c0', value: 'guest-dc0' }]) - .mockResolvedValueOnce([ - { name: 'd_c0', value: 'guest-dc0' }, - { name: '__zse_ck', value: 'browser-zse' }, - ]) - .mockResolvedValueOnce([{ name: 'd_c0', value: 'first-dc0' }]) - .mockResolvedValueOnce([{ name: 'd_c0', value: 'updated-dc0' }]); - - const html = await withZhihuClient(pageUrl, async (client) => { - expect(await client.get(apiPath)).toEqual(feedData); - expect(await client.get('/api/v4/members/example')).toEqual(feedData); - expect(mocks.destroy).not.toHaveBeenCalled(); - return client.getPage(); - }); - - expect(html).toBe(pageHtml); - expect(signature).toHaveBeenNthCalledWith(1, apiPath, 'first-dc0'); - expect(signature).toHaveBeenNthCalledWith(2, '/api/v4/members/example', 'updated-dc0'); - expect(mocks.getPlaywrightPage).toHaveBeenCalledOnce(); - expect(mocks.goto).toHaveBeenLastCalledWith(pageUrl, { waitUntil: 'domcontentloaded' }); - expect(mocks.content).toHaveBeenCalledOnce(); - expect(nativeFetch).toHaveBeenCalledTimes(2); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('requests the signed API after an initial JSON error without waiting for an unnecessary session cookie', async () => { - mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); - mocks.goto.mockImplementation((url) => { - mocks.url.mockReturnValue(url); - return Promise.resolve(url === `${origin}${columnApiPath}` ? navigationResponse('application/json; charset=utf-8', 403, '{"error":{"code":403}}') : navigationResponse()); - }); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(columnPageUrl, (client) => client.get(columnApiPath))).toEqual(feedData); - - expect(mocks.goto.mock.calls).toEqual([ - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [`${origin}${columnApiPath}`, { waitUntil: 'domcontentloaded' }], - ]); - expect(mocks.waitForFunction).not.toHaveBeenCalled(); - expect(mocks.browserCookies).toHaveBeenCalledTimes(3); - expect(mocks.evaluate).toHaveBeenCalledOnce(); - expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${columnApiPath}`, expect.objectContaining({ credentials: 'include' })); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('consumes a successful initial JSON response once without requesting the first API twice', async () => { - mocks.browserCookies.mockResolvedValue([{ name: 'd_c0', value: 'browser-dc0' }]); - mocks.goto.mockImplementation((url) => { - mocks.url.mockReturnValue(url); - return Promise.resolve(url === `${origin}${columnApiPath}` ? navigationResponse('application/json', 200, JSON.stringify(feedData)) : navigationResponse()); - }); - const refreshedData = { data: [{ id: 'refreshed-item' }] }; - nativeFetch.mockResolvedValue(Response.json(refreshedData)); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(columnPageUrl, async (client) => { - expect(await client.get(columnApiPath)).toEqual(feedData); - expect(nativeFetch).not.toHaveBeenCalled(); - expect(mocks.evaluate).not.toHaveBeenCalled(); - expect(await client.get(columnApiPath)).toEqual(refreshedData); - }); - - expect(mocks.goto).toHaveBeenCalledTimes(2); - expect(mocks.waitForFunction).not.toHaveBeenCalled(); - expect(nativeFetch).toHaveBeenCalledOnce(); - expect(mocks.evaluate).toHaveBeenCalledOnce(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('initializes a column on the API origin and restores that origin after reading the column HTML', async () => { - const columnUrl = 'https://zhuanlan.zhihu.com/example'; - const columnApi = '/api/v4/columns/example/items'; - mocks.url.mockReturnValue(columnUrl); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(columnUrl, async (client) => { - expect(await client.get(columnApi)).toEqual(feedData); - expect(await client.getPage()).toBe(pageHtml); - expect(await client.get(columnApi)).toEqual(feedData); - }); - - expect(mocks.goto.mock.calls).toEqual([ - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [`${origin}${columnApi}`, { waitUntil: 'domcontentloaded' }], - [columnUrl, { waitUntil: 'domcontentloaded' }], - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - ]); - expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(columnUrl, { noGoto: true, closeTimeout: 0 }); - expect(nativeFetch).toHaveBeenCalledTimes(2); - expect(nativeFetch).toHaveBeenCalledWith(`${origin}${columnApi}`, expect.objectContaining({ credentials: 'include' })); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('initializes a c_-style column through its API even when the page already uses the API origin', async () => { - const columnUrl = `${origin}/column/c_123`; - const columnApi = '/api/v4/columns/c_123/items'; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(columnUrl, (client) => client.get(columnApi))).toEqual(feedData); - - expect(mocks.goto.mock.calls).toEqual([ - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [`${origin}${columnApi}`, { waitUntil: 'domcontentloaded' }], - ]); - expect(mocks.getPlaywrightPage).toHaveBeenCalledExactlyOnceWith(columnUrl, { noGoto: true, closeTimeout: 0 }); - expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${columnApi}`, expect.objectContaining({ credentials: 'include' })); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it.each([ - [`${origin}/people/example`, '/api/v3/moments/example/activities?limit=5'], - [`${origin}/topic/123/newest`, '/api/v5.1/topics/123/feeds/timeline_activity'], - [`${origin}/question/123`, '/api/v4/questions/123/answers?limit=20'], - ])('initializes the human page for a non-column API: %s', async (targetUrl, targetApi) => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(targetUrl, (client) => client.get(targetApi))).toEqual(feedData); - - expect(mocks.goto.mock.calls).toEqual([ - [`${origin}/explore`, { waitUntil: 'domcontentloaded' }], - [targetUrl, { waitUntil: 'domcontentloaded' }], - ]); - expect(nativeFetch).toHaveBeenCalledExactlyOnceWith(`${origin}${targetApi}`, expect.objectContaining({ credentials: 'include' })); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('does not initialize a browser when the callback performs no requests', async () => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(pageUrl, () => Promise.resolve('cached result'))).toBe('cached result'); - - expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(nativeFetch).not.toHaveBeenCalled(); - }); - - it('closes the browser when the route callback fails after reading data', async () => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await expect( - withZhihuClient(pageUrl, async (client) => { - await client.get(apiPath); - throw new Error('Route parsing failed'); - }) - ).rejects.toThrow('Route parsing failed'); - - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it.each([ - { status: 403, body: 'test-private-response', error: 'HTTP 403' }, - { status: 200, body: 'test-private-response', error: 'did not return JSON' }, - ])('rejects an unusable API response and closes the browser: $error', async ({ status, body, error }) => { - nativeFetch.mockResolvedValue(new Response(body, { status })); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - await expect(result).rejects.toThrow(error); - await expect(result).rejects.not.toThrow('test-private-response'); - expect(mocks.destroy).toHaveBeenCalledOnce(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - }); - - it('closes the browser when its native API fetch fails', async () => { - nativeFetch.mockRejectedValue(new Error('Browser fetch failed')); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await expect(withZhihuClient(pageUrl, (client) => client.get(apiPath))).rejects.toThrow('Browser fetch failed'); - - expect(mocks.destroy).toHaveBeenCalledOnce(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - }); - - it('closes the browser and gives an actionable phase when session cookies are unavailable', async () => { - mocks.waitForFunction.mockRejectedValue(new Error('Page timeout')); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - await expect(result).rejects.toThrow('session cookie wait'); - await expect(result).rejects.toThrow('Configure ZHIHU_COOKIES'); - expect(nativeFetch).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('stops before the target and closes the browser when guest navigation fails', async () => { - mocks.goto.mockRejectedValueOnce(new Error('Navigation failed with test-private-diagnostic')); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - const result = withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - await expect(result).rejects.toThrow('guest cookie initialization'); - await expect(result).rejects.toThrow('Configure ZHIHU_COOKIES'); - await expect(result).rejects.not.toThrow('test-private-diagnostic'); - expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); - expect(mocks.browserCookies).not.toHaveBeenCalled(); - expect(mocks.waitForFunction).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('stops before the target and closes the browser when the guest page does not set d_c0', async () => { - mocks.browserCookies.mockResolvedValueOnce([{ name: '__zse_ck', value: 'browser-zse' }]); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await expect(withZhihuClient(pageUrl, (client) => client.get(apiPath))).rejects.toThrow('guest cookie initialization'); - - expect(mocks.goto).toHaveBeenCalledExactlyOnceWith(`${origin}/explore`, { waitUntil: 'domcontentloaded' }); - expect(mocks.browserCookies).toHaveBeenCalledExactlyOnceWith(origin); - expect(mocks.waitForFunction).not.toHaveBeenCalled(); - expect(mocks.evaluate).not.toHaveBeenCalled(); - expect(mocks.destroy).toHaveBeenCalledOnce(); - }); - - it('rejects non-API paths before initializing a session', async () => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await expect(withZhihuClient(pageUrl, (client) => client.get('//example.invalid/api/'))).rejects.toThrow('expected an API path'); - - expect(mocks.getPlaywrightPage).not.toHaveBeenCalled(); - expect(nativeFetch).not.toHaveBeenCalled(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - }); -}); diff --git a/tests/zhihu-jsdom.test.ts b/tests/zhihu-jsdom.test.ts deleted file mode 100644 index e77acd0246f6..000000000000 --- a/tests/zhihu-jsdom.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; - -const mocks = vi.hoisted(() => ({ - cookies: '', - ofetch: vi.fn(), - raw: vi.fn(), - tryGet: vi.fn(), - generateHeaders: vi.fn(), - createBrowserClient: vi.fn(), -})); - -vi.mock('../lib/config', () => ({ - config: { - zhihu: { - get cookies() { - return mocks.cookies; - }, - }, - cache: { contentExpire: 3600 }, - }, -})); -vi.mock('../lib/utils/ofetch', () => ({ default: Object.assign(mocks.ofetch, { raw: mocks.raw }) })); -vi.mock('../lib/utils/cache', () => ({ default: { tryGet: mocks.tryGet } })); -vi.mock('../lib/utils/header-generator', () => ({ generateHeaders: mocks.generateHeaders })); -vi.mock('../lib/utils/is-worker', () => ({ isWorker: false })); -vi.mock('../lib/routes/zhihu/browser', () => ({ createBrowserClient: mocks.createBrowserClient })); - -const origin = 'https://www.zhihu.com'; -const pageUrl = `${origin}/people/example`; -const apiPath = '/api/v4/members/example/activities?limit=20'; -const otherApiPath = '/api/v4/members/example?include=description'; -const scriptUrl = 'https://static.zhihu.com/zse-ck/v4/0123456789abcdef.js'; -const challengeHtml = ''; -const challengeScript = ` -if (navigator.userAgent !== 'RSSHub JSDOM fixture' || navigator.webdriver !== false) { - throw new Error('The challenge did not receive the configured browser fingerprint'); -} -if (document.getElementById('zh-zse-ck').getAttribute('content') !== 'challenge-fixture') { - throw new Error('The challenge did not receive its page metadata'); -} -document.cookie = '__zse_ck=generated-token; path=/'; -`; -const userAgent = 'RSSHub JSDOM fixture'; -const feedData = { data: [{ id: 'api-item' }] }; -const pageHtml = 'Profile content'; -const cached = new Map(); -let initialRejectionListeners: Set[number]>; - -const requestHeaders = (url: string) => { - const request = mocks.ofetch.mock.calls.find(([requestedUrl]) => requestedUrl === url); - expect(request).toBeDefined(); - return new Headers(request?.[1]?.headers); -}; - -beforeEach(() => { - vi.resetModules(); - vi.resetAllMocks(); - cached.clear(); - mocks.cookies = ''; - initialRejectionListeners = new Set(process.listeners('unhandledRejection')); - vi.spyOn(Math, 'random').mockReturnValue(0.25); - mocks.generateHeaders.mockReturnValue({ 'user-agent': userAgent }); - mocks.tryGet.mockImplementation(async (key: string, fetchValue: () => Promise) => { - if (!cached.has(key)) { - cached.set(key, await fetchValue()); - } - return cached.get(key); - }); - mocks.raw.mockImplementation((url: string) => Promise.resolve(url === `${origin}/explore` ? { headers: new Headers({ 'set-cookie': 'd_c0=guest-identity==|fixture; Path=/; Secure' }) } : { _data: challengeHtml })); - mocks.ofetch.mockImplementation((url: string) => { - if (url === scriptUrl) { - return Promise.resolve(challengeScript); - } - if (url === pageUrl) { - return Promise.resolve(pageHtml); - } - if (url.startsWith(`${origin}/api/`)) { - return Promise.resolve(feedData); - } - throw new Error(`Unexpected fixture URL: ${url}`); - }); -}); - -afterEach(() => { - for (const listener of process.listeners('unhandledRejection')) { - if (!initialRejectionListeners.has(listener)) { - process.removeListener('unhandledRejection', listener); - } - } - expect(mocks.createBrowserClient).not.toHaveBeenCalled(); - vi.restoreAllMocks(); -}); - -describe('Zhihu JSDOM client on Node', () => { - it.each(['', 'z_c0=isolated-login', 'd_c0=; z_c0=isolated-login'])('executes the challenge in JSDOM and keeps guest credentials isolated: %s', async (cookies) => { - mocks.cookies = cookies; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(pageUrl, (client) => client.get(apiPath))).toEqual(feedData); - - expect(mocks.raw).toHaveBeenNthCalledWith(1, `${origin}/explore`, { - headers: { 'user-agent': userAgent }, - redirect: 'manual', - ignoreResponseError: true, - }); - expect(mocks.raw).toHaveBeenNthCalledWith(2, `${origin}${apiPath}`, { - headers: { - 'user-agent': userAgent, - cookie: 'd_c0=guest-identity; __zse_ck=005_x-x', - referer: pageUrl, - 'x-requested-with': 'fetch', - }, - ignoreResponseError: true, - }); - expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=guest-identity'); - expect(requestHeaders(`${origin}${apiPath}`).get('user-agent')).toBe(userAgent); - expect(mocks.ofetch).toHaveBeenCalledWith(scriptUrl, { headers: { 'user-agent': userAgent }, parseResponse: expect.any(Function) }); - expect(mocks.tryGet).toHaveBeenCalledWith('zhihu:zse-ck:v4:guest', expect.any(Function), 3600, false); - }); - - it('preserves the configured identity and login cookies without creating a guest session', async () => { - mocks.cookies = 'd_c0=configured==; z_c0=login=value; __zse_ck=; extra=retained'; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - expect(mocks.raw).toHaveBeenCalledExactlyOnceWith(`${origin}${apiPath}`, expect.objectContaining({ headers: expect.objectContaining({ cookie: 'd_c0=configured==; __zse_ck=005_x-x' }) })); - expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=configured==; z_c0=login=value; extra=retained'); - expect(requestHeaders(`${origin}${apiPath}`).get('user-agent')).toBe(userAgent); - }); - - it('keeps API and HTML requests in the same generated identity and signs each API path separately', async () => { - mocks.cookies = 'd_c0=configured==; z_c0=login'; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - const { getSignedHeaders } = await import('../lib/routes/zhihu/sign'); - - const result = await withZhihuClient(pageUrl, async (client) => ({ - first: await client.get(apiPath), - second: await client.get(otherApiPath), - html: await client.getPage(), - })); - - expect(result).toEqual({ first: feedData, second: feedData, html: pageHtml }); - expect(mocks.raw).toHaveBeenCalledTimes(1); - expect(mocks.generateHeaders).toHaveBeenCalledTimes(1); - for (const url of [`${origin}${apiPath}`, `${origin}${otherApiPath}`, pageUrl]) { - const headers = requestHeaders(url); - expect(headers.get('cookie')).toBe('__zse_ck=generated-token; d_c0=configured==; z_c0=login'); - expect(headers.get('user-agent')).toBe(userAgent); - expect(headers.get('referer')).toBe(pageUrl); - } - expect(requestHeaders(`${origin}${apiPath}`).get('x-zse-96')).toBe(getSignedHeaders(apiPath, 'configured==')['x-zse-96']); - expect(requestHeaders(`${origin}${otherApiPath}`).get('x-zse-96')).toBe(getSignedHeaders(otherApiPath, 'configured==')['x-zse-96']); - expect(requestHeaders(`${origin}${apiPath}`).get('x-zse-96')).not.toBe(requestHeaders(`${origin}${otherApiPath}`).get('x-zse-96')); - const htmlRequest = mocks.ofetch.mock.calls.find(([url]) => url === pageUrl); - expect(htmlRequest?.[1]?.parseResponse(pageHtml)).toBe(pageHtml); - }); - - it('initializes from the page path when HTML is requested before an API', async () => { - mocks.cookies = 'd_c0=configured=='; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(pageUrl, async (client) => { - expect(await client.getPage()).toBe(pageHtml); - expect(await client.get(apiPath)).toEqual(feedData); - }); - - expect(mocks.raw).toHaveBeenCalledExactlyOnceWith(pageUrl, expect.objectContaining({ ignoreResponseError: true })); - expect(requestHeaders(pageUrl).get('cookie')).toBe(requestHeaders(`${origin}${apiPath}`).get('cookie')); - expect(requestHeaders(pageUrl).get('user-agent')).toBe(userAgent); - }); - - it('deduplicates concurrent challenge generation and reuses cached credentials across clients', async () => { - mocks.cookies = 'd_c0=configured=='; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - const results = await Promise.all([withZhihuClient(pageUrl, (client) => client.get(apiPath)), withZhihuClient(pageUrl, (client) => client.get(otherApiPath))]); - expect(results).toEqual([feedData, feedData]); - await withZhihuClient(pageUrl, (client) => client.get(apiPath)); - - expect(mocks.raw).toHaveBeenCalledTimes(1); - expect(mocks.generateHeaders).toHaveBeenCalledTimes(1); - expect(mocks.ofetch.mock.calls.filter(([url]) => url === scriptUrl)).toHaveLength(1); - }); - - it('retries generation after a failed challenge without permanently caching the rejection', async () => { - mocks.cookies = 'd_c0=configured=='; - mocks.raw.mockResolvedValueOnce({ _data: 'No challenge script' }); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(pageUrl, async (client) => { - await expect(client.get(apiPath)).rejects.toThrow('challenge page'); - expect(await client.get(apiPath)).toEqual(feedData); - }); - - expect(mocks.raw).toHaveBeenCalledTimes(2); - expect(mocks.tryGet).toHaveBeenCalledTimes(2); - expect(mocks.ofetch.mock.calls.filter(([url]) => url === `${origin}${apiPath}`)).toHaveLength(1); - }); - - it('does not reuse generated credentials for a different configured identity', async () => { - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - mocks.cookies = 'd_c0=first-identity; z_c0=first-login'; - await withZhihuClient(pageUrl, (client) => client.get(apiPath)); - mocks.cookies = 'd_c0=second-identity; z_c0=second-login'; - await withZhihuClient(pageUrl, (client) => client.get(otherApiPath)); - - expect(mocks.raw).toHaveBeenCalledTimes(2); - expect(mocks.tryGet.mock.calls[0][0]).not.toBe(mocks.tryGet.mock.calls[1][0]); - expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=first-identity; z_c0=first-login'); - expect(requestHeaders(`${origin}${otherApiPath}`).get('cookie')).toBe('__zse_ck=generated-token; d_c0=second-identity; z_c0=second-login'); - }); - - it('uses complete configured credentials without initializing JSDOM or a browser', async () => { - mocks.cookies = 'd_c0=configured==; __zse_ck=existing-token; z_c0=login=value'; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient(pageUrl, async (client) => ({ data: await client.get(apiPath), html: await client.getPage() }))).toEqual({ data: feedData, html: pageHtml }); - - expect(requestHeaders(`${origin}${apiPath}`).get('cookie')).toBe(mocks.cookies); - expect(requestHeaders(pageUrl).get('cookie')).toBe(mocks.cookies); - expect(mocks.raw).not.toHaveBeenCalled(); - expect(mocks.tryGet).not.toHaveBeenCalled(); - expect(mocks.generateHeaders).not.toHaveBeenCalled(); - }); - - it.each(['', 'd_c0=configured==; __zse_ck=existing-token'])('rejects non-API URLs before any I/O: %s', async (cookies) => { - mocks.cookies = cookies; - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - await withZhihuClient(pageUrl, async (client) => { - await expect(client.get('https://example.com/api/v4/members/example')).rejects.toThrow('expected an API path'); - await expect(client.get('/people/example')).rejects.toThrow('expected an API path'); - }); - - expect(mocks.ofetch).not.toHaveBeenCalled(); - expect(mocks.raw).not.toHaveBeenCalled(); - expect(mocks.tryGet).not.toHaveBeenCalled(); - expect(mocks.generateHeaders).not.toHaveBeenCalled(); - }); -}); diff --git a/tests/zhihu-session.worker.test.ts b/tests/zhihu-session.worker.test.ts deleted file mode 100644 index 2a8189284d0c..000000000000 --- a/tests/zhihu-session.worker.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { isWorker } from '@/utils/is-worker'; - -const mocks = vi.hoisted(() => ({ - cookies: '', - ofetch: vi.fn(), - createBrowserClient: vi.fn(), - createJSDOMClient: vi.fn(() => { - throw new Error('JSDOM must never run on Workers'); - }), - get: vi.fn(), - getPage: vi.fn(), - close: vi.fn(), -})); - -vi.mock('../lib/config', () => ({ - config: { - zhihu: { - get cookies() { - return mocks.cookies; - }, - }, - }, -})); -vi.mock('../lib/utils/ofetch', () => ({ default: mocks.ofetch })); -vi.mock('../lib/routes/zhihu/browser', () => ({ createBrowserClient: mocks.createBrowserClient })); -vi.mock('../lib/routes/zhihu/jsdom', () => ({ createJSDOMClient: mocks.createJSDOMClient })); - -beforeEach(() => { - vi.resetAllMocks(); - mocks.cookies = ''; - mocks.createBrowserClient.mockResolvedValue({ get: mocks.get, getPage: mocks.getPage, close: mocks.close }); -}); - -describe('Zhihu runtime selection in workerd', () => { - it('uses a browser session for both API and HTML requests', async () => { - expect(isWorker).toBe(true); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - mocks.get.mockResolvedValue({ data: [1] }); - mocks.getPage.mockResolvedValue('Column'); - - await withZhihuClient('https://zhuanlan.zhihu.com/example', async (client) => { - expect(await client.get('/api/v4/columns/example/items')).toEqual({ data: [1] }); - expect(await client.getPage()).toBe('Column'); - }); - - expect(mocks.createBrowserClient).toHaveBeenCalledExactlyOnceWith('https://zhuanlan.zhihu.com/example', '', '/api/v4/columns/example/items'); - expect(mocks.close).toHaveBeenCalledOnce(); - expect(mocks.createJSDOMClient).not.toHaveBeenCalled(); - expect(mocks.ofetch).not.toHaveBeenCalled(); - }); - - it('uses complete configured cookies without either initializer', async () => { - mocks.cookies = 'd_c0=configured; __zse_ck=existing'; - mocks.ofetch.mockResolvedValue({ data: [1] }); - const { withZhihuClient } = await import('../lib/routes/zhihu/utils'); - - expect(await withZhihuClient('https://www.zhihu.com/people/example', (client) => client.get('/api/v4/members/example'))).toEqual({ data: [1] }); - expect(mocks.createBrowserClient).not.toHaveBeenCalled(); - expect(mocks.createJSDOMClient).not.toHaveBeenCalled(); - expect(mocks.ofetch).toHaveBeenCalledOnce(); - }); -}); diff --git a/tsdown-worker.config.ts b/tsdown-worker.config.ts index 0b7fbb40f634..c901df65ceac 100644 --- a/tsdown-worker.config.ts +++ b/tsdown-worker.config.ts @@ -85,6 +85,8 @@ export default defineConfig({ '@sentry/node': path.resolve('./lib/shims/sentry-node.ts'), '@honeybadger-io/js': path.resolve('./lib/shims/honeybadger.ts'), typescript: path.resolve('./lib/shims/typescript.ts'), + jsdom: path.resolve('./lib/shims/jsdom.ts'), + vm2: path.resolve('./lib/shims/vm2.ts'), // Routes file with Worker-specific build (match relative import from lib/) '../assets/build/routes.js': path.resolve('./assets/build/routes-worker.js'), // routes.json is only used in test environment, but rolldown still tries to resolve it @@ -95,6 +97,7 @@ export default defineConfig({ neverBundle: [ // Let Wrangler select the package's static-WASM workerd export. 'xxhash-wasm', + '@oxc-parser/binding-wasm32-wasip1/wasm.wasm', // Exclude non-code files that might be accidentally imported /\/_README$/, /\.node$/, diff --git a/vitest.workers.config.ts b/vitest.workers.config.ts index e162ed9ad6b8..872c1242e289 100644 --- a/vitest.workers.config.ts +++ b/vitest.workers.config.ts @@ -67,9 +67,11 @@ export default defineConfig({ resolve: { alias: { 'dotenv/config': path.resolve('./lib/shims/dotenv-config.ts'), + jsdom: path.resolve('./lib/shims/jsdom.ts'), + vm2: path.resolve('./lib/shims/vm2.ts'), }, }, test: { - include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts', 'lib/utils/parse-script-data.test.ts', 'lib/utils/parse-date-in-timezone.test.ts', 'tests/source-date-routes.test.ts', 'tests/cache-coordination.test.ts'], + include: ['lib/**/*.worker.test.ts', 'tests/**/*.worker.test.ts'], }, }); From 66ca147d7bd15ac6219e3ac7f59c600bb07cd999 Mon Sep 17 00:00:00 2001 From: Jan Kahmen <36455663+kah-ja@users.noreply.github.com> Date: Tue, 15 Sep 2026 13:04:35 +0000 Subject: [PATCH 0820/1190] fix(route): gate the user supplied hosts in discuz and yna (#23301) * fix(route/discuz): gate the user supplied forum link The handler takes the whole trailing path as the URL to fetch (lib/routes/discuz/discuz.ts:98) and passes it to ofetch without looking at it (:112), so the route reaches whatever host the caller names, including loopback and link local addresses on the deployment. Other routes that take a user supplied domain are gated by ALLOW_USER_SUPPLY_UNSAFE_DOMAIN, for example lib/routes/wordpress/index.ts:16 and lib/routes/hexo/fluid.ts:25. Apply the same gate here, declare it in the route's requireConfig, and reuse the http(s) shape check from the wordpress route. * fix(route/yna): restrict lang to the documented languages lang is interpolated as the leading label of the requested host (lib/routes/yna/index.ts:59), so a value such as 127.0.0.1:8080%23 moves the request to another host. Accept only the language codes the route documents, plus the sp label the Spanish feed page in the description uses, and reject everything else with InvalidParameterError. --- lib/routes/discuz/discuz.ts | 11 +++++++++++ lib/routes/yna/index.ts | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/lib/routes/discuz/discuz.ts b/lib/routes/discuz/discuz.ts index 4a9501c891bf..800d43513ad0 100644 --- a/lib/routes/discuz/discuz.ts +++ b/lib/routes/discuz/discuz.ts @@ -88,6 +88,9 @@ export const route: Route = { }, name: '通用子版块', maintainers: ['junfengP', 'pseudoyu'], + features: { + requireConfig: [{ name: 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN', description: 'Allow user supplied domain' }], + }, handler, description: `| Discuz X Series | Discuz 7.x Series | | --------------- | ----------------- | @@ -100,6 +103,14 @@ async function handler(ctx) { const cid = ctx.req.param('cid'); link = link.replace(/:\/\//, ':/').replace(/:\//, '://'); + if (!config.feature.allow_user_supply_unsafe_domain) { + throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); + } + + if (!/^https?:\/\/[^\s#$./?].\S*$/i.test(link)) { + throw new InvalidParameterError('Invalid link'); + } + const cookie = cid === undefined ? '' : config.discuz.cookies[cid]; if (cookie === undefined) { throw new ConfigNotFoundError('缺少对应论坛的cookie.'); diff --git a/lib/routes/yna/index.ts b/lib/routes/yna/index.ts index 915c5621471f..8997d9bb5104 100644 --- a/lib/routes/yna/index.ts +++ b/lib/routes/yna/index.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -47,9 +48,19 @@ For example, the path for the RSS feed url Date: Tue, 15 Sep 2026 23:12:37 +0900 Subject: [PATCH 0821/1190] feat(route/lg): carry town, first permit date and closure in food-permit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 自治体標準オープンデータ schema that the Tokyo wards and 大阪市 publish to already carries 施設所在地_町字, 初回許可年月日, 許可満了日 and 廃業年月日, but `_extra` dropped all four. A consumer therefore had to reach into `raw` and know each publisher's own column spelling to tell an open business from a closed one. Normalise them onto PermitExtra as `town`, `first_permit_date`, `expires_at` and `closed_date`, reusing the existing isoDate helper so 和暦 (令和8年8月7日), YYYY/M/D and YYYY-MM-DD all land as YYYY-MM-DD. A column a publisher omits yields null, never 0 or an empty string. 大阪市 publishes none of 町字 / 初回許可日 / 廃業日, so only expires_at populates there; its route description says so. --- lib/routes/lg/osaka/food-permit.ts | 22 +++++++++++++++++++--- lib/routes/lg/tokyo/food-permit.ts | 12 ++++++++++-- lib/routes/lg/utils.ts | 4 ++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/routes/lg/osaka/food-permit.ts b/lib/routes/lg/osaka/food-permit.ts index 23f69eadc68d..d93d54761c6e 100644 --- a/lib/routes/lg/osaka/food-permit.ts +++ b/lib/routes/lg/osaka/food-permit.ts @@ -4,7 +4,7 @@ import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; -import { csvRecords, decodeText, type PermitExtra, type Row } from '../utils'; +import { csvRecords, decodeText, isoDate, type PermitExtra, type Row } from '../utils'; /** * 食品営業許可 facilities of 大阪市, from the CC BY 4.0 CSV linked on the city's 食品営業許可施設一覧 page @@ -65,7 +65,23 @@ const toItem = (raw: Row): DataItem & { _extra: PermitExtra } => { guid: `lg/osaka/food-permit:${permitNo}`, link: PAGE, description: [ward, address, businessType, `指令番号 ${permitNo}`, raw['許可満了日'] ? `許可満了日 ${raw['許可満了日']}` : null].filter(Boolean).join(' / '), - _extra: { source: 'osaka', ward, permit_no: permitNo, name, address, permit_date: null, business_type: businessType, lat, lon, raw }, + // 大阪市 publishes neither 町字 nor 初回許可日 nor 廃業日 — the snapshot only lists permits that are still valid. + _extra: { + source: 'osaka', + ward, + permit_no: permitNo, + name, + address, + town: null, + permit_date: null, + first_permit_date: null, + expires_at: isoDate(raw['許可満了日'] ?? null), + closed_date: null, + business_type: businessType, + lat, + lon, + raw, + }, }; }; @@ -102,7 +118,7 @@ export const route: Route = { example: '/lg/osaka/food-permit', description: `Newest food business permits (食品営業許可) in 大阪市,from the CC BY 4.0 [食品営業許可施設一覧 CSV](https://www.city.osaka.lg.jp/kenko/page/0000575579.html) — a quarterly snapshot of all valid permits with 緯度経度. -The dataset has no permit date, only 許可満了日,so items have no \`pubDate\`; they are ordered by 指令番号 (\`大 保食第<年度>-<連番>号\`), newest first, and only 申請区分 = 新規 rows are included. \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\` (always \`null\`), \`business_type\`, \`lat\`, \`lon\` and the publisher's original columns in \`raw\`. +The dataset has no permit date, only 許可満了日,so items have no \`pubDate\`; they are ordered by 指令番号 (\`大 保食第<年度>-<連番>号\`), newest first, and only 申請区分 = 新規 rows are included. \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\` (always \`null\`), \`expires_at\` (許可満了日), \`business_type\`, \`lat\`, \`lon\` and the publisher's original columns in \`raw\`. 大阪市 publishes no 町字,初回許可日 or 廃業日,so \`town\`, \`first_permit_date\` and \`closed_date\` are always \`null\` here. | Query | Description | Default | | ------- | -------------------------- | ------- | diff --git a/lib/routes/lg/tokyo/food-permit.ts b/lib/routes/lg/tokyo/food-permit.ts index 498fd5f6822d..e49f7c71e67a 100644 --- a/lib/routes/lg/tokyo/food-permit.ts +++ b/lib/routes/lg/tokyo/food-permit.ts @@ -62,6 +62,10 @@ const MINATO_CSV = 'https://opendata.city.minato.tokyo.jp/dataset/54d8c582-00e2- const DEFAULT_LIMIT = 100; const MAX_LIMIT = 500; const DATE_COLS = ['許可年月日', '許可決定日', SHIBUYA_DATE, '許可開始日', '許可日']; +const FIRST_PERMIT_COLS = ['初回許可年月日', '初回許可日']; +const EXPIRY_COLS = ['許可満了日', '有効期限']; +const CLOSED_COLS = ['廃業年月日', '廃業日', '廃止年月日']; +const TOWN_COLS = ['施設所在地_町字', '町字']; /** First non-empty column; `※` / `※※※※※` (品川区・台東区 mask individuals' data this way) counts as empty. */ const pick = (row: Row, cols: readonly string[]): string | null => { @@ -182,13 +186,17 @@ const toItem = (source: Source, raw: Row): DataItem & { _extra: PermitExtra } => const ward = (pick(raw, ['施設所在地_市区町村', '地方公共団体名']) ?? SOURCES[source].label).replace(/^東京都/, ''); const lat = toNumber(pick(raw, ['緯度'])); const lon = toNumber(pick(raw, ['経度'])); + const town = pick(raw, TOWN_COLS); + const firstPermitDate = isoDate(pick(raw, FIRST_PERMIT_COLS)); + const expiresAt = isoDate(pick(raw, EXPIRY_COLS)); + const closedDate = isoDate(pick(raw, CLOSED_COLS)); return { title: `${name}(${businessType ?? '業種不明'})`, guid: `lg/tokyo/food-permit:${source}:${permitNo}`, link: SOURCES[source].link, pubDate: permitDate === null ? undefined : timezone(parseDate(permitDate, 'YYYY-MM-DD'), 9), description: [ward, address, businessType, permitDate, `許可番号 ${permitNo}`].filter(Boolean).join(' / '), - _extra: { source, ward, permit_no: permitNo, name, address, permit_date: permitDate, business_type: businessType, lat, lon, raw }, + _extra: { source, ward, permit_no: permitNo, name, address, town, permit_date: permitDate, first_permit_date: firstPermitDate, expires_at: expiresAt, closed_date: closedDate, business_type: businessType, lat, lon, raw }, }; }; @@ -270,7 +278,7 @@ export const route: Route = { - 世田谷区: [食品関係施設情報の公開について](https://www.city.setagaya.lg.jp/02245/online_tetsuzuki/3246.html) — the two newest 例月新規許可施設一覧 CSVs (updated on the 15th) - 目黒区: [飲食店等 (BODIK CKAN)](https://data.bodik.jp/dataset/131105_food_business) — the two newest 飲食店 新規 monthly CSVs (updated by the 10th) -Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`permit_date\`, \`business_type\`, \`lat\` / \`lon\` (when the publisher gives them, else \`null\`) and the publisher's original columns in \`raw\`. Only 許可 rows are included (届出 rows are skipped). +Items are sorted by permit date (\`pubDate\`). \`_extra\` holds \`source\`, \`ward\`, \`permit_no\`, \`name\`, \`address\`, \`town\` (町字), \`permit_date\`, \`first_permit_date\`, \`expires_at\` (許可満了日), \`closed_date\` (廃業日 — non-null means the business has already closed), \`business_type\`, \`lat\` / \`lon\` and the publisher's original columns in \`raw\`. Every field a publisher omits is \`null\`, never \`0\` or an empty string. Only 許可 rows are included (届出 rows are skipped). | Query | Description | Default | | ------- | ------------------------------------- | ------- | diff --git a/lib/routes/lg/utils.ts b/lib/routes/lg/utils.ts index 3b431b225ea4..66a15a745e3e 100644 --- a/lib/routes/lg/utils.ts +++ b/lib/routes/lg/utils.ts @@ -65,7 +65,11 @@ export interface PermitExtra { permit_no: string; name: string; address: string | null; + town: string | null; // 施設所在地_町字, the finest area unit the 自治体標準オープンデータ schema carries permit_date: string | null; // YYYY-MM-DD + first_permit_date: string | null; // YYYY-MM-DD; earlier than permit_date once a permit has been renewed + expires_at: string | null; // YYYY-MM-DD, 許可満了日 + closed_date: string | null; // YYYY-MM-DD, 廃業日; non-null means the business has already closed business_type: string | null; lat: number | null; lon: number | null; From 5756c963d94ae7168b7b372d3e001414955c8be4 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 23:28:28 +0900 Subject: [PATCH 0822/1190] refactor(route/tokyometro): let RidershipExtra carry a yearly total MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 東京都統計年鑑 publishes 駅別乗降車人員 as a one-year total in 千人, not as the 一日平均 in 人/日 that the operators publish. Converting between the two would mean a route doing arithmetic on published figures, which the shared contract deliberately avoids. Add an optional `annual_total` alongside `daily_average`, widen `unit` to `'人/日' | '千人/年'` so an item says which figure it carries, and make ridershipItem render whichever one is present. Both changes are additive: the six existing routes keep `unit: '人/日'` and a daily average, so their payloads are unchanged. --- lib/routes/tokyometro/utils.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/routes/tokyometro/utils.ts b/lib/routes/tokyometro/utils.ts index 6c5109394740..c6360e838789 100644 --- a/lib/routes/tokyometro/utils.ts +++ b/lib/routes/tokyometro/utils.ts @@ -1,12 +1,15 @@ /** * Shared shape + helpers for the 駅別乗降人員 (station ridership) routes of Tokyo private railways - * (tokyometro / tokyu / odakyu / keio / toei) and JR東日本. One item per station per fiscal year; unknown = `null`, never `0`; - * the operator's cell text is kept verbatim in `raw`. + * (tokyometro / tokyu / odakyu / keio / toei), JR東日本, and 東京都統計年鑑. One item per station per fiscal year; + * unknown = `null`, never `0`; the publisher's cell text is kept verbatim in `raw`. + * + * Operators report a 一日平均 (`daily_average`, 人/日); 東京都統計年鑑 reports a yearly total (`annual_total`, 千人/年). + * They are never converted into each other — `unit` says which one an item carries. */ import type { DataItem } from '@/types'; -export type RidershipSource = 'tokyometro' | 'tokyu' | 'odakyu' | 'keio' | 'jreast' | 'toei'; +export type RidershipSource = 'tokyometro' | 'tokyu' | 'odakyu' | 'keio' | 'jreast' | 'toei' | 'toukei-tokyo'; /** What the operator counts: JR東日本 publishes 乗車 (boarding) only, the others 乗降 (boarding + alighting). */ export type RidershipMeasure = 'boarding' | 'boarding_alighting'; @@ -17,8 +20,10 @@ export interface RidershipExtra { station: string; // as printed by the operator (without 駅) line: string | null; // 路線; several lines joined with '・' when the operator lists a station once for all of them fiscal_year: number; // 2025 = FY2025 (2025-04 … 2026-03) - daily_average: number | null; // 一日平均乗降人員 - unit: '人/日'; + daily_average: number | null; // 一日平均乗降人員; null when the publisher gives a yearly total instead + /** Yearly total, for publishers that report one (東京都統計年鑑). Absent for the per-operator routes, which report a daily average. Never derived — a route must not convert between the two. */ + annual_total?: number | null; + unit: '人/日' | '千人/年'; measure: RidershipMeasure; rank: number | null; // only when the operator publishes a 順位 yoy_pct: number | null; // 前年比 / 増減率 (%); negative when the operator prints ▲ or a minus @@ -53,7 +58,8 @@ export const parseFiscalYear = (text: string): number | null => { /** Item title / guid / description are derived from `_extra` only, so all four operators read the same way. */ export const ridershipItem = (extra: RidershipExtra, link: string): DataItem & { _extra: RidershipExtra } => { - const people = extra.daily_average === null ? '不明' : `${extra.daily_average.toLocaleString('ja-JP')}人/日`; + const figure = extra.daily_average ?? extra.annual_total ?? null; + const people = figure === null ? '不明' : `${figure.toLocaleString('ja-JP')}${extra.unit}`; const yoy = extra.yoy_pct === null ? null : `前年比 ${extra.yoy_pct > 0 ? '+' : ''}${extra.yoy_pct}%`; return { title: `${extra.station}(${extra.line ?? extra.operator}) ${extra.fiscal_year}年度 ${people}`, From b394f9540307177d92e2eebc4861e8ea15f60d8e Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Tue, 15 Sep 2026 23:32:01 +0900 Subject: [PATCH 0823/1190] =?UTF-8?q?feat(route/lg):=20add=20=E6=9D=B1?= =?UTF-8?q?=E4=BA=AC=E9=83=BD=E7=B5=B1=E8=A8=88=E5=B9=B4=E9=91=91=20?= =?UTF-8?q?=E9=A7=85=E5=88=A5=E4=B9=97=E9=99=8D=E8=BB=8A=E4=BA=BA=E5=93=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 東京都統計年鑑 restates the per-station figures 私鉄各社 and the subway operators report to 東京都, under CC BY 4.0. That makes it the only permissively licensed per-station source for ゆりかもめ, りんかい線 and つくばエクスプレス, whose own sites either publish no per-station table or forbid reuse outright. Figures are one-year totals in 千人, so items carry `annual_total` with `unit: '千人/年'` and leave `daily_average` null rather than dividing by 365. The table also splits 乗車 and 降車; the item carries 乗車 and leaves 降車 verbatim in `raw` instead of summing them. Both are deliberate: the route publishes what the source publishes and never derives a figure. The file interleaves rollup rows with station rows (a company total leaves 系統 and 駅 empty, a line total leaves 駅 empty, and 会社名「総数」 is the grand total), so only rows naming a station are kept. マーク cannot be used to tell them apart, since ◎ and ※ also appear on station rows. The table number moves between editions (私鉄 was 4-12 in 令和6年 and 4-13 in 令和5年) and the dataset id is edition-specific, so both are resolved through the catalog API by name instead of a fixed URL. --- lib/routes/lg/tokyo/rail-ridership.ts | 136 ++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 lib/routes/lg/tokyo/rail-ridership.ts diff --git a/lib/routes/lg/tokyo/rail-ridership.ts b/lib/routes/lg/tokyo/rail-ridership.ts new file mode 100644 index 000000000000..e36a22005ed2 --- /dev/null +++ b/lib/routes/lg/tokyo/rail-ridership.ts @@ -0,0 +1,136 @@ +import type { RidershipExtra } from '@/routes/tokyometro/utils'; +import { parseCount, ridershipItem } from '@/routes/tokyometro/utils'; +import type { Data, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +import { csvRecords, type Row } from '../utils'; + +/** + * 駅別乗降車人員 from the 東京都統計年鑑 (CC BY 4.0), which restates the figures 私鉄各社 and the subway + * operators report to 東京都. Unlike the operators' own pages this is a single permissively licensed file, + * and it is the only source covering ゆりかもめ / りんかい線 / つくばエクスプレス — whose own sites either + * publish nothing per-station or forbid reuse. + * + * The figures are one-year totals in 千人, not the 一日平均 the operators publish, so items carry + * `annual_total` + `unit: '千人/年'` and leave `daily_average` null. The route never converts between them. + */ + +const CATALOG = 'https://catalog.data.metro.tokyo.lg.jp/api/3/action/package_search'; +const LANDING = 'https://catalog.data.metro.tokyo.lg.jp/dataset/t000003d2000001150'; +const BOARDING = '乗車人員 Boarding passengers/総数 Total'; + +/** The table number shifts between editions (私鉄 was 4-12 in 令和6年, 4-13 in 令和5年), so tables are resolved by resource name. */ +const TABLES = { + private: { resource: '私鉄の駅別乗降車人員', label: '私鉄' }, + subway: { resource: '地下鉄の駅別乗降車人員', label: '地下鉄' }, +} as const; + +type Table = keyof typeof TABLES; + +interface Resource { + name: string; + url: string; +} + +/** Newest 統計年鑑「運輸・観光」 edition, then the requested table inside it. The dataset id is edition-specific too, so it is searched for rather than hardcoded. */ +const resolveCsv = async (table: Table): Promise => { + const params = new URLSearchParams({ q: '統計年鑑 運輸', rows: '10', sort: 'metadata_modified desc' }); + const body = await ofetch(`${CATALOG}?${params.toString()}`); + const datasets: Array<{ title: string; resources: Resource[] }> = body?.result?.results ?? []; + const dataset = datasets.find((d) => d.title.includes('統計年鑑') && d.title.includes('運輸')); + const url = dataset?.resources.find((r) => r.name.includes(TABLES[table].resource))?.url; + if (!url) { + throw new Error(`lg/tokyo/rail-ridership: no 「${TABLES[table].resource}」 resource in the current 統計年鑑`); + } + return url; +}; + +/** + * The file interleaves rollup rows with station rows: a company total leaves both 系統 and 駅 empty, a line + * total leaves 駅 empty, and 会社名「総数」 is the grand total. Only rows naming a station are real. + * (マーク cannot be used to tell them apart — ◎ marks a transfer station and ※ a station inside the 23 区, + * and both appear on station rows as well.) + */ +const stationRows = (rows: Row[]): Row[] => rows.filter((r) => (r['駅'] ?? '').trim() !== '' && (r['会社名'] ?? '').trim() !== '総数'); + +const toItem = (row: Row, table: Table) => { + const station = (row['駅'] ?? '').trim(); + const operator = (row['会社名'] ?? '').trim(); + const line = (row['系統'] ?? '').trim() || null; + const raw = Object.fromEntries(Object.entries(row).map(([k, v]) => [k, v ?? ''])); + const extra: RidershipExtra = { + source: 'toukei-tokyo', + operator, + station, + line, + fiscal_year: Number((row['Fiscal year'] ?? '').trim()), + daily_average: null, + annual_total: parseCount(row[BOARDING] ?? ''), + unit: '千人/年', + // The table publishes 乗車 and 降車 in separate columns; summing them would be arithmetic on + // published figures, so the item carries 乗車 and leaves 降車 verbatim in `raw`. + measure: 'boarding', + rank: null, + yoy_pct: null, + raw, + }; + return ridershipItem(extra, `${LANDING}#${table}`); +}; + +export const handler = async (ctx): Promise => { + const table: string = ctx.req.param('table') ?? 'private'; + if (!Object.hasOwn(TABLES, table)) { + throw new Error(`Unknown table "${table}", expected one of ${Object.keys(TABLES).join(', ')}`); + } + const items = (await cache.tryGet(`lg/tokyo/rail-ridership:${table}`, async () => { + const csv: string = await ofetch(await resolveCsv(table as Table), { responseType: 'text' }); + return stationRows(csvRecords(csv.replace(/^\u{FEFF}/u, ''))).map((row) => toItem(row, table as Table)); + })) as Array>; + + return { + title: `東京都統計年鑑 ${TABLES[table as Table].label}の駅別乗降車人員`, + link: LANDING, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/tokyo/rail-ridership/:table?', + name: '東京都統計年鑑 駅別乗降車人員', + url: 'catalog.data.metro.tokyo.lg.jp', + maintainers: ['pseudoyu'], + handler, + example: '/lg/tokyo/rail-ridership', + parameters: { + table: { + description: 'Which yearbook table to read; defaults to 私鉄', + options: [ + { value: 'private', label: '私鉄の駅別乗降車人員' }, + { value: 'subway', label: '地下鉄の駅別乗降車人員' }, + ], + }, + }, + description: `Per-station yearly ridership for Tokyo's private railways and subways, from the [東京都統計年鑑 運輸・観光](${LANDING}) (CC BY 4.0). One item per station and fiscal year. + +This is the only permissively licensed source covering ゆりかもめ, りんかい線 (東京臨海高速鉄道) and つくばエクスプレス (首都圏新都市鉄道) — their own sites either publish no per-station table or forbid reuse. + +Read the figures carefully: + +- They are **one-year totals in 千人**, not the 一日平均 in 人/日 that operators publish. \`_extra\` carries \`annual_total\` with \`unit: '千人/年'\` and leaves \`daily_average\` null; the route never converts between the two. +- The table gives 乗車人員 and 降車人員 in separate columns. The item carries 乗車 (\`measure: 'boarding'\`) and leaves 降車 verbatim in \`raw\`, rather than summing them. +- Only stations **within 東京都** are listed, so a line is truncated at the prefecture border. The publisher's \`マーク\` column (kept in \`raw\`) marks 「◎ 同一会社内の乗換え駅」 and 「※ 区部にある駅 (線)」. +- Figures 「同一会社内の乗り継ぎは除く」 (exclude transfers within the same company). + +The table number changes between editions (私鉄 was 4-12 in 令和6年 but 4-13 in 令和5年), so the route resolves the current edition and table through the catalog API by name rather than a fixed URL. + +Attribution required by the licence: 出典:東京都統計年鑑(東京都総務局統計部).`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: false, + }, +}; From 8cda8f80b67fa70fcc342b9604154d28623df76d Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 15 Sep 2026 23:30:21 +0800 Subject: [PATCH 0824/1190] fix(route): add input validations (#23304) * fix(route): add input validation * fix(route): more input validation * fix(route): improve handling of publication date and category extraction --- lib/registry-dev.test.ts | 22 +++++++++++++++++++++- lib/registry-dev.ts | 4 ++-- lib/routes/10000link/info.ts | 6 ++++++ lib/routes/199it/index.tsx | 2 +- lib/routes/1lou/index.ts | 2 +- lib/routes/423down/index.ts | 2 +- lib/routes/4ksj/forum.tsx | 6 ++++++ lib/routes/6v123/index.ts | 2 +- lib/routes/abc/index.ts | 4 ++-- lib/routes/accessbriefing/index.ts | 2 +- lib/routes/amazfitwatchfaces/index.ts | 6 ++++++ lib/routes/app-sales/index.ts | 6 ++++++ lib/routes/apple/exchange-repair.ts | 6 ++++++ lib/routes/apple/security-releases.ts | 6 ++++++ lib/routes/aqara/post.tsx | 4 ++-- lib/routes/auto-stats/index.ts | 6 ++++++ lib/routes/bnu/mba.ts | 2 +- lib/routes/cbaigui/index.ts | 2 +- lib/routes/ccagm/index.ts | 2 +- lib/routes/cccmc/index.ts | 2 +- lib/routes/ccg/index.ts | 6 ++++++ lib/routes/chinacdc/index.ts | 2 +- lib/routes/chinadaily/language.ts | 2 +- lib/routes/cngold/index.ts | 6 ++++++ lib/routes/cnljxh/index.ts | 6 ++++++ lib/routes/cqu/index.ts | 2 +- lib/routes/cursor/blog.ts | 6 ++++++ lib/routes/cursor/changelog.ts | 6 ++++++ lib/routes/deepl/blog.ts | 6 ++++++ lib/routes/dehenglaw/index.ts | 6 ++++++ lib/routes/dekudeals/index.ts | 5 +++++ lib/routes/dn/news.ts | 6 ++++++ lib/routes/douban/other/explore-column.ts | 6 ++++++ lib/routes/freecomputerbooks/index.tsx | 6 ++++++ lib/routes/gitstar-ranking/index.tsx | 6 ++++++ lib/routes/go/mhlw/pdf.ts | 2 +- lib/routes/gov/ah/kjt.ts | 2 +- lib/routes/gov/chongqing/gzw.ts | 2 +- lib/routes/gov/forestry/gjlckjdjt.ts | 6 ++++++ lib/routes/gov/general/general.ts | 5 +++++ lib/routes/gov/lswz/index.ts | 2 +- lib/routes/gov/moa/gjs.ts | 2 +- lib/routes/gov/nsfc/index.ts | 2 +- lib/routes/gov/safe/util.tsx | 6 ++++++ lib/routes/gov/sh/fgw/index.tsx | 2 +- lib/routes/gov/shenzhen/zzb/index.ts | 6 ++++++ lib/routes/gov/suzhou/fg.ts | 2 +- lib/routes/grainoil/category.ts | 6 ++++++ lib/routes/hit/hitgs.ts | 6 ++++++ lib/routes/hubu/index.ts | 2 +- lib/routes/hubu/zhxy.ts | 2 +- lib/routes/hust/gs.ts | 2 +- lib/routes/hust/mse.ts | 2 +- lib/routes/ianspriggs/index.ts | 6 ++++++ lib/routes/infoq/presentations.ts | 6 ++++++ lib/routes/investor/index.ts | 2 +- lib/routes/iqilu/program.ts | 2 +- lib/routes/jimmyspa/books.ts | 6 ++++++ lib/routes/jimmyspa/news.ts | 6 ++++++ lib/routes/kantarworldpanel/index.tsx | 2 +- lib/routes/koreaherald/index.ts | 4 ++-- lib/routes/lit/xwzx.ts | 6 ++++++ lib/routes/liulinblog/index.ts | 2 +- lib/routes/lrepacks/index.ts | 6 ++++++ lib/routes/m4/index.ts | 6 ++++++ lib/routes/musify/index.ts | 6 ++++++ lib/routes/p-articles/section.ts | 6 ++++++ lib/routes/papers/category.ts | 2 +- lib/routes/producereport/index.ts | 2 +- lib/routes/qqorw/index.ts | 6 ++++++ lib/routes/samrdprc/index.ts | 2 +- lib/routes/semiconductors/index.ts | 2 +- lib/routes/sspu/pe.ts | 6 ++++++ lib/routes/thoughtco/index.ts | 6 ++++++ lib/routes/weibo/timeline.ts | 18 ++++++++---------- lib/routes/whu/hyxt.ts | 2 +- lib/routes/xjtu/zs.ts | 2 +- lib/routes/xmnn/news.ts | 2 +- lib/routes/yangtzeu/dongke.ts | 16 ++++++---------- lib/routes/yicai/dt.ts | 6 ++++++ lib/routes/yna/index.ts | 10 +++------- lib/routes/zcool/user.ts | 2 +- 82 files changed, 308 insertions(+), 72 deletions(-) diff --git a/lib/registry-dev.test.ts b/lib/registry-dev.test.ts index bcca32857d42..27c453b4c8ee 100644 --- a/lib/registry-dev.test.ts +++ b/lib/registry-dev.test.ts @@ -41,6 +41,15 @@ const fakeDirectories = { handler: (ctx: Context) => ({ title: String(ctx.get('fromOuter')), link: 'https://example.com', item: [], allowEmpty: true }), }, }, + '/redirect.ts': { + route: { + path: '/redirect', + name: 'Redirect', + handler: (ctx: Context) => { + ctx.set('redirect', '/flat/single'); + }, + }, + }, '/boom.ts': { route: { path: '/boom', @@ -90,7 +99,7 @@ afterAll(() => { const buildApp = () => { const namespaces: NamespacesType = {}; const dev = createDevRegistry({ routesDirectory, namespaces }); - const app = new Hono<{ Variables: { fromOuter: string; data: Data; apiData: { ok: boolean } } }>(); + const app = new Hono<{ Variables: { fromOuter: string; data: Data; apiData: { ok: boolean }; redirect: string } }>(); app.use(async (ctx, next) => { ctx.set('fromOuter', 'bridged'); await next(); @@ -98,6 +107,10 @@ const buildApp = () => { if (apiData) { return ctx.json(apiData); } + const redirect = ctx.get('redirect'); + if (redirect) { + return ctx.redirect(redirect, 301); + } const data = ctx.get('data'); if (data) { return ctx.json(data); @@ -143,6 +156,13 @@ describe('createDevRegistry', () => { expect(body.title).toBe('bridged'); }); + it('finalizes handlers that only set a redirect', async () => { + const { app } = buildApp(); + const response = await app.request('/flat/redirect'); + expect(response.status).toBe(301); + expect(response.headers.get('location')).toBe('/flat/single'); + }); + it('serves nested namespaces', async () => { const { app } = buildApp(); const response = await app.request('/github/enterprise/news'); diff --git a/lib/registry-dev.ts b/lib/registry-dev.ts index 904cb717d747..8e396ed68156 100644 --- a/lib/registry-dev.ts +++ b/lib/registry-dev.ts @@ -46,7 +46,7 @@ export function createDevRegistry({ routesDirectory, namespaces }: { routesDirec outer.set(key as never, value as never); } } - if (!ctx.finalized && (ctx.get('data') || ctx.get('apiData'))) { + if (!ctx.finalized && (ctx.get('data') || ctx.get('apiData') || ctx.get('redirect') || ctx.get('no-content'))) { // Data-producing handlers return undefined (the outer template middleware renders the // bridged vars); finalize so Hono does not raise "Context is not finalized". ctx.res = new Response(null, { status: 204 }); @@ -104,7 +104,7 @@ export function createDevRegistry({ routesDirectory, namespaces }: { routesDirec outerContexts.set(ctx.req.raw, ctx); try { const response = await subApp.fetch(ctx.req.raw); - if (ctx.get('data') || ctx.get('apiData')) { + if (ctx.get('data') || ctx.get('apiData') || ctx.get('redirect') || ctx.get('no-content')) { // The upstream template middleware renders from the bridged vars return; } diff --git a/lib/routes/10000link/info.ts b/lib/routes/10000link/info.ts index 548f422f4caf..47235bf88d9d 100644 --- a/lib/routes/10000link/info.ts +++ b/lib/routes/10000link/info.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx: Context): Promise => { const { category = 'newslists', id } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://info.10000link.com'; diff --git a/lib/routes/199it/index.tsx b/lib/routes/199it/index.tsx index bb6c7f41a77b..5fd1470f062b 100644 --- a/lib/routes/199it/index.tsx +++ b/lib/routes/199it/index.tsx @@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://www.199it.com'; - const targetUrl: string = new URL(category, baseUrl).href; + const targetUrl = `${baseUrl}/${category}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/1lou/index.ts b/lib/routes/1lou/index.ts index ede610a7fdfe..23ce9c437117 100644 --- a/lib/routes/1lou/index.ts +++ b/lib/routes/1lou/index.ts @@ -18,7 +18,7 @@ export const handler = async (ctx: Context) => { .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) .join('&'); - const currentUrl = new URL(`${params && params.endsWith('.htm') ? params : `${params}.htm`}${queryString ? `?${queryString}` : ''}`, rootUrl).href; + const currentUrl = `${rootUrl}/${params && params.endsWith('.htm') ? params : `${params}.htm`}${queryString ? `?${queryString}` : ''}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/423down/index.ts b/lib/routes/423down/index.ts index d148b319cfbf..d9c3fd20e976 100644 --- a/lib/routes/423down/index.ts +++ b/lib/routes/423down/index.ts @@ -13,7 +13,7 @@ export const handler = async (ctx) => { const domain = '423down.com'; const rootUrl = `https://www.${domain}`; - const currentUrl = new URL(category, rootUrl).href; + const currentUrl = `${rootUrl}/${category}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/4ksj/forum.tsx b/lib/routes/4ksj/forum.tsx index ff6f115002cf..f328d2b404d2 100644 --- a/lib/routes/4ksj/forum.tsx +++ b/lib/routes/4ksj/forum.tsx @@ -2,12 +2,14 @@ import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import md5 from '@/utils/md5'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; const renderDescription = ({ images, title, keys, details, description, info, links }) => renderToString( @@ -79,6 +81,10 @@ function stringtoHex(acSTR) { async function handler(ctx) { const { id = '4k-uhd-1' } = ctx.req.param(); + if (!isValidHost(id)) { + throw new InvalidParameterError('Invalid id'); + } + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const rootUrl = 'https://www.4ksj.com'; diff --git a/lib/routes/6v123/index.ts b/lib/routes/6v123/index.ts index 447b423fd1cb..a2c58cafeee5 100644 --- a/lib/routes/6v123/index.ts +++ b/lib/routes/6v123/index.ts @@ -17,7 +17,7 @@ export const handler = async (ctx: Context): Promise => { const encoding = 'gb2312'; const baseUrl = 'https://www.hao6v.me'; - const targetUrl: string = new URL(category.startsWith('gvod') ? `${category}.html` : category, baseUrl).href; + const targetUrl = `${baseUrl}/${category.startsWith('gvod') ? `${category}.html` : category}`; const response = await ofetch(targetUrl, { responseType: 'arrayBuffer', diff --git a/lib/routes/abc/index.ts b/lib/routes/abc/index.ts index 03ce451a642e..aa3bf2261f1f 100644 --- a/lib/routes/abc/index.ts +++ b/lib/routes/abc/index.ts @@ -42,8 +42,8 @@ async function handler(ctx) { let currentUrl: string; let documentId; - if (Number.isNaN(category)) { - currentUrl = new URL(category, rootUrl).href; + if (Number.isNaN(Number(category))) { + currentUrl = `${rootUrl}/${category}`; } else { documentId = category; const feedUrl = new URL(`news/feed/${documentId}/rss.xml`, rootUrl).href; diff --git a/lib/routes/accessbriefing/index.ts b/lib/routes/accessbriefing/index.ts index 3a60978a0565..d21ce9051a86 100644 --- a/lib/routes/accessbriefing/index.ts +++ b/lib/routes/accessbriefing/index.ts @@ -12,7 +12,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://www.accessbriefing.com'; - const currentUrl = new URL(category, rootUrl).href; + const currentUrl = `${rootUrl}/${category}`; const apiUrl = new URL('Ajax/GetPagedArticles', rootUrl).href; const { data: currentResponse } = await got(currentUrl); diff --git a/lib/routes/amazfitwatchfaces/index.ts b/lib/routes/amazfitwatchfaces/index.ts index 51d5d2210872..3c91d975b38d 100644 --- a/lib/routes/amazfitwatchfaces/index.ts +++ b/lib/routes/amazfitwatchfaces/index.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx: Context): Promise => { const { device, sort, searchParams } = ctx.req.param(); + if (!isValidHost(device)) { + throw new InvalidParameterError('Invalid device'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://amazfitwatchfaces.com'; diff --git a/lib/routes/app-sales/index.ts b/lib/routes/app-sales/index.ts index 87dea3883b9d..d0118b52775a 100644 --- a/lib/routes/app-sales/index.ts +++ b/lib/routes/app-sales/index.ts @@ -2,14 +2,20 @@ import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; +import { isValidHost } from '@/utils/valid-host'; import { baseUrl, fetchItems } from './util'; export const handler = async (ctx: Context): Promise => { const { category = 'highlights', country = 'us' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '100'); const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href; diff --git a/lib/routes/apple/exchange-repair.ts b/lib/routes/apple/exchange-repair.ts index 64d595a11b3f..26ad1d408dc3 100644 --- a/lib/routes/apple/exchange-repair.ts +++ b/lib/routes/apple/exchange-repair.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; const host = 'https://support.apple.com/'; @@ -33,6 +35,10 @@ export const route: Route = { async function handler(ctx) { const country = ctx.req.param('country') ?? ''; + if (country && !isValidHost(country)) { + throw new InvalidParameterError('Invalid country'); + } + const link = new URL(`${country}/service-programs`, host).href; const response = await got(link); diff --git a/lib/routes/apple/security-releases.ts b/lib/routes/apple/security-releases.ts index ba76da9f098e..0c73d1face0e 100644 --- a/lib/routes/apple/security-releases.ts +++ b/lib/routes/apple/security-releases.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/security-releases'; export const handler = async (ctx: Context): Promise => { const { language: languageParam = 'en-us' } = ctx.req.param(); + if (!isValidHost(languageParam)) { + throw new InvalidParameterError('Invalid language'); + } + const language = languageParam as Language; const limit = Number(ctx.req.query('limit') ?? '30'); diff --git a/lib/routes/aqara/post.tsx b/lib/routes/aqara/post.tsx index b69c7ebc0f70..d58d71277956 100644 --- a/lib/routes/aqara/post.tsx +++ b/lib/routes/aqara/post.tsx @@ -38,7 +38,7 @@ async function handler(ctx) { const filterRegion = filterMatches[1]; const filterType = filterMatches[2] === 'tag' ? 'tags' : filterMatches[2] === 'category' ? 'categories' : filterMatches[2]; const filterKeyword = decodeURI(filterMatches[3].split('/').pop()); - const filterApiUrl = new URL(`${filterRegion}/${apiSlug}/${filterType}?search=${filterKeyword}`, rootUrl).href; + const filterApiUrl = `${rootUrl}/${filterRegion}/${apiSlug}/${filterType}?search=${filterKeyword}`; const { data: filterResponse } = await got(filterApiUrl); @@ -47,7 +47,7 @@ async function handler(ctx) { if (filter?.id ?? undefined) { filterName = filter.name ?? filterKeyword; currentUrl = filter.link ?? currentUrl; - apiUrl = new URL(`${filterRegion}/${apiSlug}/posts?_embed=true&per_page=${limit}&${filterType}=${filter.id}`, rootUrl).href; + apiUrl = `${rootUrl}/${filterRegion}/${apiSlug}/posts?_embed=true&per_page=${limit}&${filterType}=${filter.id}`; } } diff --git a/lib/routes/auto-stats/index.ts b/lib/routes/auto-stats/index.ts index d66ba7fcb86d..2d81b5b2212f 100644 --- a/lib/routes/auto-stats/index.ts +++ b/lib/routes/auto-stats/index.ts @@ -1,11 +1,13 @@ import { load } from 'cheerio'; import iconv from 'iconv-lite'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:category?', @@ -30,6 +32,10 @@ export const route: Route = { async function handler(ctx) { const { category = 'xxkd' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'http://www.auto-stats.org.cn'; diff --git a/lib/routes/bnu/mba.ts b/lib/routes/bnu/mba.ts index d627e5dfb31d..6be142fbe10a 100644 --- a/lib/routes/bnu/mba.ts +++ b/lib/routes/bnu/mba.ts @@ -10,7 +10,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://mba.bnu.edu.cn'; - const currentUrl = new URL(`${category.replace(/\/$/, '')}/`, rootUrl).href; + const currentUrl = `${rootUrl}/${category.replace(/\/$/, '')}/`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/cbaigui/index.ts b/lib/routes/cbaigui/index.ts index 39bf0757bd32..d1601beaef23 100644 --- a/lib/routes/cbaigui/index.ts +++ b/lib/routes/cbaigui/index.ts @@ -28,7 +28,7 @@ async function handler(ctx) { let filterName; const path = ctx.req.param('path') ?? ''; - const currentUrl = new URL(`/${path}`, rootUrl).href; + const currentUrl = `${rootUrl}/${path}`; let apiUrl = new URL(`${apiSlug}/posts?_embed=true&per_page=${limit}`, rootUrl).href; const filterMatches = path.match(/^post-(tag|category)\/(.*)$/); diff --git a/lib/routes/ccagm/index.ts b/lib/routes/ccagm/index.ts index 097d0c628858..e6c95f07c1a2 100644 --- a/lib/routes/ccagm/index.ts +++ b/lib/routes/ccagm/index.ts @@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '10'); const baseUrl = 'http://www.ccagm.org.cn'; - const targetUrl: string = new URL(category, baseUrl).href; + const targetUrl = `${baseUrl}/${category}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/cccmc/index.ts b/lib/routes/cccmc/index.ts index 5500c30f9b5b..b98794e57ad3 100644 --- a/lib/routes/cccmc/index.ts +++ b/lib/routes/cccmc/index.ts @@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '15'); const baseUrl = 'https://www.cccmc.org.cn'; - const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href; + const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/ccg/index.ts b/lib/routes/ccg/index.ts index ff908ca92e56..cea64b01887f 100644 --- a/lib/routes/ccg/index.ts +++ b/lib/routes/ccg/index.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx: Context): Promise => { const { category = 'news' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '7'); const baseUrl = 'http://www.ccg.org.cn'; diff --git a/lib/routes/chinacdc/index.ts b/lib/routes/chinacdc/index.ts index 2236c24cb69b..776faa279be8 100644 --- a/lib/routes/chinacdc/index.ts +++ b/lib/routes/chinacdc/index.ts @@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '11'); const rootUrl = 'https://www.chinacdc.cn'; - const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, rootUrl).href; + const targetUrl = `${rootUrl}/${category.endsWith('/') ? category : `${category}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/chinadaily/language.ts b/lib/routes/chinadaily/language.ts index b8d97fb28c6b..5d0df3668ffa 100644 --- a/lib/routes/chinadaily/language.ts +++ b/lib/routes/chinadaily/language.ts @@ -17,7 +17,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://language.chinadaily.com.cn'; - const targetUrl: string = new URL(category, baseUrl).href; + const targetUrl = `${baseUrl}/${category}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/cngold/index.ts b/lib/routes/cngold/index.ts index ebe63c4acaed..cf360645983a 100644 --- a/lib/routes/cngold/index.ts +++ b/lib/routes/cngold/index.ts @@ -1,12 +1,18 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx) => { const { category = 'news-325' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12; const rootUrl = 'https://www.cngold.org.cn'; diff --git a/lib/routes/cnljxh/index.ts b/lib/routes/cnljxh/index.ts index 72484cd16204..c29a7079d662 100644 --- a/lib/routes/cnljxh/index.ts +++ b/lib/routes/cnljxh/index.ts @@ -3,14 +3,20 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx: Context): Promise => { const { category = 'news', id = '10' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '20'); const baseUrl = 'https://www.cnljxh.org.cn'; diff --git a/lib/routes/cqu/index.ts b/lib/routes/cqu/index.ts index 76fa362707fa..112ff6e24b60 100644 --- a/lib/routes/cqu/index.ts +++ b/lib/routes/cqu/index.ts @@ -37,7 +37,7 @@ export const route: Route = { async function handler(ctx) { const { path = 'index/tzgg' } = ctx.req.param(); const baseUrl = 'http://jwc.cqu.edu.cn'; - const url = new URL(`${path}.htm`, baseUrl).href; + const url = `${baseUrl}/${path}.htm`; const response = await ofetch(url); const $ = load(response); diff --git a/lib/routes/cursor/blog.ts b/lib/routes/cursor/blog.ts index 6db1ea037604..7b3b3cecf8bc 100644 --- a/lib/routes/cursor/blog.ts +++ b/lib/routes/cursor/blog.ts @@ -1,13 +1,19 @@ import { load } from 'cheerio'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx: Context): Promise => { const { topic, locale } = ctx.req.param(); + if (locale && !isValidHost(locale)) { + throw new InvalidParameterError('Invalid locale'); + } + const limit = Number(ctx.req.query('limit') ?? '10'); const baseUrl = 'https://cursor.com'; diff --git a/lib/routes/cursor/changelog.ts b/lib/routes/cursor/changelog.ts index 496069982de5..cefb0c10afd8 100644 --- a/lib/routes/cursor/changelog.ts +++ b/lib/routes/cursor/changelog.ts @@ -3,13 +3,19 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx: Context): Promise => { const locale = ctx.req.param('locale'); + if (locale && !isValidHost(locale)) { + throw new InvalidParameterError('Invalid locale'); + } + const limit = Number(ctx.req.query('limit') ?? '100'); const baseUrl = 'https://cursor.com'; diff --git a/lib/routes/deepl/blog.ts b/lib/routes/deepl/blog.ts index 9f0355179723..a885c52ea70a 100644 --- a/lib/routes/deepl/blog.ts +++ b/lib/routes/deepl/blog.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx: Context): Promise => { const { lang = 'en' } = ctx.req.param(); + if (!isValidHost(lang)) { + throw new InvalidParameterError('Invalid lang'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://www.deepl.com'; diff --git a/lib/routes/dehenglaw/index.ts b/lib/routes/dehenglaw/index.ts index 7c6e3e057e88..884f7f1bc026 100644 --- a/lib/routes/dehenglaw/index.ts +++ b/lib/routes/dehenglaw/index.ts @@ -1,14 +1,20 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx) => { const { language = 'CN', category = 'paper' } = ctx.req.param(); + if (!isValidHost(language)) { + throw new InvalidParameterError('Invalid language'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 6; const rootUrl = 'https://www.dehenglaw.com'; diff --git a/lib/routes/dekudeals/index.ts b/lib/routes/dekudeals/index.ts index add54ce9ae3f..c7d2687a069a 100644 --- a/lib/routes/dekudeals/index.ts +++ b/lib/routes/dekudeals/index.ts @@ -1,8 +1,10 @@ import { load } from 'cheerio'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import ofetch from '@/utils/ofetch'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:type', @@ -16,6 +18,9 @@ export const route: Route = { async function handler(ctx: Context) { const { type } = ctx.req.param(); + if (!isValidHost(type)) { + throw new InvalidParameterError('Invalid type'); + } const host = 'https://www.dekudeals.com/'; const link = new URL(type, host).href; diff --git a/lib/routes/dn/news.ts b/lib/routes/dn/news.ts index 666cdebb98cb..72c9b4381069 100644 --- a/lib/routes/dn/news.ts +++ b/lib/routes/dn/news.ts @@ -1,10 +1,12 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -42,6 +44,10 @@ export const route: Route = { async function handler(ctx) { const { language, category = '' } = ctx.req.param(); + if (!isValidHost(language)) { + throw new InvalidParameterError('Invalid language'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; const rootUrl = 'https://dn.com'; diff --git a/lib/routes/douban/other/explore-column.ts b/lib/routes/douban/other/explore-column.ts index 15ecfa686160..fc01746dda7c 100644 --- a/lib/routes/douban/other/explore-column.ts +++ b/lib/routes/douban/other/explore-column.ts @@ -1,7 +1,9 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import got from '@/utils/got'; +import { isValidHost } from '@/utils/valid-host'; const host = 'https://www.douban.com/explore/column/'; export const route: Route = { @@ -16,6 +18,10 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); + if (!isValidHost(id)) { + throw new InvalidParameterError('Invalid id'); + } + const link = new URL(id, host).href; const response = await got.get(link); const $ = load(response.data); diff --git a/lib/routes/freecomputerbooks/index.tsx b/lib/routes/freecomputerbooks/index.tsx index a062e0894bb0..4c63f1379369 100644 --- a/lib/routes/freecomputerbooks/index.tsx +++ b/lib/routes/freecomputerbooks/index.tsx @@ -2,9 +2,11 @@ import { load } from 'cheerio'; import { raw } from 'hono/html'; import { renderToString } from 'hono/jsx/dom/server'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; +import { isValidHost } from '@/utils/valid-host'; const baseURL = 'https://freecomputerbooks.com/'; @@ -43,6 +45,10 @@ export const route: Route = { async function handler(ctx) { const categoryId = ctx.req.param('category')?.trim(); + if (categoryId && !isValidHost(categoryId)) { + throw new InvalidParameterError('Invalid category'); + } + const requestURL = categoryId ? new URL(`${categoryId}.html`, baseURL).href : baseURL; const $ = await cheerioLoad(requestURL); diff --git a/lib/routes/gitstar-ranking/index.tsx b/lib/routes/gitstar-ranking/index.tsx index 2d7bad89266d..ae43b09768fc 100644 --- a/lib/routes/gitstar-ranking/index.tsx +++ b/lib/routes/gitstar-ranking/index.tsx @@ -4,9 +4,11 @@ import type { Element } from 'domhandler'; import type { Context } from 'hono'; import { renderToString } from 'hono/jsx/dom/server'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; +import { isValidHost } from '@/utils/valid-host'; const renderDescription = ({ images, stargazersCount, language, description }) => renderToString( @@ -49,6 +51,10 @@ const renderDescription = ({ images, stargazersCount, language, description }) = export const handler = async (ctx: Context): Promise => { const { category = 'repositories' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '100'); const baseUrl = 'https://gitstar-ranking.com'; diff --git a/lib/routes/go/mhlw/pdf.ts b/lib/routes/go/mhlw/pdf.ts index e7ac8da2b272..039c180a097d 100644 --- a/lib/routes/go/mhlw/pdf.ts +++ b/lib/routes/go/mhlw/pdf.ts @@ -8,7 +8,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://www.mhlw.go.jp'; - const currentUrl = new URL(category.endsWith('.html') ? category : `${category}.html`, rootUrl).href; + const currentUrl = `${rootUrl}/${category.endsWith('.html') ? category : `${category}.html`}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/ah/kjt.ts b/lib/routes/gov/ah/kjt.ts index cfa012e4ab67..2cd2ab9e6a2e 100644 --- a/lib/routes/gov/ah/kjt.ts +++ b/lib/routes/gov/ah/kjt.ts @@ -11,7 +11,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://kjt.ah.gov.cn'; - const currentUrl = new URL(`${category.replace(/\/$/, '').replace(/\/index\.html$/, '')}/`, rootUrl).href; + const currentUrl = `${rootUrl}/${category.replace(/\/$/, '').replace(/\/index\.html$/, '')}/`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/chongqing/gzw.ts b/lib/routes/gov/chongqing/gzw.ts index 8111bc0238eb..03ceb828fc0a 100644 --- a/lib/routes/gov/chongqing/gzw.ts +++ b/lib/routes/gov/chongqing/gzw.ts @@ -33,7 +33,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 15; const rootUrl = 'https://gzw.cq.gov.cn'; - const currentUrl = new URL(category, rootUrl).href; + const currentUrl = `${rootUrl}/${category}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/forestry/gjlckjdjt.ts b/lib/routes/gov/forestry/gjlckjdjt.ts index 8fc23b6ebf38..3a6ca04daf7c 100644 --- a/lib/routes/gov/forestry/gjlckjdjt.ts +++ b/lib/routes/gov/forestry/gjlckjdjt.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -35,6 +37,10 @@ export const route: Route = { async function handler(ctx) { const { category = 'gjlckjdjt' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'http://www.forestry.gov.cn'; diff --git a/lib/routes/gov/general/general.ts b/lib/routes/gov/general/general.ts index 236306b79121..ff82fd4f966a 100644 --- a/lib/routes/gov/general/general.ts +++ b/lib/routes/gov/general/general.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import cache from '@/utils/cache'; import { getSubPath } from '@/utils/common-utils'; // 来人拯救一下啊( >﹏<。) @@ -40,6 +41,7 @@ import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; import { finishArticleItem } from '@/utils/wechat-mp'; import { renderZcjdpt } from './templates/zcjdpt'; @@ -49,6 +51,9 @@ const gdgov = async (info, ctx) => { .split('/') .filter((item) => item !== ''); const [site, branch] = path; + if (!isValidHost(site) || !isValidHost(branch)) { + throw new InvalidParameterError('Invalid site or branch'); + } // 网站 const pathstartat = info.pathstartat === undefined ? 0 : info.pathstartat; diff --git a/lib/routes/gov/lswz/index.ts b/lib/routes/gov/lswz/index.ts index f9a208aa0d3f..78412391dda1 100644 --- a/lib/routes/gov/lswz/index.ts +++ b/lib/routes/gov/lswz/index.ts @@ -11,7 +11,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 25; const rootUrl = 'https://www.lswz.gov.cn'; - const currentUrl = new URL(`${category}.shtml`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.shtml`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/moa/gjs.ts b/lib/routes/gov/moa/gjs.ts index 8ccf7f802938..d3cc608ccd84 100644 --- a/lib/routes/gov/moa/gjs.ts +++ b/lib/routes/gov/moa/gjs.ts @@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'http://www.gjs.moa.gov.cn'; - const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href; + const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/gov/nsfc/index.ts b/lib/routes/gov/nsfc/index.ts index f899b5bb667b..482585c40e43 100644 --- a/lib/routes/gov/nsfc/index.ts +++ b/lib/routes/gov/nsfc/index.ts @@ -67,7 +67,7 @@ async function handler(ctx) { } const rootUrl = 'https://www.nsfc.gov.cn'; - const currentUrl = new URL((thePath.endsWith('/more') ? `${thePath}.htm` : thePath) || 'publish/portal0/tab442/', rootUrl).href; + const currentUrl = `${rootUrl}${(thePath.endsWith('/more') ? `${thePath}.htm` : thePath) || '/publish/portal0/tab442/'}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/safe/util.tsx b/lib/routes/gov/safe/util.tsx index 65838a7743f4..04b8c872759e 100644 --- a/lib/routes/gov/safe/util.tsx +++ b/lib/routes/gov/safe/util.tsx @@ -1,9 +1,11 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Language } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; const rootUrl = 'https://www.safe.gov.cn'; @@ -16,6 +18,10 @@ const zxfkCategoryApis = { }; const processZxfkItems = async (site = 'beijing', category = 'ywzx', limit = 3) => { + if (!isValidHost(site)) { + throw new InvalidParameterError('Invalid site'); + } + const apiUrl = new URL(`${zxfkCategoryApis[category]}${site}`, rootUrl).href; const currentUrl = new URL(`${site}/${category}/index.html`, rootUrl).href; diff --git a/lib/routes/gov/sh/fgw/index.tsx b/lib/routes/gov/sh/fgw/index.tsx index 1ab206ca2349..f655be74cb80 100644 --- a/lib/routes/gov/sh/fgw/index.tsx +++ b/lib/routes/gov/sh/fgw/index.tsx @@ -28,7 +28,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://fgw.sh.gov.cn'; - const currentUrl = new URL(`${category}/index.html`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}/index.html`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/gov/shenzhen/zzb/index.ts b/lib/routes/gov/shenzhen/zzb/index.ts index b3c6a97cc65b..924196ec3184 100644 --- a/lib/routes/gov/shenzhen/zzb/index.ts +++ b/lib/routes/gov/shenzhen/zzb/index.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; const rootURL = 'http://www.zzb.sz.gov.cn/'; @@ -36,6 +38,10 @@ export const route: Route = { async function handler(ctx) { const categoryID = ctx.req.param('caty'); + if (!isValidHost(categoryID)) { + throw new InvalidParameterError('Invalid caty'); + } + const page = ctx.req.param('page') ?? '1'; const pageParam = Number.parseInt(page) > 1 ? `_${page}` : ''; diff --git a/lib/routes/gov/suzhou/fg.ts b/lib/routes/gov/suzhou/fg.ts index 2f5e8f05aea3..1602e481e139 100644 --- a/lib/routes/gov/suzhou/fg.ts +++ b/lib/routes/gov/suzhou/fg.ts @@ -30,7 +30,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://fg.suzhou.gov.cn'; - const currentUrl = new URL(`${categoryPath}.shtml`, rootUrl).href; + const currentUrl = `${rootUrl}/${categoryPath}.shtml`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/grainoil/category.ts b/lib/routes/grainoil/category.ts index abd7f2529537..600aa1b7fc1c 100644 --- a/lib/routes/grainoil/category.ts +++ b/lib/routes/grainoil/category.ts @@ -3,15 +3,21 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx: Context): Promise => { const { category, id } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'http://load.grainoil.com.cn'; diff --git a/lib/routes/hit/hitgs.ts b/lib/routes/hit/hitgs.ts index 31f5a43207cf..75acb731e4be 100644 --- a/lib/routes/hit/hitgs.ts +++ b/lib/routes/hit/hitgs.ts @@ -3,16 +3,22 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Language, Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx: Context): Promise => { const { id = 'tzgg' } = ctx.req.param(); + if (!isValidHost(id)) { + throw new InvalidParameterError('Invalid id'); + } + const limit = Number(ctx.req.query('limit') ?? '10'); const baseUrl = 'https://hitgs.hit.edu.cn'; diff --git a/lib/routes/hubu/index.ts b/lib/routes/hubu/index.ts index 2830d4a9a374..1655b125aacc 100644 --- a/lib/routes/hubu/index.ts +++ b/lib/routes/hubu/index.ts @@ -10,7 +10,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://www.hubu.edu.cn'; - const currentUrl = new URL(`${category}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.htm`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/hubu/zhxy.ts b/lib/routes/hubu/zhxy.ts index 2dd9fd5728bb..d288a96526ff 100644 --- a/lib/routes/hubu/zhxy.ts +++ b/lib/routes/hubu/zhxy.ts @@ -10,7 +10,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://zhxy.hubu.edu.cn'; - const currentUrl = new URL(`${category}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.htm`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/hust/gs.ts b/lib/routes/hust/gs.ts index 9cc11366dae0..9e8b6d2962ed 100644 --- a/lib/routes/hust/gs.ts +++ b/lib/routes/hust/gs.ts @@ -10,7 +10,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 16; const rootUrl = 'https://gs.hust.edu.cn'; - const currentUrl = new URL(`${category}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.htm`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/hust/mse.ts b/lib/routes/hust/mse.ts index 6b4981c0b451..73829c81620b 100644 --- a/lib/routes/hust/mse.ts +++ b/lib/routes/hust/mse.ts @@ -11,7 +11,7 @@ export const handler = async (ctx) => { const domain = 'mse.hust.edu.cn'; const rootUrl = `https://${domain}`; - const currentUrl = new URL(`${category}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.htm`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/ianspriggs/index.ts b/lib/routes/ianspriggs/index.ts index 97f33171edc9..3e6a874a4b35 100644 --- a/lib/routes/ianspriggs/index.ts +++ b/lib/routes/ianspriggs/index.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -30,6 +32,10 @@ export const route: Route = { async function handler(ctx) { const { category = 'portraits' } = ctx.req.param(); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const author = 'Ian Spriggs'; diff --git a/lib/routes/infoq/presentations.ts b/lib/routes/infoq/presentations.ts index e7b2544822ad..a9d710409128 100644 --- a/lib/routes/infoq/presentations.ts +++ b/lib/routes/infoq/presentations.ts @@ -1,14 +1,20 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx) => { const { conference } = ctx.req.param(); + if (conference && !isValidHost(conference)) { + throw new InvalidParameterError('Invalid conference'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12; const rootUrl = 'https://www.infoq.com'; diff --git a/lib/routes/investor/index.ts b/lib/routes/investor/index.ts index cadf31bf0ee4..d893660c29c7 100644 --- a/lib/routes/investor/index.ts +++ b/lib/routes/investor/index.ts @@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '20'); const baseUrl = 'https://www.investor.org.cn'; - const targetUrl: string = new URL(id.endsWith('/') ? id : `${id}/`, baseUrl).href; + const targetUrl = `${baseUrl}/${id.endsWith('/') ? id : `${id}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/iqilu/program.ts b/lib/routes/iqilu/program.ts index bd0005997e58..88f57fd712a0 100644 --- a/lib/routes/iqilu/program.ts +++ b/lib/routes/iqilu/program.ts @@ -44,7 +44,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'http://v.iqilu.com'; - const currentUrl = new URL(category, rootUrl).href; + const currentUrl = `${rootUrl}/${category}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/jimmyspa/books.ts b/lib/routes/jimmyspa/books.ts index fd8b6903c973..9aa3f36604be 100644 --- a/lib/routes/jimmyspa/books.ts +++ b/lib/routes/jimmyspa/books.ts @@ -1,10 +1,12 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -40,6 +42,10 @@ export const route: Route = { async function handler(ctx) { const language = ctx.req.param('language'); + if (!isValidHost(language)) { + throw new InvalidParameterError('Invalid language'); + } + const baseUrl = 'https://www.jimmyspa.com'; const booksListUrl = new URL(`/${language}/Books/Ajax/changeList?year=&keyword=&categoryId=0&page=1`, baseUrl).href; diff --git a/lib/routes/jimmyspa/news.ts b/lib/routes/jimmyspa/news.ts index e1d98843a81c..da06d566a7b2 100644 --- a/lib/routes/jimmyspa/news.ts +++ b/lib/routes/jimmyspa/news.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -39,6 +41,10 @@ export const route: Route = { async function handler(ctx) { const language = ctx.req.param('language'); + if (!isValidHost(language)) { + throw new InvalidParameterError('Invalid language'); + } + const rootUrl = 'https://www.jimmyspa.com'; const currentUrl = new URL(`/${language}/News/Ajax/changeList?year=&keyword=&categoryId=0&page=1`, rootUrl).href; diff --git a/lib/routes/kantarworldpanel/index.tsx b/lib/routes/kantarworldpanel/index.tsx index 20f928fdc7ee..b182fa1cba47 100644 --- a/lib/routes/kantarworldpanel/index.tsx +++ b/lib/routes/kantarworldpanel/index.tsx @@ -92,7 +92,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://www.kantarworldpanel.com/'; - const currentUrl = new URL(`${region}/${category}`, rootUrl).href; + const currentUrl = `${rootUrl}${region}/${category}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/koreaherald/index.ts b/lib/routes/koreaherald/index.ts index 96b6d7407819..f8b2825f438a 100644 --- a/lib/routes/koreaherald/index.ts +++ b/lib/routes/koreaherald/index.ts @@ -39,7 +39,7 @@ async function handler(ctx) { const category = ctx.req.param('category') ?? 'National'; const baseUrl = 'https://www.koreaherald.com/'; - const response = await got(new URL(category, baseUrl).href); + const response = await got(`${baseUrl}${category}`); const $ = load(response.data); const title = $('ul.gnb').find('[class="on"]').length > 0 ? $('ul.gnb').find('[class="on"]').text() : $('div.nav_area > a.category').text(); const list = $('article.recent_news > ul.news_list > li') @@ -63,7 +63,7 @@ async function handler(ctx) { ); return { title: `The Korea Herald - ${title}`, - link: new URL(category, baseUrl).href, + link: `${baseUrl}${category}`, item: items, }; } diff --git a/lib/routes/lit/xwzx.ts b/lib/routes/lit/xwzx.ts index d4d465525405..e843db677adf 100644 --- a/lib/routes/lit/xwzx.ts +++ b/lib/routes/lit/xwzx.ts @@ -1,11 +1,13 @@ import { load } from 'cheerio'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/xwzx/:name?', @@ -31,6 +33,10 @@ const nameProps = { async function handler(ctx: Context): Promise { const { name = 'all' } = ctx.req.param(); + if (!isValidHost(name)) { + throw new InvalidParameterError('Invalid name'); + } + const categories = name === 'all' ? Object.keys(nameProps) : [name]; const lists = await Promise.all( diff --git a/lib/routes/liulinblog/index.ts b/lib/routes/liulinblog/index.ts index 3e53dc330b31..8bcda5db0fea 100644 --- a/lib/routes/liulinblog/index.ts +++ b/lib/routes/liulinblog/index.ts @@ -50,7 +50,7 @@ export async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20; const rootUrl = 'https://www.liulinblog.com'; - const currentUrl = subPath === '/' ? rootUrl : new URL(subPath, rootUrl).href; + const currentUrl = subPath === '/' ? rootUrl : `${rootUrl}${subPath}`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/lrepacks/index.ts b/lib/routes/lrepacks/index.ts index 044c1fa7602e..c23d2c5ab819 100644 --- a/lib/routes/lrepacks/index.ts +++ b/lib/routes/lrepacks/index.ts @@ -1,14 +1,20 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const handler = async (ctx) => { const { category = '' } = ctx.req.param(); + if (category && !isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12; const rootUrl = 'https://lrepacks.net'; diff --git a/lib/routes/m4/index.ts b/lib/routes/m4/index.ts index c131e5dc1ec4..b5854b0ed229 100644 --- a/lib/routes/m4/index.ts +++ b/lib/routes/m4/index.ts @@ -1,11 +1,13 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -36,6 +38,10 @@ export const route: Route = { export async function handler(ctx) { const [id, category = 'china'] = getSubPath(ctx).split('/').filter(Boolean); + if (!isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = `http://${id}.m4.cn`; diff --git a/lib/routes/musify/index.ts b/lib/routes/musify/index.ts index e45620508db0..911a9de9588b 100644 --- a/lib/routes/musify/index.ts +++ b/lib/routes/musify/index.ts @@ -3,12 +3,18 @@ import { load } from 'cheerio'; import type { Element } from 'domhandler'; import type { Context } from 'hono'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Data, DataItem, Route } from '@/types'; import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; +import { isValidHost } from '@/utils/valid-host'; export const handler = async (ctx: Context): Promise => { const { language = '' } = ctx.req.param(); + if (language && !isValidHost(language)) { + throw new InvalidParameterError('Invalid language'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://musify.club'; diff --git a/lib/routes/p-articles/section.ts b/lib/routes/p-articles/section.ts index d550753a51b4..45d15ba9f229 100644 --- a/lib/routes/p-articles/section.ts +++ b/lib/routes/p-articles/section.ts @@ -1,8 +1,10 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Language, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; +import { isValidHost } from '@/utils/valid-host'; import { ProcessFeed, rootUrl } from './utils'; @@ -23,6 +25,10 @@ export const route: Route = { async function handler(ctx) { let sectionName: string = ctx.req.param('section'); + if (!isValidHost(sectionName)) { + throw new InvalidParameterError('Invalid section'); + } + sectionName = sectionName.replace('-', '/'); sectionName += '/'; const sectionUrl = new URL(sectionName, rootUrl).href; diff --git a/lib/routes/papers/category.ts b/lib/routes/papers/category.ts index 3d336ef13291..3b161a37d0f1 100644 --- a/lib/routes/papers/category.ts +++ b/lib/routes/papers/category.ts @@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '50'); const baseUrl = 'https://papers.cool'; - const targetUrl: string = new URL(`${id}?show=${limit}`, baseUrl).href; + const targetUrl = `${baseUrl}/${id}?show=${limit}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/producereport/index.ts b/lib/routes/producereport/index.ts index 6e99f5d0df40..d9e7194cc587 100644 --- a/lib/routes/producereport/index.ts +++ b/lib/routes/producereport/index.ts @@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '10'); const baseUrl = 'https://www.producereport.com'; - const targetUrl: string = new URL(category, baseUrl).href; + const targetUrl = `${baseUrl}/${category}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/qqorw/index.ts b/lib/routes/qqorw/index.ts index 20ac53e7f215..65e15e0414ca 100644 --- a/lib/routes/qqorw/index.ts +++ b/lib/routes/qqorw/index.ts @@ -1,10 +1,12 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:category?', @@ -34,6 +36,10 @@ export const route: Route = { async function handler(ctx) { const { category = '' } = ctx.req.param(); + if (category && !isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; const rootUrl = 'https://qqorw.cn'; diff --git a/lib/routes/samrdprc/index.ts b/lib/routes/samrdprc/index.ts index 2c19525c8f59..087343110fa2 100644 --- a/lib/routes/samrdprc/index.ts +++ b/lib/routes/samrdprc/index.ts @@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '17'); const baseUrl = 'https://www.samrdprc.org.cn'; - const targetUrl: string = new URL(id.endsWith('/') ? id : `${id}/`, baseUrl).href; + const targetUrl = `${baseUrl}/${id.endsWith('/') ? id : `${id}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/semiconductors/index.ts b/lib/routes/semiconductors/index.ts index 8c845e0ef1c6..98b7fe71f65c 100644 --- a/lib/routes/semiconductors/index.ts +++ b/lib/routes/semiconductors/index.ts @@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '12'); const baseUrl = 'https://www.semiconductors.org'; - const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href; + const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/sspu/pe.ts b/lib/routes/sspu/pe.ts index d9a386d600e6..1f9d6519294a 100644 --- a/lib/routes/sspu/pe.ts +++ b/lib/routes/sspu/pe.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/pe/:id?', @@ -67,6 +69,10 @@ export const route: Route = { async function handler(ctx) { const { id = '342' } = ctx.req.param(); + if (!isValidHost(id)) { + throw new InvalidParameterError('Invalid id'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://pe2016.sspu.edu.cn'; diff --git a/lib/routes/thoughtco/index.ts b/lib/routes/thoughtco/index.ts index a12a26365958..f7c1051bc5c5 100644 --- a/lib/routes/thoughtco/index.ts +++ b/lib/routes/thoughtco/index.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -323,6 +325,10 @@ export const route: Route = { async function handler(ctx) { const { category = '' } = ctx.req.param(); + if (category && !isValidHost(category)) { + throw new InvalidParameterError('Invalid category'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50; const rootUrl = 'https://www.thoughtco.com'; diff --git a/lib/routes/weibo/timeline.ts b/lib/routes/weibo/timeline.ts index f48045994101..c102bfcdbb2e 100644 --- a/lib/routes/weibo/timeline.ts +++ b/lib/routes/weibo/timeline.ts @@ -22,7 +22,8 @@ export const route: Route = { }, { name: 'WEIBO_REDIRECT_URL', - description: '', + optional: true, + description: "OAuth callback URL. Defaults to `/weibo/timeline/0`. Set it when the auto-composed URL doesn't work", }, ], requirePuppeteer: false, @@ -93,7 +94,7 @@ async function handler(ctx) { ); // 检查token失效 if (response.error !== undefined) { - const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0' } = config.weibo; + const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0` } = config.weibo; ctx.status = 302; ctx.set({ @@ -164,8 +165,8 @@ async function handler(ctx) { item: resultItem, }); } - if (uid === '0' || ctx.req.query()) { - const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0', app_secret = '' } = config.weibo; + if (uid === '0' || ctx.req.query('code')) { + const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0`, app_secret = '' } = config.weibo; const code = ctx.req.query('code'); const routeParams = ctx.req.query('state'); @@ -176,14 +177,11 @@ async function handler(ctx) { const expires_in = rep.data.expires_in; await cache.set('weibotimelineuid' + uid, token, expires_in); - ctx.set({ - 'Content-Type': 'text/html; charset=UTF-8', - 'Cache-Control': 'no-cache', - }); - ctx.html(``); + ctx.header('Cache-Control', 'no-cache'); + return ctx.redirect(`/weibo/timeline/${uid}${routeParams ? `/${routeParams}` : ''}`); } } else { - const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0' } = config.weibo; + const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0` } = config.weibo; ctx.status = 302; ctx.set({ diff --git a/lib/routes/whu/hyxt.ts b/lib/routes/whu/hyxt.ts index d4887b7f0f37..978f74cd5ef6 100644 --- a/lib/routes/whu/hyxt.ts +++ b/lib/routes/whu/hyxt.ts @@ -61,7 +61,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = `https://hyxt.${domain}`; - const currentUrl = new URL(`${category}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}.htm`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/xjtu/zs.ts b/lib/routes/xjtu/zs.ts index dda131258707..f352bda59d14 100644 --- a/lib/routes/xjtu/zs.ts +++ b/lib/routes/xjtu/zs.ts @@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise => { const limit = Number(ctx.req.query('limit') ?? '30'); const baseUrl = 'https://zs.xjtu.edu.cn'; - const targetUrl: string = new URL(`${category}.htm`, baseUrl).href; + const targetUrl = `${baseUrl}/${category}.htm`; const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); diff --git a/lib/routes/xmnn/news.ts b/lib/routes/xmnn/news.ts index 16a6ce3b965a..b77387208148 100644 --- a/lib/routes/xmnn/news.ts +++ b/lib/routes/xmnn/news.ts @@ -46,7 +46,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://news.xmnn.cn'; - const currentUrl = new URL(`${category}/`, rootUrl).href; + const currentUrl = `${rootUrl}/${category}/`; const { data: response } = await got(currentUrl); diff --git a/lib/routes/yangtzeu/dongke.ts b/lib/routes/yangtzeu/dongke.ts index 2cd9f38c97fd..3632a24ffa17 100644 --- a/lib/routes/yangtzeu/dongke.ts +++ b/lib/routes/yangtzeu/dongke.ts @@ -25,7 +25,7 @@ async function handler(ctx): Promise { const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10; const rootUrl = 'https://dongke.yangtzeu.edu.cn'; - const currentUrl = new URL(`/${ctx.req.param('path') ?? 'yqzl/xyxw'}.htm`, rootUrl).href; + const currentUrl = `${rootUrl}/${ctx.req.param('path') ?? 'yqzl/xyxw'}.htm`; const { data: response } = await got(currentUrl); @@ -52,15 +52,11 @@ async function handler(ctx): Promise { item.title = content('title').text(); item.description = content('div.v_news_content').html(); - item.category = content('meta[name="keywords"]').prop('content').split(','); - item.pubDate = timezone( - parseDate( - content('p.content-info') - .text() - .match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)![0] - ), - 8 - ); + item.category = content('meta[name="keywords"]').prop('content')?.split(','); + const pubDate = content('p.content-info') + .text() + .match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)?.[0]; + item.pubDate = pubDate ? timezone(parseDate(pubDate), 8) : undefined; return item; }) diff --git a/lib/routes/yicai/dt.ts b/lib/routes/yicai/dt.ts index 76cb760c9da2..641cb4a83849 100644 --- a/lib/routes/yicai/dt.ts +++ b/lib/routes/yicai/dt.ts @@ -1,9 +1,11 @@ import { load } from 'cheerio'; +import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Language, Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; +import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; @@ -74,6 +76,10 @@ export const route: Route = { async function handler(ctx) { const { column = 'article', category = '0' } = ctx.req.param(); + if (!isValidHost(column)) { + throw new InvalidParameterError('Invalid column'); + } + const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const rootUrl = 'https://dt.yicai.com'; diff --git a/lib/routes/yna/index.ts b/lib/routes/yna/index.ts index 8997d9bb5104..9ed13ca14c50 100644 --- a/lib/routes/yna/index.ts +++ b/lib/routes/yna/index.ts @@ -7,6 +7,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import parser from '@/utils/rss-parser'; import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; export const route: Route = { path: '/:lang?/:channel?', @@ -48,17 +49,12 @@ For example, the path for the RSS feed url Date: Wed, 16 Sep 2026 04:18:16 +0800 Subject: [PATCH 0825/1190] fix(route): fix ccdi (#23306) --- lib/routes/gov/ccdi/utils.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/routes/gov/ccdi/utils.ts b/lib/routes/gov/ccdi/utils.ts index b4bc8c31f137..66e1d9d17d60 100644 --- a/lib/routes/gov/ccdi/utils.ts +++ b/lib/routes/gov/ccdi/utils.ts @@ -2,14 +2,14 @@ import { load } from 'cheerio'; import { Cookie, CookieJar } from 'tough-cookie'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; const cookieJar = new CookieJar(); const owner = '中央纪委国家监委网站'; -const rootUrl = 'https://www.ccdi.gov.cn'; +export const rootUrl = 'https://www.ccdi.gov.cn'; const regex = /(?[A-Z_]+)=(?.*?(?=; max-age)|[\dA-Fa-f]+)/g; const parseCookie = async (body) => { @@ -26,9 +26,15 @@ const parseCookie = async (body) => { await Promise.all(cookies.map((c) => cookieJar.setCookie(c, rootUrl))); }; -const parseNewsList = async (url, selector, ctx) => { - const response = await got(url, { cookieJar }); - const data = response.data; +const fetchPage = async (url: string, attempt = 0): Promise => { + const cookie = await cookieJar.getCookieString(url); + const response = await ofetch.raw(url, { headers: { cookie }, redirect: 'manual' }); + await Promise.all(response.headers.getSetCookie().map((c) => cookieJar.setCookie(c, url))); + return response.status === 302 && attempt < 2 ? fetchPage(url, attempt + 1) : response._data!; +}; + +export const parseNewsList = async (url, selector, ctx) => { + const data = await fetchPage(url); await parseCookie(data); const $ = load(data); @@ -64,11 +70,10 @@ const changeTrCookie = async () => { } }; -const parseArticle = async (item) => { +export const parseArticle = async (item) => { await changeTrCookie(); return cache.tryGet(item.link, async () => { - const response = await got(item.link, { cookieJar }); - const data = response.data; + const data = await fetchPage(item.link); await parseCookie(data); const $ = load(data); @@ -88,5 +93,3 @@ const parseArticle = async (item) => { return item; }); }; - -export { parseArticle, parseNewsList, rootUrl }; From dc718785dc9ad193e627bbd9b1b4694ad5bed357 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 16 Sep 2026 04:33:34 +0800 Subject: [PATCH 0826/1190] feat(route): add jma (#23305) * feat(route): add jma * fix(route): either daily or monthly --- lib/routes/jma/namespace.ts | 8 ++ lib/routes/jma/wxchart.ts | 145 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 lib/routes/jma/namespace.ts create mode 100644 lib/routes/jma/wxchart.ts diff --git a/lib/routes/jma/namespace.ts b/lib/routes/jma/namespace.ts new file mode 100644 index 000000000000..b8b89c9f56d9 --- /dev/null +++ b/lib/routes/jma/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '気象庁 Japan Meteorological Agency', + url: 'www.jma.go.jp', + categories: ['forecast'], + lang: 'ja', +}; diff --git a/lib/routes/jma/wxchart.ts b/lib/routes/jma/wxchart.ts new file mode 100644 index 000000000000..9914addb1a51 --- /dev/null +++ b/lib/routes/jma/wxchart.ts @@ -0,0 +1,145 @@ +import type { Context } from 'hono'; + +import type { DataItem, Route } from '@/types'; +import { ViewType } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +export const route: Route = { + path: '/wxchart/:type?', + categories: ['forecast'], + view: ViewType.Pictures, + example: '/jma/wxchart/daily', + parameters: { + type: { + description: 'Chart type', + options: [ + { value: 'daily', label: '天気図(最新)' }, + { value: 'monthly', label: '過去の実況天気図(今月)' }, + ], + default: 'daily', + }, + }, + radar: [ + { + source: ['www.jma.go.jp/bosai/weather_map/'], + target: '/wxchart/daily', + }, + { + source: ['www.data.jma.go.jp/yoho/wxchart/quickmonthly.html'], + target: '/wxchart/monthly', + }, + ], + name: '天気図', + maintainers: ['TonyRL'], + handler, + url: 'www.jma.go.jp/bosai/weather_map/', +}; + +const yohoUrl = 'https://www.data.jma.go.jp/yoho'; +const archiveUrl = `${yohoUrl}/data/wxchart/quick`; + +const areas = { + near: { region: '日本周辺域', prefix: 'SPAS', mono: false }, + near_monochrome: { region: '日本周辺域', prefix: 'SPAS', mono: true }, + asia: { region: 'アジア太平洋域', prefix: 'ASAS', mono: false }, + asia_monochrome: { region: 'アジア太平洋域', prefix: 'ASAS', mono: true }, +}; + +const charts = { + spas: '実況天気図', + asas: '実況天気図', + fsas24: '24時間予想天気図', + fsas48: '48時間予想天気図', +}; + +const toJst = (marker: string) => timezone(parseDate(marker.slice(0, 12), 'YYYYMMDDHHmm'), -9); +const toYmd = (date: Date) => date.toISOString().slice(0, 10).replaceAll('-', ''); + +async function handler(ctx: Context) { + const { type = 'daily' } = ctx.req.param(); + return type === 'monthly' ? await monthly() : await daily(); +} + +async function daily() { + const bosaiUrl = 'https://www.jma.go.jp/bosai/weather_map'; + const list = await ofetch>>(`${bosaiUrl}/data/list.json`); + + const files = Object.entries(list).flatMap(([area, chartGroups]) => + Object.values(chartGroups) + .flat() + .map((file) => { + // 20260915141230_0_Z__C_010000_20260915120000_MET_CHT_JCIspas_JCP600x581_JRcolor_Tjmahp_image.png + const [, issued, valid, chart] = file.match(/^(\d{14})_.+?_(\d{14})_MET_CHT_JCI(\w+?)_/)!; + return { ...areas[area], file, issued, valid, chart }; + }) + ); + + const items = Map.groupBy(files, ({ region, chart, valid }) => `${region}_${chart}_${valid}`) + .values() + .toArray() + .map((group): DataItem => { + const { region, prefix, chart, valid, issued } = group[0]; + const lead = { fsas24: 24, fsas48: 48 }[chart] ?? 0; + const jst = timezone(parseDate(valid, 'YYYYMMDDHHmmss'), -(9 + lead)); + const description = group + .map(({ file, prefix, mono }) => { + let html = ``; + if (chart === 'spas' || chart === 'asas') { + const archiveFile = `${archiveUrl}/${valid.slice(0, 6)}/${prefix}_${mono ? 'MONO' : 'COLOR'}_${valid.slice(0, 12)}`; + html += `
    [PNG] [SVG] [PDF]`; + } + return html; + }) + .join(''); + + return { + title: `${charts[chart]} ${region} ${jst.getUTCMonth() + 1}月${jst.getUTCDate()}日${jst.getUTCHours()}時`, + link: `${yohoUrl}/wxchart/quickdaily.html?show=${toYmd(jst)}#${prefix.toLowerCase()}`, + guid: `${bosaiUrl}/data/png/${group[0].file}`, + description, + pubDate: timezone(parseDate(issued, 'YYYYMMDDHHmmss'), 0), + }; + }); + + return { + title: '気象庁 天気図', + link: `${bosaiUrl}/`, + image: 'https://www.jma.go.jp/bosai/favicon.ico', + item: items, + }; +} + +async function monthly() { + const [first, latest] = await Promise.all([ofetch(`${archiveUrl}/first.txt`), ofetch(`${archiveUrl}/spas_latest.txt`)]); + const firstDay = toYmd(toJst(first)); + const latestJst = toJst(latest); + const latestDay = toYmd(latestJst); + const ym = latestDay.slice(0, 6); + const year = Number(ym.slice(0, 4)); + const monthIndex = Number(ym.slice(4, 6)) - 1; + + const items: DataItem[] = Array.from({ length: 31 }, (_, i) => new Date(Date.UTC(year, monthIndex, i + 1))) + .filter((date) => { + const ymd = toYmd(date); + return ymd.slice(0, 6) === ym && ymd >= firstDay && ymd <= latestDay; + }) + .map((date) => { + const ymd = toYmd(date); + const image = ymd === latestDay && latestJst.getUTCHours() < 9 ? `${archiveUrl}/${latest.slice(0, 6)}/SPAS_COLOR_${latest.slice(0, 10)}00.png` : `${archiveUrl}/${ym}/SPAS_COLOR_${ymd}0000.png`; + return { + title: `${date.getUTCMonth() + 1}月${date.getUTCDate()}日`, + link: `${yohoUrl}/wxchart/quickdaily.html?show=${ymd}`, + description: ``, + pubDate: date, + }; + }); + + return { + title: `気象庁 ${year}年${monthIndex + 1}月の天気図一覧`, + link: `${yohoUrl}/wxchart/quickmonthly.html?show=${ym}`, + image: 'https://www.jma.go.jp/bosai/favicon.ico', + item: items, + }; +} From 0f49a5ade11a042c5de5ad871735a70f8acf8426 Mon Sep 17 00:00:00 2001 From: ashi-koki Date: Wed, 16 Sep 2026 15:01:42 +0800 Subject: [PATCH 0827/1190] feat(route/okayafrica): add music latest route (#23210) * feat(route/okayafrica): add music latest route Co-Authored-By: Claude Code * fix(route/okayafrica): drop redundant first() and trim() on byline Co-Authored-By: Claude Code --------- Co-authored-by: ashi-koki <274328227+ashi-koki@users.noreply.github.com> Co-authored-by: Claude Code --- lib/routes/okayafrica/music.ts | 58 ++++++++++++++++++++++++++++++ lib/routes/okayafrica/namespace.ts | 7 ++++ 2 files changed, 65 insertions(+) create mode 100644 lib/routes/okayafrica/music.ts create mode 100644 lib/routes/okayafrica/namespace.ts diff --git a/lib/routes/okayafrica/music.ts b/lib/routes/okayafrica/music.ts new file mode 100644 index 000000000000..63fe2ba4786a --- /dev/null +++ b/lib/routes/okayafrica/music.ts @@ -0,0 +1,58 @@ +import { load } from 'cheerio'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +export const route: Route = { + path: '/music/latest', + categories: ['new-media'], + example: '/okayafrica/music/latest', + name: 'Music Latest', + maintainers: ['ashi-koki'], + handler, +}; + +async function handler(): Promise { + const rootUrl = 'https://www.okayafrica.com'; + const listUrl = `${rootUrl}/music/latest`; + + const listResponse = await ofetch(listUrl); + const $ = load(listResponse); + + const items = await Promise.all( + $('article a[itemprop="url"]') + .toArray() + .map((el) => { + const link = $(el).attr('href')!; + const title = $(el).closest('article').find('h2[itemprop="headline"]').text().trim(); + + return cache.tryGet(link, async () => { + const pageResponse = await ofetch(link); + const $page = load(pageResponse); + + $page('.bodytext .google-ad, .bodytext .articlesByTag').remove(); + + const pubDate = $page('.dateGroup.datePublished time').attr('datetime'); + + return { + title: $page('h1.headline').text().trim() || title, + link, + pubDate: pubDate ? parseDate(pubDate) : undefined, + author: $page('.lab-hidden-byline-name').text(), + description: $page('.bodytext').html() ?? undefined, + image: $page('meta[property="og:image"]').attr('content'), + }; + }); + }) + ); + + return { + title: 'OkayAfrica Music', + link: listUrl, + description: 'Latest music articles from OkayAfrica', + language: 'en', + item: items as DataItem[], + }; +} diff --git a/lib/routes/okayafrica/namespace.ts b/lib/routes/okayafrica/namespace.ts new file mode 100644 index 000000000000..f3058b750389 --- /dev/null +++ b/lib/routes/okayafrica/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'OkayAfrica', + url: 'www.okayafrica.com', + lang: 'en', +}; From 0f5bc058279924a87fdf97f292dfba72a0807a36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2026 17:05:13 +0800 Subject: [PATCH 0828/1190] chore(deps): bump nixpkgs from `8ce4ef6` to `eaad089` in the nix group (#23307) Bumps the nix group with 1 update: [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `nixpkgs` from `8ce4ef6` to `eaad089` - [Commits](https://github.com/NixOS/nixpkgs/compare/8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe...eaad089433ca2bb662274377d33df3d0e51ef28b) --- updated-dependencies: - dependency-name: nixpkgs dependency-version: eaad089433ca2bb662274377d33df3d0e51ef28b dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2bc57d186eef..77721d79e97c 100644 --- a/flake.lock +++ b/flake.lock @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789006805, - "narHash": "sha256-xB8mKMOx1IA9vTDNLmJZ6n4wCMq/cuWBBOzGCRnqxrU=", + "lastModified": 1789149629, + "narHash": "sha256-H6GwaZzZf+4npqv0tph94w9tZddSjFjmQrVsW0z78uk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe", + "rev": "eaad089433ca2bb662274377d33df3d0e51ef28b", "type": "github" }, "original": { From 4bd7c935c70d6061d87b5f7cd17332770e43b99e Mon Sep 17 00:00:00 2001 From: DIYgod Date: Wed, 16 Sep 2026 20:58:30 +0800 Subject: [PATCH 0829/1190] docs: add Hostinger deployment sponsorship --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ac3ef5a52689..1706c8cc5b92 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,10 @@ Refer to [Quick Start](https://docs.rsshub.app/joinus/) ## Deployment +Deploy RSSHub with a single click on Hostinger – pre-configured and ready to run + +[![Deploy on Hostinger](https://assets.hostinger.com/vps/deploy.svg)](https://www.hostg.xyz/aff_c?offer_id=48&aff_id=244128&url_id=6824) + Refer to [Deployment](https://docs.rsshub.app/deploy/) ## Special Thanks From 7ce3d75beaaa8da1a1781a6b6d1016480e7e13d8 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Thu, 17 Sep 2026 02:00:37 +0800 Subject: [PATCH 0830/1190] fix: update selector for location (#23308) --- lib/routes/hebeimuseum/list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/routes/hebeimuseum/list.tsx b/lib/routes/hebeimuseum/list.tsx index d9c3fc1d4139..f924f4be2b5d 100644 --- a/lib/routes/hebeimuseum/list.tsx +++ b/lib/routes/hebeimuseum/list.tsx @@ -144,7 +144,7 @@ export const route: Route = { let location = texts.find((text) => text.includes('地点:'))?.replaceAll(/(?:展(?:览|出))?地点:/g, '') || ''; - const locMatch = location.match(/^.*?展厅/) || ['']; + const locMatch = location.match(/^.*?厅/) || ['']; location = locMatch[0]; From 7c1248e19501ab73861a636a41e70789aa6d98f2 Mon Sep 17 00:00:00 2001 From: NicholasYZ <46787434+NicholasYZ@users.noreply.github.com> Date: Thu, 17 Sep 2026 04:46:42 +0800 Subject: [PATCH 0831/1190] feat(route): add southplus forum (#22186) * feat(route): add South Plus forum route - /south-plus/forum/:fid? for PHPWind-based forum - Cookie-based auth via SOUTHPLUS_COOKIE env var - Configurable UA via SOUTHPLUS_UA (cookie-version binding) - Full-text fetching with cache.tryGet - p-map concurrency control (3 at a time) - Category extraction from thread listing - Fid reference table and cookie guide in description * feat(south-plus): support PROXY_URI for cookie-IP binding, use config.trueUA as default - Replace hardcoded Firefox 151 UA with config.trueUA to comply with guideline 36 - Use built-in proxy.dispatcher (PROXY_URI) for proxy support instead of custom env var - Update documentation: F12 Network approach for cookie/UA extraction, PROXY_URI tip - Fix markdown structure: close Cookie tip block before UA tip * fix(south-plus): remove manual dispatcher to avoid assertion failure in CI RSSHub globally overrides globalThis.fetch with proxy auto-injection via request-rewriter. Passing dispatcher: null (when PROXY_URI is unset) triggers undici internal assert(dispatcher) failure in GitHub Actions. * Update lib/routes/south-plus/forum.ts Co-authored-by: Tony * fix(south-plus): address PR review comments - Use ofetch instead of native fetch - Simplify thread list selector per review suggestion - Only update author/pubDate from detail page when content is found - Move config block to correct alphabetical position * fix(south-plus): do not fall back to config.trueUA for User-Agent When SOUTHPLUS_UA is not configured, omit the User-Agent header entirely to let the request-rewriter generate a random browser UA. config.trueUA (RSSHub/1.0) is a crawler UA and may cause 403 errors with strict WAFs. Closes review comment #7 in PR #22186. * refactor(south-plus): remove redundant list selector indexing * fix(south-plus): type thread items for detail enrichment * refactor(south-plus): normalize route limit parsing --------- --- lib/config.ts | 10 ++ lib/routes/south-plus/forum.ts | 204 +++++++++++++++++++++++++++++ lib/routes/south-plus/namespace.ts | 22 ++++ 3 files changed, 236 insertions(+) create mode 100644 lib/routes/south-plus/forum.ts create mode 100644 lib/routes/south-plus/namespace.ts diff --git a/lib/config.ts b/lib/config.ts index 345a2800fdd8..0385672f5c0d 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -197,6 +197,8 @@ type ConfigEnvKeys = | 'SIS001_BASE_URL' | 'SKEB_BEARER_TOKEN' | 'SORRYCC_COOKIES' + | 'SOUTHPLUS_COOKIE' + | 'SOUTHPLUS_UA' | 'SPOTIFY_CLIENT_ID' | 'SPOTIFY_CLIENT_SECRET' | 'SPOTIFY_REFRESHTOKEN' @@ -622,6 +624,10 @@ export type Config = { sorrycc: { cookie?: string; }; + southplus: { + cookie?: string; + ua?: string; + }; spotify: { clientId?: string; clientSecret?: string; @@ -1135,6 +1141,10 @@ const calculateValue = () => { sorrycc: { cookie: envs.SORRYCC_COOKIES, }, + southplus: { + cookie: envs.SOUTHPLUS_COOKIE, + ua: envs.SOUTHPLUS_UA, + }, spotify: { clientId: envs.SPOTIFY_CLIENT_ID, clientSecret: envs.SPOTIFY_CLIENT_SECRET, diff --git a/lib/routes/south-plus/forum.ts b/lib/routes/south-plus/forum.ts new file mode 100644 index 000000000000..7dfd011a82e2 --- /dev/null +++ b/lib/routes/south-plus/forum.ts @@ -0,0 +1,204 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import { config } from '@/config'; +import ConfigNotFoundError from '@/errors/types/config-not-found'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const BASE_URL = 'https://south-plus.net'; + +async function handler(ctx): Promise { + const fid = ctx.req.param('fid') ?? '8'; + const cookie = config.southplus.cookie; + const ua = config.southplus.ua; + + const forumUrl = `${BASE_URL}/thread.php?fid-${fid}.html`; + + const headers: Record = { + Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'zh-CN,zh;q=0.9', + Referer: 'https://south-plus.net/index.php', + }; + if (ua) { + headers['User-Agent'] = ua; + } + if (cookie) { + headers.Cookie = cookie; + } + + const html = await ofetch(forumUrl, { headers }); + const $ = load(html); + + // Check if access is denied + const pageTitle = $('head > title').text(); + if (pageTitle.includes('没有权限') || pageTitle.includes('没有登录') || pageTitle.includes('认证版块')) { + if (!cookie) { + throw new ConfigNotFoundError('此版块需要登录才能访问。请配置 SOUTHPLUS_COOKIE 环境变量。'); + } + throw new ConfigNotFoundError('Cookie 已过期或无效,无法访问此版块。请更新 SOUTHPLUS_COOKIE 环境变量。'); + } + + // Parse thread list + // Thread rows have class="tr3 t_one" + // Structure: 5 td columns + // [0] status icon + // [1] category + title (h3 > a#a_ajax_XXXX) + page links + // [2] author (a.bl) + post date (div.f10.gray2) + // [3] replies/views + // [4] last post date (a.f10) + last poster (span.gray2) + const threadList: DataItem[] = $('tr.tr3.t_one a[id^="a_ajax_"]') + .toArray() + .map((item) => { + const $el = $(item); + const $row = $el.closest('tr'); + const threadLink = $el.attr('href'); + const title = $el.text().trim(); + const link = threadLink ? new URL(threadLink, BASE_URL).href : ''; + + // Author in column 2 + const author = $row.find('a.bl[href*="action-show-uid"]').text().trim(); + + // Thread post date in column 2 (div.f10.gray2) + const postDateText = $row.find('div.f10.gray2').text().trim(); + + // Last post date in column 4 (a.f10) + const lastPostDateText = $row.find('td.tal.y-style a.f10').text().trim(); + + // Use last post date as pubDate for RSS sorting + const pubDate = parseDate(lastPostDateText) || parseDate(postDateText); + + // Thread category tag (e.g. [自购], [公告]) in column 1 + const category = $row.find('a.s8').text().trim(); + + return { + title, + link, + author, + pubDate, + category: category ? [category] : undefined, + }; + }); + + // Optionally fetch full content for each thread (with cache) + const limit = ctx.req.query('limit') ? Math.trunc(Number(ctx.req.query('limit'))) : 50; + const items = await pMap( + threadList.slice(0, limit), + (item) => + cache.tryGet(item.link!, async () => { + try { + const detailHtml = await ofetch(item.link!, { headers }); + const $detail = load(detailHtml); + + // Get the main post content + // PHPWind:
    for the first post + const contentEl = $detail('#read_tpc'); + if (contentEl.length > 0) { + item.description = contentEl.html() ?? ''; + + // Get the original post date from tiptop area + const dateEl = $detail('.tiptop .fl.gray'); + if (dateEl.length > 0) { + const dateText = dateEl.first().text().trim(); + const dateMatch = dateText.match(/(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})/); + if (dateMatch) { + item.pubDate = parseDate(dateMatch[1]); + } + } + + // Get the author from the detail page + const authorEl = $detail('.r_two a[href*="action-show-uid"] strong'); + if (authorEl.length > 0) { + item.author = authorEl.first().text().trim(); + } + } + } catch { + // If detail page fails, keep the list page data + } + + return item; + }), + { concurrency: 3 } + ); + + return { + title: $('head > title').text().trim(), + link: forumUrl, + description: $('meta[name="description"]').attr('content'), + language: 'zh-CN', + item: items, + }; +} + +export const route: Route = { + path: '/forum/:fid?', + categories: ['bbs'], + example: '/south-plus/forum/8', + parameters: { + fid: '论坛版块 ID,默认为 8(ACG交流)。可在 thread.php?fid-XXX.html 中找到。常用 fid 见下方说明', + }, + description: `::: tip 常用版块 ID + +| fid | 版块名称 | 需要登录 | +| --- | -------- | :------: | +| 48 | 询问求物 | 是 | +| 8 | ACG 交流 | 否 | +| 12 | 轻小说 | 是 | +| 9 | 茶馆 | 是 | +| 201 | COSPLAY | 是 | +| 6 | 游戏资源 | 是 | +| 5 | 实用漫画 | 是 | +| 4 | 实用动画 | 是 | +| 128 | 同人音声 | 是 | +| 208 | AI 交流 | 是 | + +::: + +::: tip Cookie 示例 + +\`\`\` +eb9e6_winduser=XXXX...XXXX%3D%3D; eb9e6_cknum=YYYY...YYYY%3D; eb9e6_ck_info=%2F%09; cf_clearance=ZZZZ...ZZZZ; eb9e6_lastpos=other; eb9e6_ol_offset=123456; eb9e6_readlog=%2C...; eb9e6_threadlog=%2C...; eb9e6_lastvisit=...; peacemaker=1 +\`\`\` + +\`eb9e6_winduser\` 和 \`eb9e6_cknum\` 是必需的认证 cookie,其余可选。 +::: + +::: tip UA 说明 +South Plus 服务器会校验 Cookie 与浏览器 User-Agent 的绑定关系。Cookie 仅在登录时使用的浏览器版本下有效,不同版本或不同平台的 UA 均会被拒绝。 + +如需更换 Cookie,请同时设置 \`SOUTHPLUS_UA\` 为对应浏览器的 UA 字符串。未设置时 RSSHub 会自动生成随机的浏览器 UA。 + +如果 Cookie 是通过代理获取的,需设置 RSSHub 全局环境变量 \`PROXY_URI\`(如 \`http://host:port\`),否则服务器会拒绝认证。 +::: +:::`, + features: { + requireConfig: [ + { + name: 'SOUTHPLUS_COOKIE', + optional: true, + description: '登录 Cookie,格式为分号+空格分隔的 key=value 对。核心字段:eb9e6_winduser(认证令牌)、eb9e6_cknum(会话校验)。从浏览器登录后导出完整 cookie 字符串即可。', + }, + { + name: 'SOUTHPLUS_UA', + optional: true, + description: '浏览器 User-Agent,需与获取 Cookie 时使用的浏览器版本完全一致。可从浏览器 F12 → Network → 请求头中复制。未设置时由 RSSHub 自动生成随机浏览器 UA。', + }, + ], + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['south-plus.net/thread.php', 'snow-plus.net/thread.php'], + target: '/forum/:fid', + }, + ], + name: '论坛帖子', + maintainers: ['NicholasYZ'], + handler, +}; diff --git a/lib/routes/south-plus/namespace.ts b/lib/routes/south-plus/namespace.ts new file mode 100644 index 000000000000..216151cf9162 --- /dev/null +++ b/lib/routes/south-plus/namespace.ts @@ -0,0 +1,22 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'South Plus', + url: 'south-plus.net', + description: `::: tip +South Plus (南 +) 是一个基于 PHPWind 架构的 ACG 综合交流论坛。 + +部分板块需要登录才能访问,请配置 \`SOUTHPLUS_COOKIE\` 环境变量。 + +**获取 Cookie 和 User-Agent 步骤:** + +1. 在浏览器中登录 [south-plus.net](https://south-plus.net) 或 [snow-plus.net](https://snow-plus.net) +2. 确认右上角显示用户名和「退出」链接(而非「登录」) +3. 按 F12 → **Network**(网络)→ 刷新页面 → 点击任意请求 → **Request Headers**(请求头) +4. 复制 \`Cookie\` 字段的完整值(单行,分号 + 空格分隔),设置为 \`SOUTHPLUS_COOKIE\` +5. 复制 \`User-Agent\` 字段的值,设置为 \`SOUTHPLUS_UA\`(Cookie 与 UA 版本绑定,必须匹配) +6. 如果 Cookie 是通过代理获取的,需设置 RSSHub 全局环境变量 \`PROXY_URI\`(如 \`http://host:port\`) + +:::`, + lang: 'zh-CN', +}; From a456b880da1e6a62262eda45a84e30bdf89a790b Mon Sep 17 00:00:00 2001 From: ashi-koki Date: Thu, 17 Sep 2026 13:11:41 +0800 Subject: [PATCH 0832/1190] feat(route/mikiki): add mikiki latest articles route (#23208) * feat(route/mikiki): add mikiki latest articles route Co-Authored-By: Claude Code * fix(route/mikiki): normalize pubDate with parseDate Co-Authored-By: Claude Code * fix(route/mikiki): drop redundant responseType option Co-Authored-By: Claude Code --------- Co-authored-by: ashi-koki <274328227+ashi-koki@users.noreply.github.com> Co-authored-by: Claude Code --- lib/routes/mikiki/index.ts | 50 ++++++++++++++++++++++++++++++++++ lib/routes/mikiki/namespace.ts | 7 +++++ 2 files changed, 57 insertions(+) create mode 100644 lib/routes/mikiki/index.ts create mode 100644 lib/routes/mikiki/namespace.ts diff --git a/lib/routes/mikiki/index.ts b/lib/routes/mikiki/index.ts new file mode 100644 index 000000000000..e966e2505e0b --- /dev/null +++ b/lib/routes/mikiki/index.ts @@ -0,0 +1,50 @@ +import { load } from 'cheerio'; + +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import parser from '@/utils/rss-parser'; + +export const route: Route = { + path: '/', + categories: ['new-media'], + example: '/mikiki', + name: '最新記事', + maintainers: ['ashi-koki'], + handler, +}; + +async function handler(): Promise { + const rootUrl = 'https://mikiki.tokyo.jp'; + + const feedResponse = await ofetch(`${rootUrl}/list/feed/rss`); + const feed = await parser.parseString(feedResponse); + + const items = await Promise.all( + feed.items.map((item) => + cache.tryGet(item.link!, async () => { + const response = await ofetch(item.link!); + const $ = load(response); + + $('.article-index, .ranking-in-article').remove(); + + return { + title: item.title, + link: item.link, + pubDate: item.isoDate ? parseDate(item.isoDate) : undefined, + author: $('.article-header-author-name').first().text(), + description: $('.article-body').html(), + }; + }) + ) + ); + + return { + title: feed.title!, + link: rootUrl, + description: feed.description, + language: 'ja', + item: items as DataItem[], + }; +} diff --git a/lib/routes/mikiki/namespace.ts b/lib/routes/mikiki/namespace.ts new file mode 100644 index 000000000000..0ee6b91d350e --- /dev/null +++ b/lib/routes/mikiki/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Mikiki', + url: 'mikiki.tokyo.jp', + lang: 'ja', +}; From 1fea257b10c1c538392eebaa8606cc0e7da08fb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:11:02 +0000 Subject: [PATCH 0833/1190] chore(deps): bump zod from 4.6.2 to 4.6.5 (#23311) Bumps [zod](https://github.com/colinhacks/zod) from 4.6.2 to 4.6.5. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v4.6.2...v4.6.5) --- updated-dependencies: - dependency-name: zod dependency-version: 4.6.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 257 +++++++++++++++++++++++++------------------------ 2 files changed, 133 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 64154c2331b1..6f16f8599c45 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "xxhash-wasm": "1.1.0", "youtube-caption-extractor": "1.10.2", "youtubei.js": "18.0.0", - "zod": "4.6.2" + "zod": "4.6.5" }, "devDependencies": { "@actions/core": "3.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5225b9a3005f..81119c82e8e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: version: 2.1.1(hono@4.13.7) '@hono/zod-openapi': specifier: 1.6.3 - version: 1.6.3(hono@4.13.7)(zod@4.6.2) + version: 1.6.3(hono@4.13.7)(zod@4.6.5) '@jocmp/mercury-parser': specifier: 3.1.0 version: 3.1.0 @@ -287,8 +287,8 @@ importers: specifier: 18.0.0 version: 18.0.0 zod: - specifier: 4.6.2 - version: 4.6.2 + specifier: 4.6.5 + version: 4.6.5 devDependencies: '@actions/core': specifier: 3.0.1 @@ -364,7 +364,7 @@ importers: version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 - version: 1.11.0(rollup@4.63.1) + version: 1.11.0(rollup@4.63.2) '@vitest/coverage-v8': specifier: 4.1.11 version: 4.1.11(vitest@4.1.11) @@ -2489,141 +2489,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.63.1': - resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==} + '@rollup/rollup-android-arm-eabi@4.63.2': + resolution: {integrity: sha512-Xa6RDoWa+hNiX6PgsljlH6W75RaONx3y6PVlbLhkEWW+GaPQ3dP5gwbL/erAzQHWwkvW5UxdD5l87Qx2FAQ/4A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.63.1': - resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==} + '@rollup/rollup-android-arm64@4.63.2': + resolution: {integrity: sha512-vNASxsghMfQ5s+v3PrpnJd+ryL/26lxCCaGI+sDJ7VzmHiYXIrrVltsDhaawxLM1WcoMU2oYlbPHLaYQtBzhcg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.63.1': - resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==} + '@rollup/rollup-darwin-arm64@4.63.2': + resolution: {integrity: sha512-0dWDjmlrpZAgjPD/aPzUDhBW8APLRjAni5bOrM76wiiZm+E+KTMVKNhAzaTBohz8UyO2fKNAl0+fygbe2HZXOA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.63.1': - resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==} + '@rollup/rollup-darwin-x64@4.63.2': + resolution: {integrity: sha512-N58uktcwzk3+qT4KHEuNdIxX1N01RWrkfVoml69EAbSaNDL+sbNVLx2RMl4Qd23lpA0fgPvyh5hHb4weD5WKmg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.63.1': - resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==} + '@rollup/rollup-freebsd-arm64@4.63.2': + resolution: {integrity: sha512-HWF2zH8EAp2scWRpt2PGe6iUGz7zi04waXsdRr3zb4DWCk2ImIo5FZu0jjmD53nP/DGSvnW0e7/1ToCNZs2lZw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.63.1': - resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==} + '@rollup/rollup-freebsd-x64@4.63.2': + resolution: {integrity: sha512-MkvcwHMnzPSMOQEwB6wHnLzmc+hT8BGc5bW/Mhmjjgx3wbj6VBnlc47XsK74kD0K9MikFfXpQqyz4NUXaUW62A==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.63.1': - resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==} + '@rollup/rollup-linux-arm-gnueabihf@4.63.2': + resolution: {integrity: sha512-xe1bCKPJaKsD0tfd7Rb6bGfUogJTpKbTEEthsfdb7hTfTRNJVQTdirabQx0o6ERVba/smkM720soMY+0QnrlSQ==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.63.1': - resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==} + '@rollup/rollup-linux-arm-musleabihf@4.63.2': + resolution: {integrity: sha512-yOM7LdK0p6gk6+Q773OEwtlsikT1TL3yMmYsTtRlDRPha5vV2DC5x7LqRWDr6f3cSYNMKVqxzffXv8ivxNBIFQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.63.1': - resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==} + '@rollup/rollup-linux-arm64-gnu@4.63.2': + resolution: {integrity: sha512-qiWuJJV3DybA2IfzvRimeKXGrGuVPv1zobSY/26KnP3HbV0VcNb3ECzgvtbvF3xjSMkcooou6HASXZuLdjnhpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.63.1': - resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==} + '@rollup/rollup-linux-arm64-musl@4.63.2': + resolution: {integrity: sha512-akcZquRzCY/KpUoZAMBhGf7oi4LmXq1BzRA5CPAC3rkUf28Y/sAYV3jSL+JKd7cwEyFvR5G0XVZ0gaMedP+60A==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.63.1': - resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==} + '@rollup/rollup-linux-loong64-gnu@4.63.2': + resolution: {integrity: sha512-fNwYHrPyYyxauPzX/cpYw8Z7LQpp+DGA0KCoswA0aVFBpmdMil9XgjB8V3Ny64Ihu797+GKcuJqnsOKEmor7fA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.63.1': - resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==} + '@rollup/rollup-linux-loong64-musl@4.63.2': + resolution: {integrity: sha512-XfvsgzR7DZqREdst7K1Mj3ilSUM5xLAHJcIMDFPKdxTs9q5VHOT8aMA+a683fqBu7DQl8+Sd9HCsQYL8EMY9qA==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.63.1': - resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==} + '@rollup/rollup-linux-ppc64-gnu@4.63.2': + resolution: {integrity: sha512-Pp7gVZggEFlbcuztay+/U0gVG9S1XAh8i7I1Re/htbAzo43P5wHZHw6pTyzotISqlKohoh9RpIfnOz3RbemK1w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.63.1': - resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==} + '@rollup/rollup-linux-ppc64-musl@4.63.2': + resolution: {integrity: sha512-zkgL2xff6i7u5hau/m6FGeS8gRkLEdgLw522WGmdWWlLd9btmNl3S80mcEjtGq+kvgUekQ3+BOYLLLcPlS2LIA==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.63.1': - resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==} + '@rollup/rollup-linux-riscv64-gnu@4.63.2': + resolution: {integrity: sha512-qOheJomrkVCbbHFJ7L3J97cnhfogKqguAQphv26+3ZsAQIF1L19b+dArl//s8rjJHJLz9byykyM8NBP4nmSa1g==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.63.1': - resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==} + '@rollup/rollup-linux-riscv64-musl@4.63.2': + resolution: {integrity: sha512-XlxLD54wQhH3FciCgMofxBw27NzUe818gJH410qWvc41UT0ZFcgxVjyX5/EK8MPTupjeVWqN5oy+9pCA9mqfCA==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.63.1': - resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==} + '@rollup/rollup-linux-s390x-gnu@4.63.2': + resolution: {integrity: sha512-vdryWeRb2bLJZf0Fv/W8se6nvsHe2PkTCxV0meheK3nQE+G90VCJcke51Miy1yQRsfm2uqIyjXOu4wmUzbTtkQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.63.1': - resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==} + '@rollup/rollup-linux-x64-gnu@4.63.2': + resolution: {integrity: sha512-bcq2h2pkKmH2po4cZV8VWzO4lL40STyu/nLoFpYMQp9C2tCVNTdcVv86MwSsn3D5s1FBe2Ty1atqvVAUTMimNg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.63.1': - resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==} + '@rollup/rollup-linux-x64-musl@4.63.2': + resolution: {integrity: sha512-EGoo5DMVMRkTId8fuTDaoxVlR5ZTsKULUezRjd9gCw5eeY+DjCvDpZAOlNUvKPGX+7rS1RWx6j+yOpNPx0cUgQ==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.63.1': - resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==} + '@rollup/rollup-openbsd-x64@4.63.2': + resolution: {integrity: sha512-MErl12k7BFHZG1TI9QF/3lSSZARzq9KgNy/FjnqFMCkv+N4RSSzoUCA5h2mqHX4Mox3WaTVKblyzhQ1zRb2ZuQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.63.1': - resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==} + '@rollup/rollup-openharmony-arm64@4.63.2': + resolution: {integrity: sha512-ILs8k07Wh4p0PsNY4wYLEaXZKMOpVhrG5QDB0yHhGhuzOfDlnyHN6sflL4El/MpUP1y8uY2lUZrv4oBS6pTT3g==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.63.1': - resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==} + '@rollup/rollup-win32-arm64-msvc@4.63.2': + resolution: {integrity: sha512-hKgB3nz/TKD3Wv78XEsyXzQsNjvhOHmwKQTvXADGOyU/cIClZDO7DsoggbdmJDPGp5V80tA3Vfv61PaKTLH3LA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.63.1': - resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==} + '@rollup/rollup-win32-ia32-msvc@4.63.2': + resolution: {integrity: sha512-T4wf1mudIDxN8Q/CWIBJC1u5gQUc+r5mPvlwoSbIvNkyVTP2TAFeobEmst5AQ4gMyAz4sSByVdoTDfvTmGK/8g==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.63.1': - resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==} + '@rollup/rollup-win32-x64-gnu@4.63.2': + resolution: {integrity: sha512-tC3IY7qoaD9Ll3/8WJQn49j5V2f/NuI9S41NOE2iM5MPs3sPIvOkVToLcz/7Bz4pyF7PSvrtwu8I/pUrGOSecQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.63.1': - resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==} + '@rollup/rollup-win32-x64-msvc@4.63.2': + resolution: {integrity: sha512-6NHnk/K3eq2ZFYcU1X8g67s9qIJRCOTT92gwLMVBp08dB2uuuwI1/Q/empzL2Bfr2f2WRLJVwpp90RmacQyFkw==} cpu: [x64] os: [win32] @@ -4987,6 +4987,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@5.1.16: resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} engines: {node: ^18 || >=20} @@ -5518,8 +5523,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.63.1: - resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==} + rollup@4.63.2: + resolution: {integrity: sha512-l5eyksV4tPBj6lJyEa37YzIOCSOV7lkZzEHUdpjWZbtD7wTcFYmEYXSgm5bT4vV+dZLb9rBG1W9GROOG4NS4Ew==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6392,8 +6397,8 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zod@4.6.2: - resolution: {integrity: sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==} + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -6446,10 +6451,10 @@ snapshots: is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 - '@asteasolutions/zod-to-openapi@9.1.0(zod@4.6.2)': + '@asteasolutions/zod-to-openapi@9.1.0(zod@4.6.5)': dependencies: openapi3-ts: 4.6.1 - zod: 4.6.2 + zod: 4.6.5 '@babel/code-frame@7.29.7': dependencies: @@ -6947,18 +6952,18 @@ snapshots: dependencies: hono: 4.13.7 - '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.6.2)': + '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.6.5)': dependencies: - '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.6.2) - '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.6.2) + '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.6.5) + '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.6.5) hono: 4.13.7 openapi3-ts: 4.6.1 - zod: 4.6.2 + zod: 4.6.5 - '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.6.2)': + '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.6.5)': dependencies: hono: 4.13.7 - zod: 4.6.2 + zod: 4.6.5 '@humanfs/core@0.19.2': dependencies: @@ -7814,87 +7819,87 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.63.1)': + '@rollup/pluginutils@5.4.0(rollup@4.63.2)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: - rollup: 4.63.1 + rollup: 4.63.2 - '@rollup/rollup-android-arm-eabi@4.63.1': + '@rollup/rollup-android-arm-eabi@4.63.2': optional: true - '@rollup/rollup-android-arm64@4.63.1': + '@rollup/rollup-android-arm64@4.63.2': optional: true - '@rollup/rollup-darwin-arm64@4.63.1': + '@rollup/rollup-darwin-arm64@4.63.2': optional: true - '@rollup/rollup-darwin-x64@4.63.1': + '@rollup/rollup-darwin-x64@4.63.2': optional: true - '@rollup/rollup-freebsd-arm64@4.63.1': + '@rollup/rollup-freebsd-arm64@4.63.2': optional: true - '@rollup/rollup-freebsd-x64@4.63.1': + '@rollup/rollup-freebsd-x64@4.63.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + '@rollup/rollup-linux-arm-gnueabihf@4.63.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.63.1': + '@rollup/rollup-linux-arm-musleabihf@4.63.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.63.1': + '@rollup/rollup-linux-arm64-gnu@4.63.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.63.1': + '@rollup/rollup-linux-arm64-musl@4.63.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.63.1': + '@rollup/rollup-linux-loong64-gnu@4.63.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.63.1': + '@rollup/rollup-linux-loong64-musl@4.63.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.63.1': + '@rollup/rollup-linux-ppc64-gnu@4.63.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.63.1': + '@rollup/rollup-linux-ppc64-musl@4.63.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.63.1': + '@rollup/rollup-linux-riscv64-gnu@4.63.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.63.1': + '@rollup/rollup-linux-riscv64-musl@4.63.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.63.1': + '@rollup/rollup-linux-s390x-gnu@4.63.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.63.1': + '@rollup/rollup-linux-x64-gnu@4.63.2': optional: true - '@rollup/rollup-linux-x64-musl@4.63.1': + '@rollup/rollup-linux-x64-musl@4.63.2': optional: true - '@rollup/rollup-openbsd-x64@4.63.1': + '@rollup/rollup-openbsd-x64@4.63.2': optional: true - '@rollup/rollup-openharmony-arm64@4.63.1': + '@rollup/rollup-openharmony-arm64@4.63.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.63.1': + '@rollup/rollup-win32-arm64-msvc@4.63.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.63.1': + '@rollup/rollup-win32-ia32-msvc@4.63.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.63.1': + '@rollup/rollup-win32-x64-gnu@4.63.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.63.1': + '@rollup/rollup-win32-x64-msvc@4.63.2': optional: true '@rss3/api-core@0.0.25': @@ -7935,7 +7940,7 @@ snapshots: '@scalar/helpers': 0.11.3 nanoid: 5.1.16 type-fest: 5.8.0 - zod: 4.6.2 + zod: 4.6.5 '@scalar/validation@0.6.3': {} @@ -8322,10 +8327,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.11.0(rollup@4.63.1)': + '@vercel/nft@1.11.0(rollup@4.63.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0(rollup@4.63.1) + '@rollup/pluginutils': 5.4.0(rollup@4.63.2) acorn: 8.18.0 acorn-import-attributes: 1.9.5(acorn@8.18.0) async-sema: 3.1.1 @@ -10501,6 +10506,8 @@ snapshots: nanoid@3.3.18: {} + nanoid@3.3.19: {} + nanoid@5.1.16: {} narou@2.0.1: {} @@ -10868,7 +10875,7 @@ snapshots: postcss@8.5.28: dependencies: - nanoid: 3.3.18 + nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -11138,36 +11145,36 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.8 '@rolldown/binding-win32-x64-msvc': 1.2.8 - rollup@4.63.1: + rollup@4.63.2: dependencies: '@types/estree': 1.0.9 optionalDependencies: '@napi-rs/lzma-linux-x64-gnu': 1.5.1 - '@rollup/rollup-android-arm-eabi': 4.63.1 - '@rollup/rollup-android-arm64': 4.63.1 - '@rollup/rollup-darwin-arm64': 4.63.1 - '@rollup/rollup-darwin-x64': 4.63.1 - '@rollup/rollup-freebsd-arm64': 4.63.1 - '@rollup/rollup-freebsd-x64': 4.63.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.63.1 - '@rollup/rollup-linux-arm-musleabihf': 4.63.1 - '@rollup/rollup-linux-arm64-gnu': 4.63.1 - '@rollup/rollup-linux-arm64-musl': 4.63.1 - '@rollup/rollup-linux-loong64-gnu': 4.63.1 - '@rollup/rollup-linux-loong64-musl': 4.63.1 - '@rollup/rollup-linux-ppc64-gnu': 4.63.1 - '@rollup/rollup-linux-ppc64-musl': 4.63.1 - '@rollup/rollup-linux-riscv64-gnu': 4.63.1 - '@rollup/rollup-linux-riscv64-musl': 4.63.1 - '@rollup/rollup-linux-s390x-gnu': 4.63.1 - '@rollup/rollup-linux-x64-gnu': 4.63.1 - '@rollup/rollup-linux-x64-musl': 4.63.1 - '@rollup/rollup-openbsd-x64': 4.63.1 - '@rollup/rollup-openharmony-arm64': 4.63.1 - '@rollup/rollup-win32-arm64-msvc': 4.63.1 - '@rollup/rollup-win32-ia32-msvc': 4.63.1 - '@rollup/rollup-win32-x64-gnu': 4.63.1 - '@rollup/rollup-win32-x64-msvc': 4.63.1 + '@rollup/rollup-android-arm-eabi': 4.63.2 + '@rollup/rollup-android-arm64': 4.63.2 + '@rollup/rollup-darwin-arm64': 4.63.2 + '@rollup/rollup-darwin-x64': 4.63.2 + '@rollup/rollup-freebsd-arm64': 4.63.2 + '@rollup/rollup-freebsd-x64': 4.63.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.2 + '@rollup/rollup-linux-arm-musleabihf': 4.63.2 + '@rollup/rollup-linux-arm64-gnu': 4.63.2 + '@rollup/rollup-linux-arm64-musl': 4.63.2 + '@rollup/rollup-linux-loong64-gnu': 4.63.2 + '@rollup/rollup-linux-loong64-musl': 4.63.2 + '@rollup/rollup-linux-ppc64-gnu': 4.63.2 + '@rollup/rollup-linux-ppc64-musl': 4.63.2 + '@rollup/rollup-linux-riscv64-gnu': 4.63.2 + '@rollup/rollup-linux-riscv64-musl': 4.63.2 + '@rollup/rollup-linux-s390x-gnu': 4.63.2 + '@rollup/rollup-linux-x64-gnu': 4.63.2 + '@rollup/rollup-linux-x64-musl': 4.63.2 + '@rollup/rollup-openbsd-x64': 4.63.2 + '@rollup/rollup-openharmony-arm64': 4.63.2 + '@rollup/rollup-win32-arm64-msvc': 4.63.2 + '@rollup/rollup-win32-ia32-msvc': 4.63.2 + '@rollup/rollup-win32-x64-gnu': 4.63.2 + '@rollup/rollup-win32-x64-msvc': 4.63.2 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -11744,7 +11751,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 postcss: 8.5.28 - rollup: 4.63.1 + rollup: 4.63.2 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.5.1 @@ -12035,6 +12042,6 @@ snapshots: zod@4.4.3: {} - zod@4.6.2: {} + zod@4.6.5: {} zwitch@2.0.4: {} From 9c02fef779a79bd76ab5d6e3fb0a4cfee2213954 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:13:21 +0000 Subject: [PATCH 0834/1190] chore(deps): bump p-map from 7.0.7 to 7.0.8 (#23312) Bumps [p-map](https://github.com/sindresorhus/p-map) from 7.0.7 to 7.0.8. - [Release notes](https://github.com/sindresorhus/p-map/releases) - [Commits](https://github.com/sindresorhus/p-map/compare/v7.0.7...v7.0.8) --- updated-dependencies: - dependency-name: p-map dependency-version: 7.0.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6f16f8599c45..a086f6f6b9d3 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "ofetch": "1.5.1", "otplib": "13.5.0", "oxc-parser": "0.149.0", - "p-map": "7.0.7", + "p-map": "7.0.8", "pac-proxy-agent": "9.1.0", "patchright": "1.63.0", "postal-mime": "3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81119c82e8e4..1c576700dd1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,8 +200,8 @@ importers: specifier: 0.149.0 version: 0.149.0 p-map: - specifier: 7.0.7 - version: 7.0.7 + specifier: 7.0.8 + version: 7.0.8 pac-proxy-agent: specifier: 9.1.0 version: 9.1.0 @@ -5186,8 +5186,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@7.0.7: - resolution: {integrity: sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ==} + p-map@7.0.8: + resolution: {integrity: sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw==} engines: {node: '>=18'} p-timeout@6.1.4: @@ -10762,7 +10762,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.7: {} + p-map@7.0.8: {} p-timeout@6.1.4: {} From 8700471c8ad3614da60bc328eb1053c05f3f1ae7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:14:07 +0000 Subject: [PATCH 0835/1190] chore(deps): bump tldts from 7.4.12 to 7.4.13 (#23313) Bumps [tldts](https://github.com/remusao/tldts) from 7.4.12 to 7.4.13. - [Release notes](https://github.com/remusao/tldts/releases) - [Changelog](https://github.com/remusao/tldts/blob/master/CHANGELOG.md) - [Commits](https://github.com/remusao/tldts/compare/v7.4.12...v7.4.13) --- updated-dependencies: - dependency-name: tldts dependency-version: 7.4.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index a086f6f6b9d3..993bdc817b38 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "source-map": "0.8.0", "teleproto": "1.228.5", "title": "4.0.1", - "tldts": "7.4.12", + "tldts": "7.4.13", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", "tsx": "4.23.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c576700dd1c..382c7f159c34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,8 +248,8 @@ importers: specifier: 4.0.1 version: 4.0.1 tldts: - specifier: 7.4.12 - version: 7.4.12 + specifier: 7.4.13 + version: 7.4.13 tosource: specifier: 2.0.0-alpha.3 version: 2.0.0-alpha.3 @@ -5795,8 +5795,8 @@ packages: tldts-core@7.4.13: resolution: {integrity: sha512-mbYsrih5FRtGxs3Usvl/PqwJsNpp+jsmrdFviiK02teHDG0/HebBG/pqCylje3kzgXYzuLoHJF/0mz9W53t8Xg==} - tldts@7.4.12: - resolution: {integrity: sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==} + tldts@7.4.13: + resolution: {integrity: sha512-iHtaIWWIbMDkCeJdTBzZFGgbluE5J+oHlb2g7+oAz1S1gpuVpabRZdQyd471Vl8UUkcz2vXSL8xZH2kyCe8tfA==} hasBin: true to-no-case@1.0.2: @@ -11456,7 +11456,7 @@ snapshots: tldts-core@7.4.13: {} - tldts@7.4.12: + tldts@7.4.13: dependencies: tldts-core: 7.4.13 @@ -11479,7 +11479,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.12 + tldts: 7.4.13 tr46@0.0.3: {} From 255db921d023a3a434e3b9d6bdd5249f50536a28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:39:31 +0800 Subject: [PATCH 0836/1190] chore(deps): bump codecov/codecov-action from 7.0.0 to 7.1.0 (#23310) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 7.0.0 to 7.1.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/fb8b3582c8e4def4969c97caa2f19720cb33a72f...0b35c9ecc4f0529d0eb674914510c22f85b196b4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f91322fc7c4e..a797ffb7f6e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: run: pnpm run vitest:workerd - name: Upload coverage to Codecov if: ${{ matrix.node-version == 'lts/*' }} - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos as documented, but seems broken From 5b132fffc5dff0f5460c965bb52cc1d42b71bc26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:42:45 +0800 Subject: [PATCH 0837/1190] chore(deps): bump devenv from `6e32383` to `6e83050` in the nix group (#23314) Bumps the nix group with 1 update: [devenv](https://github.com/cachix/devenv). Updates `devenv` from `6e32383` to `6e83050` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/6e323832a55097670dbd8eed04b1e63efc841dfa...6e830506b517d6a373f2dec6ee8c7f4683908856) --- updated-dependencies: - dependency-name: devenv dependency-version: 6e830506b517d6a373f2dec6ee8c7f4683908856 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 77721d79e97c..5e5eb4bd7ed2 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789152246, - "narHash": "sha256-8ESHEGygXS5lmDepm5cMbmVd8DjcwgwzsmjHc9ffY1c=", + "lastModified": 1789340509, + "narHash": "sha256-It2AD16uFwiAe9GvuLbk+j+qTDrQxT3XPclL71ZnJ3I=", "owner": "cachix", "repo": "devenv", - "rev": "6e323832a55097670dbd8eed04b1e63efc841dfa", + "rev": "6e830506b517d6a373f2dec6ee8c7f4683908856", "type": "github" }, "original": { From 9928f8854a5e87bc681c8b110d51ee8812124787 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 17 Sep 2026 10:20:07 -0700 Subject: [PATCH 0838/1190] fix(route/theverge): adapt to schema changes and harden article rendering (#23082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(route/theverge): adapt to renamed paragraph fields The Verge renamed `tempContents` to `paragraphContents` on CoreParagraphBlockType, and stream excerpt entries changed from `{ contents: { html } }` to `{ paragraphContents: [{ html }] }`, so the route threw "Cannot read properties of undefined (reading 'map')". Co-Authored-By: Claude Fable 5 * fix(route/theverge): harden article rendering and refresh hub list - fetch the feed via ofetch + parseString (rss-parser's parseURL uses https.get, unavailable on Cloudflare Workers) - unknown or malformed blocks are skipped with a warning instead of throwing and failing the whole feed - fall back to the server-rendered article body when __NEXT_DATA__ cannot be rendered, and to the feed summary when the page cannot be fetched (the latter is not cached so it is retried) - render VideoBlockType, exclude FeaturedProductsBlockType - use category titles instead of raw objects for item.category - replace the stale hub table (gaming/hd/photography are gone) with the hubs that currently publish Co-Authored-By: Claude Fable 5 * fix(route/theverge): keep only full-text results in the cache Addresses the auto review: - The summary-only fallback used to run inside `cache.tryGet`, so it was written to the cache and served for the whole TTL. The article body fallback now throws when the server-rendered body is missing too, so the summary result is produced in the uncached outer catch, as documented. - `renderBlock`'s default branch throws again instead of returning an empty string, and `renderBlocks` no longer swallows per-block errors: an unknown block type now makes that article fall back to the server-rendered body, which is still full text, rather than silently emitting a description with a hole in it. - Exclude `ActionBoxBlockType` (a podcast subscribe call to action), the only block type not already handled across 48 articles in six hubs. Co-Authored-By: Claude Fable 5 * fix(route/theverge): drop the fallbacks and the unrelated feed fetch change Addressing review: - Sort the hub table alphabetically by slug, as the original was. - Restore parser.parseURL; swapping it for ofetch + parseString was unrelated to this fix. - Remove the server-rendered-body and feed-summary fallbacks. Across the 10 hub feeds, all 79 distinct articles render from __NEXT_DATA__, so neither fallback has a case to justify it, and a schema change should surface rather than quietly degrade to a summary. What remains is the actual breakage: the renamed paragraph fields, VideoBlockType, the FeaturedProductsBlockType exclusion, category titles instead of raw objects, and a hub list that matches what The Verge publishes today. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL * fix(route/theverge): restore ActionBoxBlockType, drop the tempContents branch ActionBoxBlockType was dropped by mistake when the previous commit rebuilt this file from master; without it four of the hub feeds fail outright now that there is no per-article fallback. The `tempContents` branch had no article behind it and is gone. Also sorts the VideoBlockType case into place, keeps `blackberry` (its feed still serves items) and removes the claim that any other hub slug works — an unknown slug silently returns the site-wide feed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL --------- Co-authored-by: Claude Fable 5 --- lib/routes/theverge/index.ts | 76 ++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/routes/theverge/index.ts b/lib/routes/theverge/index.ts index 325dcce34e44..7ccb763a46bd 100644 --- a/lib/routes/theverge/index.ts +++ b/lib/routes/theverge/index.ts @@ -7,10 +7,19 @@ import parser from '@/utils/rss-parser'; import { renderHeader } from './templates/header'; -const excludeTypes = new Set(['NewsletterBlockType', 'RelatedPostsBlockType', 'ProductsTableBlockType', 'TableOfContentsBlockType']); +const excludeTypes = new Set(['ActionBoxBlockType', 'FeaturedProductsBlockType', 'NewsletterBlockType', 'ProductsTableBlockType', 'RelatedPostsBlockType', 'TableOfContentsBlockType']); const shouldKeep = (b: any) => !excludeTypes.has(b.__typename); +// Paragraphs and stream excerpts carry `paragraphContents`; headings, list items and pullquotes carry a single `contents` +const renderContents = (b: any): string => (b.paragraphContents ?? [b.contents]).map((c) => c?.html ?? '').join(''); + +const renderBlocks = (blocks: any[] | undefined, separator: string): string => + (blocks ?? []) + .map((b) => renderBlock(b)) + .filter(Boolean) + .join(separator); + export const route: Route = { path: '/:hub?', categories: ['new-media'], @@ -32,20 +41,35 @@ export const route: Route = { name: 'Category', maintainers: ['HenryQW', 'vbali'], handler, - description: `| Hub | Hub name | -| ----------- | ------------------- | -| | All Posts | -| android | Android | -| apple | Apple | -| apps | Apps & Software | -| blackberry | BlackBerry | -| culture | Culture | -| gaming | Gaming | -| hd | HD & Home | -| microsoft | Microsoft | -| photography | Photography & Video | -| policy | Policy & Law | -| web | Web & Social | + description: `| Hub | Hub name | +| -------------- | -------------- | +| | All Posts | +| amazon | Amazon | +| android | Android | +| apple | Apple | +| apps | Apps | +| blackberry | BlackBerry | +| business | Business | +| creators | Creators | +| culture | Culture | +| entertainment | Entertainment | +| film | Film | +| games | Gaming | +| google | Google | +| health | Health | +| meta | Meta | +| microsoft | Microsoft | +| music | Music | +| policy | Policy | +| reviews | Reviews | +| samsung | Samsung | +| science | Science | +| space | Space | +| streaming | Streaming | +| tech | Tech | +| transportation | Transportation | +| tv | TV Shows | +| web | Web | Provides a better reading experience (full text articles) over the official one.`, }; @@ -60,23 +84,23 @@ const renderBlock = (b) => { case 'CoreGalleryBlockType': return b.images.map((i) => `
    ${i.alt}
    ${i.caption.html}
    `).join(''); case 'CoreHeadingBlockType': - return `${b.contents.html}`; + return `${renderContents(b)}`; case 'CoreHTMLBlockType': return b.markup; case 'CoreImageBlockType': return `
    ${b.alt}
    ${b.caption.html}
    `; case 'CoreListBlockType': - return `${b.ordered ? '
      ' : '
        '}${b.items.map((i) => `
      • ${i.contents.html}
      • `).join('')}${b.ordered ? '
    ' : ''}`; + return `${b.ordered ? '
      ' : '
        '}${b.items.map((i) => `
      • ${renderContents(i)}
      • `).join('')}${b.ordered ? '
    ' : ''}`; case 'CoreParagraphBlockType': - return b.tempContents.map((c) => c.html).join(''); + return renderContents(b); case 'CorePullquoteBlockType': - return `
    ${b.contents.html}
    `; + return `
    ${renderContents(b)}
    `; case 'CoreQuoteBlockType': - return `
    ${b.children.map((child) => renderBlock(child)).join('')}
    `; + return `
    ${renderBlocks(b.children, '')}
    `; case 'CoreSeparatorBlockType': return '
    '; case 'HighlightBlockType': - return b.children.map((c) => renderBlock(c)).join(''); + return renderBlocks(b.children, ''); case 'ImageCompareBlockType': return `
    ${b.leftImage.alt}${b.rightImage.alt}
    ${b.caption.html}
    `; case 'ImageSliderBlockType': @@ -89,6 +113,8 @@ const renderBlock = (b) => { } case 'TableBlockType': return `${b.header.map((cell) => ``).join('')}${b.rows.map((row) => `${row.map((cell) => ``).join('')}`).join('')}
    ${cell}
    ${cell}
    `; + case 'VideoBlockType': + return `
    ${b.caption?.html ? `
    ${b.caption.html}
    ` : ''}
    `; default: throw new Error(`Unsupported block type: ${b.__typename}`); } @@ -114,7 +140,7 @@ async function handler(ctx) { ledeMediaData: node.ledeMediaData, }); - description += node.blocks.map((b) => renderBlock(b)).join('

    '); + description += renderBlocks(node.blocks, '

    '); if (node.__typename === 'StreamResourceType') { description += node.posts.edges @@ -126,10 +152,10 @@ async function handler(ctx) { }); switch (n.__typename) { case 'PostResourceType': - d += n.excerpt.map((e) => e.contents.html).join('
    '); + d += n.excerpt.map((e) => renderContents(e)).join('
    '); break; case 'QuickPostResourceType': - d += n.blocks.map((b) => renderBlock(b)).join('
    '); + d += renderBlocks(n.blocks, '
    '); break; default: break; @@ -140,7 +166,7 @@ async function handler(ctx) { } item.description = description; - item.category = node.categories; + item.category = node.categories?.map((c) => c.title); return item; }) From bf9504c045f4213304bf712145e652c3584de8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeK=E6=97=A9=E7=82=B9=E7=9D=A1?= <1148579949@qq.com> Date: Fri, 18 Sep 2026 03:16:52 +0800 Subject: [PATCH 0839/1190] fix(route/openai): adapt cookbook to the new Astro-based site (#23024) --- lib/routes/openai/cookbook.ts | 154 ++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 54 deletions(-) diff --git a/lib/routes/openai/cookbook.ts b/lib/routes/openai/cookbook.ts index ab5eeb18bb61..2e0a4ebfc454 100644 --- a/lib/routes/openai/cookbook.ts +++ b/lib/routes/openai/cookbook.ts @@ -2,77 +2,123 @@ import { load } from 'cheerio'; import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; -import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/cookbook', categories: ['programming'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, description: 'OpenAI Cookbook 提供了大量使用 OpenAI API 的实用指南和示例代码,涵盖了从基础到高级的各种主题,包括 GPT 模型、嵌入、函数调用、微调等。这里汇集了最新的 API 功能介绍和流行的应用案例,是开发者学习和应用 OpenAI 技术的宝贵资源。', maintainers: ['liyaozhong'], radar: [ { - source: ['cookbook.openai.com/'], + source: ['developers.openai.com/cookbook', 'cookbook.openai.com/'], }, ], - url: 'cookbook.openai.com/', + url: 'developers.openai.com/cookbook', handler, example: '/openai/cookbook', name: 'Cookbook', }; +// Recursively decode Astro component props serialization. +// [0, value] is a scalar; [1, [...]] is an array. +function decodeAstroValue(val: unknown): unknown { + if (Array.isArray(val)) { + return val[0] === 0 ? decodeAstroValue(val[1]) : val[1].map((item) => decodeAstroValue(item)); + } + if (val !== null && typeof val === 'object') { + const result: Record = {}; + for (const [key, value] of Object.entries(val)) { + result[key] = decodeAstroValue(value); + } + return result; + } + return val; +} + +interface CookbookEntry { + title: string; + date: string; + internal_path: string; + description?: string; + tags?: string[]; + archived?: boolean; + authors?: Array<{ name: string }>; + authorAffiliations?: string[]; +} + async function handler() { - const rootUrl = 'https://cookbook.openai.com'; - const currentUrl = `${rootUrl}/`; - - try { - const response = await ofetch(currentUrl); - const $ = load(response); - - let items = $('[class="min-h-[90vh] mt-4"] .grid a') - .toArray() - .map((element): DataItem => { - const $element = $(element); - const $title = $element.find('div.font-semibold.text-sm.text-primary.line-clamp-1.overflow-ellipsis'); - const $date = $element.find(String.raw`span.text-xs.text-muted-foreground.md\:w-24.text-end`); - const $author = $element.find('p:contains("OpenAI")'); - const $tags = $element.find('span[style^="color:"]'); - - return { - title: $title.text().trim(), - link: `${rootUrl}/${$element.attr('href')}`, - pubDate: $date.text().trim(), - author: $author.text().replace('OpenAI', '').trim(), - category: $tags.toArray().map((tag) => $(tag).text().trim()), - }; - }); - - items = ( - await Promise.all( - items.map((item) => - cache.tryGet(item.link!, async () => { - try { - const detailResponse = await ofetch(item.link!); - const $ = load(detailResponse); - - item.description = $(String.raw`article.prose.prose-sm.sm\:prose-base.max-w-none.dark\:prose-invert`).html(); - return item; - } catch { - return { ...item, description: '' }; - } - }) - ) - ) - ).filter((item) => item?.description); - - return { - title: 'OpenAI Cookbook', - link: currentUrl, - item: items, - }; - } catch (error) { - logger.error(`处理 OpenAI Cookbook 请求时发生错误: ${error}`); - throw error; + const rootUrl = 'https://developers.openai.com'; + const currentUrl = `${rootUrl}/cookbook`; + + const response = await ofetch(currentUrl); + const $ = load(response); + + // Extract article data from the astro-island props attribute. + const propsRaw = $('astro-island[component-url*="Cookbook"]').attr('props'); + + if (!propsRaw) { + throw new TypeError('Failed to extract Cookbook component data from the page'); } + + // Parse the astro-island props JSON. + const props = JSON.parse(propsRaw) as { entries: [number, unknown[]] }; + const entriesArray = props.entries[1]; + + if (!Array.isArray(entriesArray)) { + throw new TypeError('Unexpected Cookbook entries format'); + } + + const articles = entriesArray + .map((item) => decodeAstroValue(item) as CookbookEntry | null) + .filter((item): item is CookbookEntry => item !== null && typeof item === 'object' && 'title' in item) + .filter((item) => !item.archived) + .toSorted((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + const items = await Promise.all( + articles.slice(0, 30).map((item) => + cache.tryGet(`${rootUrl}/cookbook/${item.internal_path}`, async () => { + const link = `${rootUrl}/cookbook/${item.internal_path}`; + const dataItem: DataItem = { + title: item.title, + link, + pubDate: parseDate(item.date), + category: item.tags ?? [], + description: item.description, + }; + + if (item.authorAffiliations?.length) { + dataItem.author = item.authorAffiliations.join(', '); + } else if (item.authors?.length) { + dataItem.author = item.authors + .map((a) => a.name) + .filter(Boolean) + .join(', '); + } + + const detailResponse = await ofetch(link); + const $detail = load(detailResponse); + const content = $detail('article').html(); + dataItem.description = content ?? dataItem.description; + + return dataItem; + }) + ) + ); + + return { + title: 'OpenAI Cookbook', + link: currentUrl, + item: items, + }; } From d1581ffe10040fe86ee6657f14a16dd6c0a97a66 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 17 Sep 2026 19:05:13 -0700 Subject: [PATCH 0840/1190] feat(route): add 35mmc (#23083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(route): add 35mmc The official feed at 35mmc.com/feed only carries excerpts. This route reads the WordPress REST API and returns full posts with all images, featured image, author and categories, optionally filtered by category. Co-Authored-By: Claude Fable 5 * fix(route/35mmc): correct the radar source host and target The site is canonical on www.35mmc.com, and `source[0]`'s hostname decides which subdomain the rule is registered under. Drop the explicit target so it falls back to the route path, whose category segment is optional. Co-Authored-By: Claude Fable 5 * fix(route/35mmc): strip lazy-loading attributes from embedded iframes too Readers that never trigger lazy loading show an empty box where the embed should be, the same way they do for images. Co-Authored-By: Claude Fable 5 * feat(route): add 35mmc — address review - Fetch the posts in a single embedded request instead of listing ids and then fetching each post; the default limit drops to 10, which keeps that one request at about 17s rather than 53s. - Drop the Array.isArray guards: these endpoints always answer with an array on success. - Drop the explicit `accept: application/json`; this host serves JSON either way, browser-like headers included. - Keep only the http to https rewrite on image sources, which the site really does need — all of its images are referenced over plain http and readers block them as mixed content. `loading`, `decoding` and `srcset` are left alone. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL --------- Co-authored-by: Claude Fable 5 --- lib/routes/35mmc/index.tsx | 133 ++++++++++++++++++++++++++++++++++ lib/routes/35mmc/namespace.ts | 9 +++ 2 files changed, 142 insertions(+) create mode 100644 lib/routes/35mmc/index.tsx create mode 100644 lib/routes/35mmc/namespace.ts diff --git a/lib/routes/35mmc/index.tsx b/lib/routes/35mmc/index.tsx new file mode 100644 index 000000000000..6e731c5cdb0d --- /dev/null +++ b/lib/routes/35mmc/index.tsx @@ -0,0 +1,133 @@ +import { load } from 'cheerio'; +import { decodeHTML } from 'entities'; +import { raw } from 'hono/html'; +import { renderToString } from 'hono/jsx/dom/server'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import type { DataItem, Route } from '@/types'; +import { ViewType } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const baseUrl = 'https://www.35mmc.com'; + +const api = (path: string, query: Record) => ofetch(`${baseUrl}/wp-json/wp/v2/${path}`, { query }); + +// Post bodies still reference their images over plain http, which readers block as mixed content +const cleanContent = (html: string): string => { + const $ = load(html, null, false); + $('img').each((_, el) => { + const $img = $(el); + const src = $img.attr('src'); + if (src?.startsWith('http://')) { + $img.attr('src', src.replace(/^http:\/\//, 'https://')); + } + }); + return $.html(); +}; + +export const route: Route = { + path: '/:category?', + categories: ['picture'], + view: ViewType.Articles, + example: '/35mmc/5-frames-with', + parameters: { category: 'Category slug, see the table below or the URL of a category page. All posts by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.35mmc.com/category/:category', 'www.35mmc.com/category/:parent/:category', 'www.35mmc.com/'], + }, + ], + name: 'Posts', + maintainers: ['IvanWng97'], + handler, + description: `The official feed only carries excerpts; this route returns the full post with all images. + +| Category | Slug | +| ----------------------------- | --------------------------------- | +| 5 frames with... | \`5-frames-with\` | +| Gear Reviews & Experiences | \`reviews-experinces\` | +| Photos & Projects | \`photos-projects\` | +| Theory & Reflections | \`theory-reflections\` | +| Philosophy & Reflections | \`philosophy-reflections\` | +| News & Events | \`news-events\` | +| One Shot Story | \`one-shot-story\` | +| Tutorials & Knowhow | \`tutorials-knowhow\` | +| Processes, Tutorials & Guides | \`tutorials\` | +| Learning Journeys | \`learning-journeys\` | +| Film | \`film\` | +| Lenses | \`lenses\` | +| Gear Theory | \`gear-theory\` | +| Compact Cameras | \`compact-cameras\` | +| Point & Shoot | \`point-shoot-film-camera-reviews\` | +| Rangefinder Cameras | \`rangefinder-cameras\` | +| SLRs | \`slrs\` | +| Scale Focus | \`scale-focus-cameras\` | +| Medium & Large Format | \`medium-format\` | +| Digital Cameras | \`digital-cameras\` | +| Accessories & More | \`accessories-more\` | +| Mods, DIY & Lens Adapting | \`lens-adapting-mods\` |`, +}; + +async function handler(ctx) { + const categorySlug = ctx.req.param('category'); + const limit = Number(ctx.req.query('limit')) || 10; + + const category = categorySlug + ? await cache.tryGet(`35mmc:category:${categorySlug}`, async () => { + const data = await api('categories', { slug: categorySlug }); + if (data.length === 0) { + throw new InvalidParameterError(`Category "${categorySlug}" not found`); + } + return { id: data[0].id, name: decodeHTML(data[0].name), link: data[0].link }; + }) + : undefined; + + const posts = await api('posts', { + per_page: limit, + _embed: 'wp:featuredmedia,wp:term', + ...(category && { categories: category.id }), + }); + + const items: DataItem[] = posts.map((post) => { + const featured = post._embedded?.['wp:featuredmedia']?.find((media) => media.id === post.featured_media); + const image = featured?.source_url; + + return { + title: decodeHTML(post.title.rendered), + link: post.link, + // WordPress returns *_gmt without a timezone designator + pubDate: parseDate(`${post.date_gmt}Z`), + updated: parseDate(`${post.modified_gmt}Z`), + // the users endpoint is disabled on this site, so the author name is only available via the Yoast SEO block + author: post.yoast_head_json?.author, + category: (post._embedded?.['wp:term'] ?? []).flat().map((term) => decodeHTML(term.name)), + description: renderToString( + <> + {image ? ( +
    + {featured.alt_text + {featured.caption?.rendered ?
    {raw(featured.caption.rendered)}
    : null} +
    + ) : null} + {raw(cleanContent(post.content.rendered))} + + ), + }; + }); + + return { + title: category ? `35mmc - ${category.name}` : '35mmc', + link: category?.link ?? baseUrl, + description: 'Film photography blog: camera and lens reviews, "5 frames with…", photo projects and essays.', + item: items, + }; +} diff --git a/lib/routes/35mmc/namespace.ts b/lib/routes/35mmc/namespace.ts new file mode 100644 index 000000000000..553d53f44720 --- /dev/null +++ b/lib/routes/35mmc/namespace.ts @@ -0,0 +1,9 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '35mmc', + url: '35mmc.com', + categories: ['picture'], + description: 'Film photography blog: camera and lens reviews, "5 frames with…", photo projects and essays.', + lang: 'en', +}; From f4ed1d4696678a05234a5f8009659a55ef0705b5 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2026 10:32:48 +0800 Subject: [PATCH 0841/1190] fix(route/ff14): update api url (#23317) --- lib/routes/ff14/ff14-global.ts | 2 +- lib/routes/ff14/ff14-zh.ts | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/routes/ff14/ff14-global.ts b/lib/routes/ff14/ff14-global.ts index 8e271494a0e5..06ec55133313 100644 --- a/lib/routes/ff14/ff14-global.ts +++ b/lib/routes/ff14/ff14-global.ts @@ -7,7 +7,7 @@ import { isValidHost } from '@/utils/valid-host'; import { renderDescription } from './templates/description'; export const route: Route = { - path: ['/global/:lang/:type?', '/ff14_global/:lang/:type?'], + path: '/global/:lang/:type?', categories: ['game'], example: '/ff14/global/na/all', parameters: { lang: 'Region', type: 'Category, `all` by default' }, diff --git a/lib/routes/ff14/ff14-zh.ts b/lib/routes/ff14/ff14-zh.ts index 72c59631d885..0d0e6707b41e 100644 --- a/lib/routes/ff14/ff14-zh.ts +++ b/lib/routes/ff14/ff14-zh.ts @@ -6,7 +6,7 @@ import timezone from '@/utils/timezone'; import { renderDescription } from './templates/description'; export const route: Route = { - path: ['/zh/:type?', '/ff14_zh/:type?'], + path: '/zh/:type?', categories: ['game'], example: '/ff14/zh/news', parameters: { type: '分类名,预设为 `all`' }, @@ -34,7 +34,7 @@ export const route: Route = { }; async function handler(ctx) { - const referer = 'https://ff.sdo.com/web8/index.html'; + const referer = 'https://ff.web.sdo.com/web8/index.html'; const type = ctx.req.param('type') ?? 'all'; const categories = { @@ -49,13 +49,7 @@ async function handler(ctx) { all: `5309,${Object.values(categories).join(',')}`, }; - const response = await got({ - method: 'get', - url: `http://api.act.sdo.com/UnionNews/List?gameCode=ff&category=${typeNumber[type]}&pageIndex=0&pageSize=50`, - headers: { - Referer: referer, - }, - }); + const response = await got(`https://cqnews.web.sdo.com/api/news/newsList?gameCode=ff&CategoryCode=${typeNumber[type]}&pageIndex=0&pageSize=50`); const data = response.data.Data; @@ -63,9 +57,9 @@ async function handler(ctx) { title: '最终幻想14(国服)新闻中心', link: referer + '#/newstab/newslist', description: '《最终幻想14》是史克威尔艾尼克斯出品的全球经典游戏品牌FINAL FANTASY系列的最新作品,IGN获得9.2高分!全球累计用户突破1600万!', - item: data.map(({ Title, Summary, Author, PublishDate, HomeImagePath, Id }) => ({ + item: data.map(({ Title, Summary, OutLink, PublishDate, HomeImagePath, Id }) => ({ title: Title, - link: Author || `https://ff.web.sdo.com/web8/index.html#/newstab/newscont/${Id}`, + link: OutLink || `https://ff.web.sdo.com/web8/index.html#/newstab/newscont/${Id}`, description: renderDescription({ image: HomeImagePath, description: Summary, From e24934cbadcbd6180f05f09ec439074883cba00d Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 17 Sep 2026 22:34:26 -0700 Subject: [PATCH 0842/1190] fix(route/theverge): handle list blocks without items (#23318) Follow-up to #23082. The GraphQL query selects no fields for a list nested inside a quote, so the block arrives as `{"__typename":"CoreListBlockType"}` and `b.items.map` threw, taking down the whole feed: TypeError: Cannot read properties of undefined (reading 'map') Reproduced with the quick post "Anthropic proposes three rules for measuring AI progress" inside the stream https://www.theverge.com/ai-artificial-intelligence/996923/ai-safety-slow-openai-anthropic Across 81 articles in 11 hub feeds this is the only block type that arrives without fields; paragraphs nested in the same quote carry theirs. Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL Co-authored-by: Claude Opus 5 --- lib/routes/theverge/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/routes/theverge/index.ts b/lib/routes/theverge/index.ts index 7ccb763a46bd..5d3fc8021b5c 100644 --- a/lib/routes/theverge/index.ts +++ b/lib/routes/theverge/index.ts @@ -90,7 +90,8 @@ const renderBlock = (b) => { case 'CoreImageBlockType': return `
    ${b.alt}
    ${b.caption.html}
    `; case 'CoreListBlockType': - return `${b.ordered ? '
      ' : '
        '}${b.items.map((i) => `
      • ${renderContents(i)}
      • `).join('')}${b.ordered ? '
    ' : ''}`; + // a list nested in a quote comes through the GraphQL payload with no fields beyond __typename + return b.items?.length ? `${b.ordered ? '
      ' : '
        '}${b.items.map((i) => `
      • ${renderContents(i)}
      • `).join('')}${b.ordered ? '
    ' : ''}` : ''; case 'CoreParagraphBlockType': return renderContents(b); case 'CorePullquoteBlockType': From e64df443e8db215a3ef2a111e0edc3157e105b64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:16:31 +0000 Subject: [PATCH 0843/1190] chore(deps): bump hono from 4.13.7 to 4.13.8 in the hono group (#23321) Bumps the hono group with 1 update: [hono](https://github.com/honojs/hono). Updates `hono` from 4.13.7 to 4.13.8 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.13.7...v4.13.8) --- updated-dependencies: - dependency-name: hono dependency-version: 4.13.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 254 ++++++++++++++++++++++++------------------------- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index 993bdc817b38..f8e51b176293 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "fanfou-sdk": "6.0.0", "google-play-scraper": "10.1.3", "header-generator": "2.1.88", - "hono": "4.13.7", + "hono": "4.13.8", "html-to-text": "10.0.1", "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 382c7f159c34..83bfd5341911 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -51,10 +51,10 @@ importers: version: 6.16.3 '@hono/node-server': specifier: 2.1.1 - version: 2.1.1(hono@4.13.7) + version: 2.1.1(hono@4.13.8) '@hono/zod-openapi': specifier: 1.6.3 - version: 1.6.3(hono@4.13.7)(zod@4.6.5) + version: 1.6.3(hono@4.13.8)(zod@4.6.5) '@jocmp/mercury-parser': specifier: 3.1.0 version: 3.1.0 @@ -90,7 +90,7 @@ importers: version: 0.0.25 '@scalar/hono-api-reference': specifier: 0.12.1 - version: 0.12.1(hono@4.13.7) + version: 0.12.1(hono@4.13.8) '@sentry/node': specifier: 10.74.0 version: 10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) @@ -134,8 +134,8 @@ importers: specifier: 2.1.88 version: 2.1.88 hono: - specifier: 4.13.7 - version: 4.13.7 + specifier: 4.13.8 + version: 4.13.8 html-to-text: specifier: 10.0.1 version: 10.0.1 @@ -364,7 +364,7 @@ importers: version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 - version: 1.11.0(rollup@4.63.2) + version: 1.11.0(rollup@4.63.3) '@vitest/coverage-v8': specifier: 4.1.11 version: 4.1.11(vitest@4.1.11) @@ -2489,141 +2489,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.63.2': - resolution: {integrity: sha512-Xa6RDoWa+hNiX6PgsljlH6W75RaONx3y6PVlbLhkEWW+GaPQ3dP5gwbL/erAzQHWwkvW5UxdD5l87Qx2FAQ/4A==} + '@rollup/rollup-android-arm-eabi@4.63.3': + resolution: {integrity: sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.63.2': - resolution: {integrity: sha512-vNASxsghMfQ5s+v3PrpnJd+ryL/26lxCCaGI+sDJ7VzmHiYXIrrVltsDhaawxLM1WcoMU2oYlbPHLaYQtBzhcg==} + '@rollup/rollup-android-arm64@4.63.3': + resolution: {integrity: sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.63.2': - resolution: {integrity: sha512-0dWDjmlrpZAgjPD/aPzUDhBW8APLRjAni5bOrM76wiiZm+E+KTMVKNhAzaTBohz8UyO2fKNAl0+fygbe2HZXOA==} + '@rollup/rollup-darwin-arm64@4.63.3': + resolution: {integrity: sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.63.2': - resolution: {integrity: sha512-N58uktcwzk3+qT4KHEuNdIxX1N01RWrkfVoml69EAbSaNDL+sbNVLx2RMl4Qd23lpA0fgPvyh5hHb4weD5WKmg==} + '@rollup/rollup-darwin-x64@4.63.3': + resolution: {integrity: sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.63.2': - resolution: {integrity: sha512-HWF2zH8EAp2scWRpt2PGe6iUGz7zi04waXsdRr3zb4DWCk2ImIo5FZu0jjmD53nP/DGSvnW0e7/1ToCNZs2lZw==} + '@rollup/rollup-freebsd-arm64@4.63.3': + resolution: {integrity: sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.63.2': - resolution: {integrity: sha512-MkvcwHMnzPSMOQEwB6wHnLzmc+hT8BGc5bW/Mhmjjgx3wbj6VBnlc47XsK74kD0K9MikFfXpQqyz4NUXaUW62A==} + '@rollup/rollup-freebsd-x64@4.63.3': + resolution: {integrity: sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.63.2': - resolution: {integrity: sha512-xe1bCKPJaKsD0tfd7Rb6bGfUogJTpKbTEEthsfdb7hTfTRNJVQTdirabQx0o6ERVba/smkM720soMY+0QnrlSQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.63.3': + resolution: {integrity: sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.63.2': - resolution: {integrity: sha512-yOM7LdK0p6gk6+Q773OEwtlsikT1TL3yMmYsTtRlDRPha5vV2DC5x7LqRWDr6f3cSYNMKVqxzffXv8ivxNBIFQ==} + '@rollup/rollup-linux-arm-musleabihf@4.63.3': + resolution: {integrity: sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.63.2': - resolution: {integrity: sha512-qiWuJJV3DybA2IfzvRimeKXGrGuVPv1zobSY/26KnP3HbV0VcNb3ECzgvtbvF3xjSMkcooou6HASXZuLdjnhpQ==} + '@rollup/rollup-linux-arm64-gnu@4.63.3': + resolution: {integrity: sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.63.2': - resolution: {integrity: sha512-akcZquRzCY/KpUoZAMBhGf7oi4LmXq1BzRA5CPAC3rkUf28Y/sAYV3jSL+JKd7cwEyFvR5G0XVZ0gaMedP+60A==} + '@rollup/rollup-linux-arm64-musl@4.63.3': + resolution: {integrity: sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.63.2': - resolution: {integrity: sha512-fNwYHrPyYyxauPzX/cpYw8Z7LQpp+DGA0KCoswA0aVFBpmdMil9XgjB8V3Ny64Ihu797+GKcuJqnsOKEmor7fA==} + '@rollup/rollup-linux-loong64-gnu@4.63.3': + resolution: {integrity: sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.63.2': - resolution: {integrity: sha512-XfvsgzR7DZqREdst7K1Mj3ilSUM5xLAHJcIMDFPKdxTs9q5VHOT8aMA+a683fqBu7DQl8+Sd9HCsQYL8EMY9qA==} + '@rollup/rollup-linux-loong64-musl@4.63.3': + resolution: {integrity: sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.63.2': - resolution: {integrity: sha512-Pp7gVZggEFlbcuztay+/U0gVG9S1XAh8i7I1Re/htbAzo43P5wHZHw6pTyzotISqlKohoh9RpIfnOz3RbemK1w==} + '@rollup/rollup-linux-ppc64-gnu@4.63.3': + resolution: {integrity: sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.63.2': - resolution: {integrity: sha512-zkgL2xff6i7u5hau/m6FGeS8gRkLEdgLw522WGmdWWlLd9btmNl3S80mcEjtGq+kvgUekQ3+BOYLLLcPlS2LIA==} + '@rollup/rollup-linux-ppc64-musl@4.63.3': + resolution: {integrity: sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.63.2': - resolution: {integrity: sha512-qOheJomrkVCbbHFJ7L3J97cnhfogKqguAQphv26+3ZsAQIF1L19b+dArl//s8rjJHJLz9byykyM8NBP4nmSa1g==} + '@rollup/rollup-linux-riscv64-gnu@4.63.3': + resolution: {integrity: sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.63.2': - resolution: {integrity: sha512-XlxLD54wQhH3FciCgMofxBw27NzUe818gJH410qWvc41UT0ZFcgxVjyX5/EK8MPTupjeVWqN5oy+9pCA9mqfCA==} + '@rollup/rollup-linux-riscv64-musl@4.63.3': + resolution: {integrity: sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.63.2': - resolution: {integrity: sha512-vdryWeRb2bLJZf0Fv/W8se6nvsHe2PkTCxV0meheK3nQE+G90VCJcke51Miy1yQRsfm2uqIyjXOu4wmUzbTtkQ==} + '@rollup/rollup-linux-s390x-gnu@4.63.3': + resolution: {integrity: sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.63.2': - resolution: {integrity: sha512-bcq2h2pkKmH2po4cZV8VWzO4lL40STyu/nLoFpYMQp9C2tCVNTdcVv86MwSsn3D5s1FBe2Ty1atqvVAUTMimNg==} + '@rollup/rollup-linux-x64-gnu@4.63.3': + resolution: {integrity: sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.63.2': - resolution: {integrity: sha512-EGoo5DMVMRkTId8fuTDaoxVlR5ZTsKULUezRjd9gCw5eeY+DjCvDpZAOlNUvKPGX+7rS1RWx6j+yOpNPx0cUgQ==} + '@rollup/rollup-linux-x64-musl@4.63.3': + resolution: {integrity: sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.63.2': - resolution: {integrity: sha512-MErl12k7BFHZG1TI9QF/3lSSZARzq9KgNy/FjnqFMCkv+N4RSSzoUCA5h2mqHX4Mox3WaTVKblyzhQ1zRb2ZuQ==} + '@rollup/rollup-openbsd-x64@4.63.3': + resolution: {integrity: sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.63.2': - resolution: {integrity: sha512-ILs8k07Wh4p0PsNY4wYLEaXZKMOpVhrG5QDB0yHhGhuzOfDlnyHN6sflL4El/MpUP1y8uY2lUZrv4oBS6pTT3g==} + '@rollup/rollup-openharmony-arm64@4.63.3': + resolution: {integrity: sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.63.2': - resolution: {integrity: sha512-hKgB3nz/TKD3Wv78XEsyXzQsNjvhOHmwKQTvXADGOyU/cIClZDO7DsoggbdmJDPGp5V80tA3Vfv61PaKTLH3LA==} + '@rollup/rollup-win32-arm64-msvc@4.63.3': + resolution: {integrity: sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.63.2': - resolution: {integrity: sha512-T4wf1mudIDxN8Q/CWIBJC1u5gQUc+r5mPvlwoSbIvNkyVTP2TAFeobEmst5AQ4gMyAz4sSByVdoTDfvTmGK/8g==} + '@rollup/rollup-win32-ia32-msvc@4.63.3': + resolution: {integrity: sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.63.2': - resolution: {integrity: sha512-tC3IY7qoaD9Ll3/8WJQn49j5V2f/NuI9S41NOE2iM5MPs3sPIvOkVToLcz/7Bz4pyF7PSvrtwu8I/pUrGOSecQ==} + '@rollup/rollup-win32-x64-gnu@4.63.3': + resolution: {integrity: sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.63.2': - resolution: {integrity: sha512-6NHnk/K3eq2ZFYcU1X8g67s9qIJRCOTT92gwLMVBp08dB2uuuwI1/Q/empzL2Bfr2f2WRLJVwpp90RmacQyFkw==} + '@rollup/rollup-win32-x64-msvc@4.63.3': + resolution: {integrity: sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==} cpu: [x64] os: [win32] @@ -3722,7 +3722,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -4251,8 +4251,8 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.13.7: - resolution: {integrity: sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==} + hono@4.13.8: + resolution: {integrity: sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==} engines: {node: '>=16.9.0'} hookable@6.1.1: @@ -5523,8 +5523,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.63.2: - resolution: {integrity: sha512-l5eyksV4tPBj6lJyEa37YzIOCSOV7lkZzEHUdpjWZbtD7wTcFYmEYXSgm5bT4vV+dZLb9rBG1W9GROOG4NS4Ew==} + rollup@4.63.3: + resolution: {integrity: sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6948,21 +6948,21 @@ snapshots: '@types/aws-lambda': 8.10.162 '@types/express': 5.0.6 - '@hono/node-server@2.1.1(hono@4.13.7)': + '@hono/node-server@2.1.1(hono@4.13.8)': dependencies: - hono: 4.13.7 + hono: 4.13.8 - '@hono/zod-openapi@1.6.3(hono@4.13.7)(zod@4.6.5)': + '@hono/zod-openapi@1.6.3(hono@4.13.8)(zod@4.6.5)': dependencies: '@asteasolutions/zod-to-openapi': 9.1.0(zod@4.6.5) - '@hono/zod-validator': 0.9.1(hono@4.13.7)(zod@4.6.5) - hono: 4.13.7 + '@hono/zod-validator': 0.9.1(hono@4.13.8)(zod@4.6.5) + hono: 4.13.8 openapi3-ts: 4.6.1 zod: 4.6.5 - '@hono/zod-validator@0.9.1(hono@4.13.7)(zod@4.6.5)': + '@hono/zod-validator@0.9.1(hono@4.13.8)(zod@4.6.5)': dependencies: - hono: 4.13.7 + hono: 4.13.8 zod: 4.6.5 '@humanfs/core@0.19.2': @@ -7819,87 +7819,87 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.63.2)': + '@rollup/pluginutils@5.4.0(rollup@4.63.3)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: - rollup: 4.63.2 + rollup: 4.63.3 - '@rollup/rollup-android-arm-eabi@4.63.2': + '@rollup/rollup-android-arm-eabi@4.63.3': optional: true - '@rollup/rollup-android-arm64@4.63.2': + '@rollup/rollup-android-arm64@4.63.3': optional: true - '@rollup/rollup-darwin-arm64@4.63.2': + '@rollup/rollup-darwin-arm64@4.63.3': optional: true - '@rollup/rollup-darwin-x64@4.63.2': + '@rollup/rollup-darwin-x64@4.63.3': optional: true - '@rollup/rollup-freebsd-arm64@4.63.2': + '@rollup/rollup-freebsd-arm64@4.63.3': optional: true - '@rollup/rollup-freebsd-x64@4.63.2': + '@rollup/rollup-freebsd-x64@4.63.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.63.2': + '@rollup/rollup-linux-arm-gnueabihf@4.63.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.63.2': + '@rollup/rollup-linux-arm-musleabihf@4.63.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.63.2': + '@rollup/rollup-linux-arm64-gnu@4.63.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.63.2': + '@rollup/rollup-linux-arm64-musl@4.63.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.63.2': + '@rollup/rollup-linux-loong64-gnu@4.63.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.63.2': + '@rollup/rollup-linux-loong64-musl@4.63.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.63.2': + '@rollup/rollup-linux-ppc64-gnu@4.63.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.63.2': + '@rollup/rollup-linux-ppc64-musl@4.63.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.63.2': + '@rollup/rollup-linux-riscv64-gnu@4.63.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.63.2': + '@rollup/rollup-linux-riscv64-musl@4.63.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.63.2': + '@rollup/rollup-linux-s390x-gnu@4.63.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.63.2': + '@rollup/rollup-linux-x64-gnu@4.63.3': optional: true - '@rollup/rollup-linux-x64-musl@4.63.2': + '@rollup/rollup-linux-x64-musl@4.63.3': optional: true - '@rollup/rollup-openbsd-x64@4.63.2': + '@rollup/rollup-openbsd-x64@4.63.3': optional: true - '@rollup/rollup-openharmony-arm64@4.63.2': + '@rollup/rollup-openharmony-arm64@4.63.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.63.2': + '@rollup/rollup-win32-arm64-msvc@4.63.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.63.2': + '@rollup/rollup-win32-ia32-msvc@4.63.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.63.2': + '@rollup/rollup-win32-x64-gnu@4.63.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.63.2': + '@rollup/rollup-win32-x64-msvc@4.63.3': optional: true '@rss3/api-core@0.0.25': @@ -7925,10 +7925,10 @@ snapshots: '@scalar/helpers@0.11.3': {} - '@scalar/hono-api-reference@0.12.1(hono@4.13.7)': + '@scalar/hono-api-reference@0.12.1(hono@4.13.8)': dependencies: '@scalar/client-side-rendering': 0.4.0 - hono: 4.13.7 + hono: 4.13.8 '@scalar/schemas@0.9.0': dependencies: @@ -8327,10 +8327,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.11.0(rollup@4.63.2)': + '@vercel/nft@1.11.0(rollup@4.63.3)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0(rollup@4.63.2) + '@rollup/pluginutils': 5.4.0(rollup@4.63.3) acorn: 8.18.0 acorn-import-attributes: 1.9.5(acorn@8.18.0) async-sema: 3.1.1 @@ -9585,7 +9585,7 @@ snapshots: hmacsha1@1.0.0: {} - hono@4.13.7: {} + hono@4.13.8: {} hookable@6.1.1: {} @@ -11145,36 +11145,36 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.8 '@rolldown/binding-win32-x64-msvc': 1.2.8 - rollup@4.63.2: + rollup@4.63.3: dependencies: '@types/estree': 1.0.9 optionalDependencies: '@napi-rs/lzma-linux-x64-gnu': 1.5.1 - '@rollup/rollup-android-arm-eabi': 4.63.2 - '@rollup/rollup-android-arm64': 4.63.2 - '@rollup/rollup-darwin-arm64': 4.63.2 - '@rollup/rollup-darwin-x64': 4.63.2 - '@rollup/rollup-freebsd-arm64': 4.63.2 - '@rollup/rollup-freebsd-x64': 4.63.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.63.2 - '@rollup/rollup-linux-arm-musleabihf': 4.63.2 - '@rollup/rollup-linux-arm64-gnu': 4.63.2 - '@rollup/rollup-linux-arm64-musl': 4.63.2 - '@rollup/rollup-linux-loong64-gnu': 4.63.2 - '@rollup/rollup-linux-loong64-musl': 4.63.2 - '@rollup/rollup-linux-ppc64-gnu': 4.63.2 - '@rollup/rollup-linux-ppc64-musl': 4.63.2 - '@rollup/rollup-linux-riscv64-gnu': 4.63.2 - '@rollup/rollup-linux-riscv64-musl': 4.63.2 - '@rollup/rollup-linux-s390x-gnu': 4.63.2 - '@rollup/rollup-linux-x64-gnu': 4.63.2 - '@rollup/rollup-linux-x64-musl': 4.63.2 - '@rollup/rollup-openbsd-x64': 4.63.2 - '@rollup/rollup-openharmony-arm64': 4.63.2 - '@rollup/rollup-win32-arm64-msvc': 4.63.2 - '@rollup/rollup-win32-ia32-msvc': 4.63.2 - '@rollup/rollup-win32-x64-gnu': 4.63.2 - '@rollup/rollup-win32-x64-msvc': 4.63.2 + '@rollup/rollup-android-arm-eabi': 4.63.3 + '@rollup/rollup-android-arm64': 4.63.3 + '@rollup/rollup-darwin-arm64': 4.63.3 + '@rollup/rollup-darwin-x64': 4.63.3 + '@rollup/rollup-freebsd-arm64': 4.63.3 + '@rollup/rollup-freebsd-x64': 4.63.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.3 + '@rollup/rollup-linux-arm-musleabihf': 4.63.3 + '@rollup/rollup-linux-arm64-gnu': 4.63.3 + '@rollup/rollup-linux-arm64-musl': 4.63.3 + '@rollup/rollup-linux-loong64-gnu': 4.63.3 + '@rollup/rollup-linux-loong64-musl': 4.63.3 + '@rollup/rollup-linux-ppc64-gnu': 4.63.3 + '@rollup/rollup-linux-ppc64-musl': 4.63.3 + '@rollup/rollup-linux-riscv64-gnu': 4.63.3 + '@rollup/rollup-linux-riscv64-musl': 4.63.3 + '@rollup/rollup-linux-s390x-gnu': 4.63.3 + '@rollup/rollup-linux-x64-gnu': 4.63.3 + '@rollup/rollup-linux-x64-musl': 4.63.3 + '@rollup/rollup-openbsd-x64': 4.63.3 + '@rollup/rollup-openharmony-arm64': 4.63.3 + '@rollup/rollup-win32-arm64-msvc': 4.63.3 + '@rollup/rollup-win32-ia32-msvc': 4.63.3 + '@rollup/rollup-win32-x64-gnu': 4.63.3 + '@rollup/rollup-win32-x64-msvc': 4.63.3 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -11751,7 +11751,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 postcss: 8.5.28 - rollup: 4.63.2 + rollup: 4.63.3 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.5.1 From bd6d6bdb5345f409248a18445d09856fcba09aee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:20:10 +0000 Subject: [PATCH 0844/1190] chore(deps): bump imapflow from 2.0.2 to 2.0.3 (#23326) Bumps [imapflow](https://github.com/postalsys/imapflow) from 2.0.2 to 2.0.3. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v2.0.2...v2.0.3) --- updated-dependencies: - dependency-name: imapflow dependency-version: 2.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index f8e51b176293..e8fe32decbb8 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "2.0.2", + "imapflow": "2.0.3", "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83bfd5341911..ebaeb3de7fce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 2.0.2 - version: 2.0.2 + specifier: 2.0.3 + version: 2.0.3 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -4357,8 +4357,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@2.0.2: - resolution: {integrity: sha512-jrWWecWFifN3Feum2MqaTUupNeYSAdPEltKuwGdr8IPdxW72NZXf7ObVUfTWoUH2MPrli0q7SZm9pRt8ob/9XQ==} + imapflow@2.0.3: + resolution: {integrity: sha512-Y5jq5EbG+7hZVZKBRw6gMii/wh+ZdWpRkq9PE5UHVNm29lE89QodiTxcAWxpTFwqroGsb2UXx3z5TO0Z8tirEw==} engines: {node: '>=20.0.0'} import-in-the-middle@3.4.0: @@ -4403,6 +4403,10 @@ packages: resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} engines: {node: '>= 12'} + ip-address@10.7.1: + resolution: {integrity: sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA==} + engines: {node: '>= 12'} + ip-regex@4.3.0: resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} engines: {node: '>=8'} @@ -9700,7 +9704,7 @@ snapshots: image-size@0.7.5: {} - imapflow@2.0.2: + imapflow@2.0.3: dependencies: '@zone-eu/mailsplit': 5.4.16 encoding-japanese: 2.3.0 @@ -9771,6 +9775,8 @@ snapshots: ip-address@10.7.0: {} + ip-address@10.7.1: {} + ip-regex@4.3.0: {} ip-regex@5.0.0: {} @@ -11295,7 +11301,7 @@ snapshots: socks@2.8.10: dependencies: - ip-address: 10.7.0 + ip-address: 10.7.1 smart-buffer: 4.2.0 socks@2.8.9: From b6fb5d262299490e1be2f762990c8a17160e97ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 17:56:09 +0800 Subject: [PATCH 0845/1190] chore(deps): bump the nix group with 2 updates (#23327) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `6e83050` to `8ac3f3a` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/6e830506b517d6a373f2dec6ee8c7f4683908856...8ac3f3a9f47df8c23e4b24c8488d609c7d9bd7a3) Updates `nixpkgs` from `eaad089` to `ef34387` - [Commits](https://github.com/NixOS/nixpkgs/compare/eaad089433ca2bb662274377d33df3d0e51ef28b...ef34387ddd751e1ab8857adf4676492d32eb24ec) --- updated-dependencies: - dependency-name: devenv dependency-version: 8ac3f3a9f47df8c23e4b24c8488d609c7d9bd7a3 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: ef34387ddd751e1ab8857adf4676492d32eb24ec dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 5e5eb4bd7ed2..128e2449c611 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789340509, - "narHash": "sha256-It2AD16uFwiAe9GvuLbk+j+qTDrQxT3XPclL71ZnJ3I=", + "lastModified": 1789395478, + "narHash": "sha256-s3k1Qkp37Jz2oWyjDgLfzpCIrdg/ESYS0G/Y/Cy0k/8=", "owner": "cachix", "repo": "devenv", - "rev": "6e830506b517d6a373f2dec6ee8c7f4683908856", + "rev": "8ac3f3a9f47df8c23e4b24c8488d609c7d9bd7a3", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789149629, - "narHash": "sha256-H6GwaZzZf+4npqv0tph94w9tZddSjFjmQrVsW0z78uk=", + "lastModified": 1789286504, + "narHash": "sha256-eiEK7cKZORNEvX0GeF3RtNEF/JXhgf2RqSp3230q13E=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "eaad089433ca2bb662274377d33df3d0e51ef28b", + "rev": "ef34387ddd751e1ab8857adf4676492d32eb24ec", "type": "github" }, "original": { From c06d7870cd26e1225a942186a2ae8322de6df256 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 20:55:37 +0800 Subject: [PATCH 0846/1190] chore(deps): bump docker/setup-buildx-action from 4.3.0 to 4.4.0 (#23322) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/37fe631027851001ddb9b187196cc803df7f5f0e...594f3bf4285d9ea8dc53c9a0c9c4092420091003) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 4 ++-- .github/workflows/docker-test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index f08f172b2a0d..2bc631731fcd 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -71,7 +71,7 @@ jobs: echo "repo-name=$REPO_NAME_LOWER" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0 - name: Log in to Docker Hub uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 @@ -204,7 +204,7 @@ jobs: id-token: write steps: - name: Set up Docker Buildx - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0 - name: Log in to Docker Hub uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 741f5ffc649f..c3a4bd8759b1 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Docker Buildx # needed by `cache-from` - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0 - name: Extract Docker metadata id: meta From 14079b86929311b14a4f0edd925d80a860e86ad4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:12:34 +0800 Subject: [PATCH 0847/1190] chore(deps): bump docker/build-push-action from 7.3.0 to 7.4.0 (#23319) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.3.0 to 7.4.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/53b7df96c91f9c12dcc8a07bcb9ccacbed38856a...c3c9e263c25d99ce0380d002d59b67737d91b0dc) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 7.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-release.yml | 4 ++-- .github/workflows/docker-test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 2bc631731fcd..f93221a3bd1c 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -107,7 +107,7 @@ jobs: - name: Build and push Docker image (ordinary version) id: build-and-push - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . tags: ${{ steps.image-name-ordinary.outputs.tags }} @@ -160,7 +160,7 @@ jobs: - name: Build and push Docker image (Chromium-bundled version) id: build-and-push-chromium - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . build-args: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index c3a4bd8759b1..0bbad3b60e76 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -40,7 +40,7 @@ jobs: flavor: latest=true - name: Build Docker image - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . build-args: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 # also test bundling Chromium From 809f244a5a0a8778e40fe9371b0c976b8a81c141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:31:31 +0800 Subject: [PATCH 0848/1190] chore(deps): bump @googleapis/youtube from 38.0.0 to 39.0.0 (#23325) Bumps [@googleapis/youtube](https://github.com/googleapis/google-api-nodejs-client) from 38.0.0 to 39.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/v39.0.0/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v38.0.0...v39.0.0) --- updated-dependencies: - dependency-name: "@googleapis/youtube" dependency-version: 39.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e8fe32decbb8..48ce66cc482b 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@bbob/html": "4.4.1", "@bbob/plugin-helper": "4.4.1", "@bbob/preset-html5": "4.4.1", - "@googleapis/youtube": "38.0.0", + "@googleapis/youtube": "39.0.0", "@honeybadger-io/js": "6.16.3", "@hono/node-server": "2.1.1", "@hono/zod-openapi": "1.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ebaeb3de7fce..c87dabcc184e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,8 +44,8 @@ importers: specifier: 4.4.1 version: 4.4.1 '@googleapis/youtube': - specifier: 38.0.0 - version: 38.0.0 + specifier: 39.0.0 + version: 39.0.0 '@honeybadger-io/js': specifier: 6.16.3 version: 6.16.3 @@ -1232,8 +1232,8 @@ packages: '@noble/hashes': optional: true - '@googleapis/youtube@38.0.0': - resolution: {integrity: sha512-1E8QQ9cZKt2bs0ly6lketaNPO1pmgqLLCBUXkKzwPiRB7Gm478wUzlAdFc4gbQaT4ZirlO0xUIUon+lVMv8Svg==} + '@googleapis/youtube@39.0.0': + resolution: {integrity: sha512-ROGXvKLwOWOS4dSEexWS4ZF061ZGaTmGzkdOiTB6OvpimzmbnVyMioRRi0q+/sZ6C1CwffOb8+WYo5SPiZlTag==} engines: {node: '>=22.0.0'} '@honeybadger-io/core@6.11.1': @@ -6935,7 +6935,7 @@ snapshots: optionalDependencies: '@noble/hashes': 2.3.0 - '@googleapis/youtube@38.0.0': + '@googleapis/youtube@39.0.0': dependencies: googleapis-common: 9.0.4 transitivePeerDependencies: From d1f247fed815579cb7e009a4033d09d9f759d9ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:39:27 +0800 Subject: [PATCH 0849/1190] chore(deps-dev): bump the cloudflare group with 3 updates (#23320) Bumps the cloudflare group with 3 updates: [@cloudflare/vitest-plugin](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-plugin), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-plugin` from 1.1.5 to 1.1.9 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-plugin/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-plugin@1.1.9/packages/vitest-plugin) Updates `@cloudflare/workers-types` from 5.20260908.1 to 5.20260915.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.129.1 to 4.131.2 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.131.2/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-plugin" dependency-version: 1.1.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260915.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.131.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +- pnpm-lock.yaml | 349 +++++++++++++++++++++++++------------------------ 2 files changed, 182 insertions(+), 173 deletions(-) diff --git a/package.json b/package.json index 48ce66cc482b..9825a66c4d5f 100644 --- a/package.json +++ b/package.json @@ -152,8 +152,8 @@ "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", - "@cloudflare/vitest-plugin": "1.1.5", - "@cloudflare/workers-types": "5.20260908.1", + "@cloudflare/vitest-plugin": "1.1.9", + "@cloudflare/workers-types": "5.20260915.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.82.0", "@stylistic/eslint-plugin": "5.10.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.3", "vitest": "4.1.11", - "wrangler": "4.129.1", + "wrangler": "4.131.2", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c87dabcc184e..e240e6393de5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,11 +306,11 @@ importers: specifier: 1.3.6 version: 1.3.6 '@cloudflare/vitest-plugin': - specifier: 1.1.5 - version: 1.1.5(@cloudflare/workers-types@5.20260908.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + specifier: 1.1.9 + version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260908.1 - version: 5.20260908.1 + specifier: 5.20260915.1 + version: 5.20260915.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) @@ -471,8 +471,8 @@ importers: specifier: 4.1.11 version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: - specifier: 4.129.1 - version: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.131.2 + version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -597,45 +597,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-plugin@1.1.5': - resolution: {integrity: sha512-bXO4ObOJllPTzUYbklEsFy+Kvy0E4MFvpzXmbe/UFQpEFHAxiUE9vl9glnyqfBwrFjNDxvQQbA8CNA9WuPpAIg==} + '@cloudflare/vitest-plugin@1.1.9': + resolution: {integrity: sha512-RzKBjIwoTAgENJ7YBhCStb9F5ZBW3GDv7uQKBMxQWQBoXP+jip2RI+FXwPeRuBEOj41urTmCYrbYLMmp3448lQ==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260907.1': - resolution: {integrity: sha512-Xaum46kviN8xixl0axXm7mfLTf2GAG+baotnEIv8M5EbvyHxHagvS5Z8bU5B/WJlTWwwvGzItFfKZvga78o3ew==} + '@cloudflare/workerd-darwin-64@1.20260911.1': + resolution: {integrity: sha512-785eaY1bkR1cm4Z/PCUeteZYmTMe6lre2zz63/GdGGimsoMsKxgl4brFPRukim8iv28EyD1XoCB/VPYF20BERA==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260907.1': - resolution: {integrity: sha512-P2o7xpZbxvoES9xY5uHqJPXvsWBWi0w1STCGoYLqxI1rh1gSUdlr4HruMiWq/TA94PPoQCg0eUfoshyQxMdGpA==} + '@cloudflare/workerd-darwin-arm64@1.20260911.1': + resolution: {integrity: sha512-WU4bFqEN0H7ndGWxoedegv95DmNVBtv0ncXcHG9nYFTUI78sxEb0qoT3U6Ga4hyBkzsJFBX/zvVBIGX3qKldGA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260907.1': - resolution: {integrity: sha512-4aXjFMtD76FgVmDm7vUtl96LWNmFScfrYZ9lA6JpP5+svRfUQOYaJKcYK4g9IVmAxemItJizTTIA8jhEZaw0vg==} + '@cloudflare/workerd-linux-64@1.20260911.1': + resolution: {integrity: sha512-0Y2gy62oxQxWa38qinSPE6zNL5+JmumJtDY9AWW1HB8KHuATxN71o5MGzmVFfB8PwZsiHfUd2Sv7O22krCOrhw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260907.1': - resolution: {integrity: sha512-nex+emDfyOYnQlhfPoCfLYQN6L+Ng0fVg5f3+8Ot4XRZaKJUzpiBFiRkgpoUuEN47U8Pm8BwhhOK2dQwtKwivQ==} + '@cloudflare/workerd-linux-arm64@1.20260911.1': + resolution: {integrity: sha512-kttNPnx1r2lCqFUoMH62z7CqGV+j4QBbw5fdtaz4pzOrzBv0AWkNATt7onFUe+SwP8zhcepMtbm2F4kKzTf6VA==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260907.1': - resolution: {integrity: sha512-/ZHtEmpdSUKZyQCBUSqmGp+I7GwWVR2eUaGE5NzFJIu31HBsoQEakXaDpTrbwXydpBzpI9N0DC83W7qekkGPMQ==} + '@cloudflare/workerd-windows-64@1.20260911.1': + resolution: {integrity: sha512-5iO/YfoBDOgO3CrHdkiiVP8SL3O2jC+c6Ux3d378TSPKLhU5+CgHjtE/ZSodWQrzr4FzFRqdW8S7n5nbyD1MHQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260908.1': - resolution: {integrity: sha512-cILmYEd/YtL+Hlwytc5n+QVV8F+E5doQOtc6QiBtPb51kK+5fkk909db+G8dxeUsMd2ytJgdpAt/lyciUEobcw==} + '@cloudflare/workers-types@5.20260915.1': + resolution: {integrity: sha512-3Lw2wkyVcEDdD0W6rNBmannaAkUsWGsO5TjbyHflWIDlhxtFRDFnxqHoDlg4YiVxinsIcBl1j9jW1kdruUhZpg==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -1296,160 +1296,160 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.2': - resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.2': - resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.2': - resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.1': - resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.1': - resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.1': - resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.1': - resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.1': - resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.3.1': - resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.3.1': - resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.3.1': - resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.3.1': - resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.3.1': - resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.35.2': - resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.35.2': - resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.35.2': - resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.35.2': - resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.35.2': - resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.35.2': - resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.35.2': - resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.35.2': - resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.35.2': - resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.2': - resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.2': - resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.2': - resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.2': - resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -4937,8 +4937,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@5.20260907.0-alpha: - resolution: {integrity: sha512-56F13BeJQuDbTxcoGGN4MGxN8oNNIH3pK48VyJ1JGmjbzE9MMu6S2kxoON3nOBHlLdNjXyzg1Rz3jdHx+JpuZA==} + miniflare@5.20260911.1-alpha: + resolution: {integrity: sha512-7IDj9monoYcCPrS8HfcTt90T3pDwKGvNEAR1Y061KbJhBd5JkStOwOpHMUDFBo9PEbjWVDxPICAnXGNNV2LNfQ==} engines: {node: '>=22.0.0'} minimatch@10.2.6: @@ -5570,9 +5570,14 @@ packages: set-cookie-parser@3.1.2: resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} - sharp@0.35.2: - resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -6261,17 +6266,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260907.1: - resolution: {integrity: sha512-MN/jgZkg2y9ZOOXdN9ecYynRFaqTCRkmpt3jS6LQNPoSx8HxTgcG947SduxJEB38YisB7RJ3Gu/y7fpd7fZ5bA==} + workerd@1.20260911.1: + resolution: {integrity: sha512-vRr8QdBxueQOZJO1hRCI73EZlix87IAyBAcSyI3rA1VB+6oxjw3oaqzYnIV8C4IOPtUgihbdMAgzkb5GM4V7DQ==} engines: {node: '>=16'} hasBin: true - wrangler@4.129.1: - resolution: {integrity: sha512-u1Q8dMAczlmj41K0xk5LkfcHHmbwq5xZRctlyrErVqpcoGsSIW07i74phv3B9xt6mTEO1dX3U5d9WVxR3GFRfA==} + wrangler@4.131.2: + resolution: {integrity: sha512-jmkGE7monbPKyYQr1FPQN+SARVhddqw2fhXOmTKCw4lroqlFGSS6rit/RTvPi/qzNLKrXxkS8DhWXasJnStplg==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260907.1 + '@cloudflare/workers-types': ^5.20260911.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6543,43 +6548,44 @@ snapshots: '@cloudflare/playwright@1.3.6': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260907.1 + workerd: 1.20260911.1 - '@cloudflare/vitest-plugin@1.1.5(@cloudflare/workers-types@5.20260908.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) - wrangler: 4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' + - '@types/node' - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260907.1': + '@cloudflare/workerd-darwin-64@1.20260911.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260907.1': + '@cloudflare/workerd-darwin-arm64@1.20260911.1': optional: true - '@cloudflare/workerd-linux-64@1.20260907.1': + '@cloudflare/workerd-linux-64@1.20260911.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260907.1': + '@cloudflare/workerd-linux-arm64@1.20260911.1': optional: true - '@cloudflare/workerd-windows-64@1.20260907.1': + '@cloudflare/workerd-windows-64@1.20260911.1': optional: true - '@cloudflare/workers-types@5.20260908.1': {} + '@cloudflare/workers-types@5.20260915.1': {} '@colors/colors@1.6.0': {} @@ -6987,108 +6993,108 @@ snapshots: '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.35.2': + '@img/sharp-darwin-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-arm64': 1.3.3 optional: true - '@img/sharp-darwin-x64@0.35.2': + '@img/sharp-darwin-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.3 optional: true - '@img/sharp-freebsd-wasm32@0.35.2': + '@img/sharp-freebsd-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.2 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.1': + '@img/sharp-libvips-darwin-arm64@1.3.3': optional: true - '@img/sharp-libvips-darwin-x64@1.3.1': + '@img/sharp-libvips-darwin-x64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm64@1.3.1': + '@img/sharp-libvips-linux-arm64@1.3.3': optional: true - '@img/sharp-libvips-linux-arm@1.3.1': + '@img/sharp-libvips-linux-arm@1.3.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.1': + '@img/sharp-libvips-linux-ppc64@1.3.3': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.1': + '@img/sharp-libvips-linux-riscv64@1.3.3': optional: true - '@img/sharp-libvips-linux-s390x@1.3.1': + '@img/sharp-libvips-linux-s390x@1.3.3': optional: true - '@img/sharp-libvips-linux-x64@1.3.1': + '@img/sharp-libvips-linux-x64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.1': + '@img/sharp-libvips-linuxmusl-x64@1.3.3': optional: true - '@img/sharp-linux-arm64@0.35.2': + '@img/sharp-linux-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.3 optional: true - '@img/sharp-linux-arm@0.35.2': + '@img/sharp-linux-arm@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.3 optional: true - '@img/sharp-linux-ppc64@0.35.2': + '@img/sharp-linux-ppc64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.35.2': + '@img/sharp-linux-riscv64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.3 optional: true - '@img/sharp-linux-s390x@0.35.2': + '@img/sharp-linux-s390x@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.3 optional: true - '@img/sharp-linux-x64@0.35.2': + '@img/sharp-linux-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.3 optional: true - '@img/sharp-linuxmusl-arm64@0.35.2': + '@img/sharp-linuxmusl-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 optional: true - '@img/sharp-linuxmusl-x64@0.35.2': + '@img/sharp-linuxmusl-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 optional: true - '@img/sharp-wasm32@0.35.2': + '@img/sharp-wasm32@0.35.4': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.2': + '@img/sharp-webcontainers-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.2 + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-win32-arm64@0.35.2': + '@img/sharp-win32-arm64@0.35.4': optional: true - '@img/sharp-win32-ia32@0.35.2': + '@img/sharp-win32-ia32@0.35.4': optional: true - '@img/sharp-win32-x64@0.35.2': + '@img/sharp-win32-x64@0.35.4': optional: true '@inquirer/ansi@1.0.2': {} @@ -10446,15 +10452,16 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.2 + sharp: 0.35.4(@types/node@26.5.1) undici: 7.29.0 - workerd: 1.20260907.1 + workerd: 1.20260911.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: + - '@types/node' - bufferutil - utf-8-validate @@ -11224,37 +11231,38 @@ snapshots: set-cookie-parser@3.1.2: {} - sharp@0.35.2: + sharp@0.35.4(@types/node@26.5.1): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.2 - '@img/sharp-darwin-x64': 0.35.2 - '@img/sharp-freebsd-wasm32': 0.35.2 - '@img/sharp-libvips-darwin-arm64': 1.3.1 - '@img/sharp-libvips-darwin-x64': 1.3.1 - '@img/sharp-libvips-linux-arm': 1.3.1 - '@img/sharp-libvips-linux-arm64': 1.3.1 - '@img/sharp-libvips-linux-ppc64': 1.3.1 - '@img/sharp-libvips-linux-riscv64': 1.3.1 - '@img/sharp-libvips-linux-s390x': 1.3.1 - '@img/sharp-libvips-linux-x64': 1.3.1 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 - '@img/sharp-libvips-linuxmusl-x64': 1.3.1 - '@img/sharp-linux-arm': 0.35.2 - '@img/sharp-linux-arm64': 0.35.2 - '@img/sharp-linux-ppc64': 0.35.2 - '@img/sharp-linux-riscv64': 0.35.2 - '@img/sharp-linux-s390x': 0.35.2 - '@img/sharp-linux-x64': 0.35.2 - '@img/sharp-linuxmusl-arm64': 0.35.2 - '@img/sharp-linuxmusl-x64': 0.35.2 - '@img/sharp-webcontainers-wasm32': 0.35.2 - '@img/sharp-win32-arm64': 0.35.2 - '@img/sharp-win32-ia32': 0.35.2 - '@img/sharp-win32-x64': 0.35.2 + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 26.5.1 shebang-command@2.0.0: dependencies: @@ -11874,28 +11882,29 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260907.1: + workerd@1.20260911.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260907.1 - '@cloudflare/workerd-darwin-arm64': 1.20260907.1 - '@cloudflare/workerd-linux-64': 1.20260907.1 - '@cloudflare/workerd-linux-arm64': 1.20260907.1 - '@cloudflare/workerd-windows-64': 1.20260907.1 + '@cloudflare/workerd-darwin-64': 1.20260911.1 + '@cloudflare/workerd-darwin-arm64': 1.20260911.1 + '@cloudflare/workerd-linux-64': 1.20260911.1 + '@cloudflare/workerd-linux-arm64': 1.20260911.1 + '@cloudflare/workerd-windows-64': 1.20260911.1 - wrangler@4.129.1(@cloudflare/workers-types@5.20260908.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260907.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260907.0-alpha(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260907.1 + workerd: 1.20260911.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260908.1 + '@cloudflare/workers-types': 5.20260915.1 fsevents: 2.3.3 transitivePeerDependencies: + - '@types/node' - bufferutil - utf-8-validate From ebc6b08cbdfc21725b824ad850bf3466a18d1912 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 22:05:30 +0800 Subject: [PATCH 0850/1190] chore(deps): bump the oxc group across 1 directory with 5 updates (#23323) Bumps the oxc group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.149.0` | `0.150.0` | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.82.0` | `1.83.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.67.0` | `0.68.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.82.0` | `1.83.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.82.0` | `1.83.0` | Updates `oxc-parser` from 0.149.0 to 0.150.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.150.0/napi/parser) Updates `@oxlint/plugins` from 1.82.0 to 1.83.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.83.0/npm/oxlint-plugins) Updates `oxfmt` from 0.67.0 to 0.68.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.68.0/npm/oxfmt) Updates `oxlint` from 1.82.0 to 1.83.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.83.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.82.0 to 1.83.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/apps_v1.83.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.83.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.150.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.68.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.83.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.83.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 10 +- pnpm-lock.yaml | 515 +++++++++++++++++++++++++------------------------ 2 files changed, 265 insertions(+), 260 deletions(-) diff --git a/package.json b/package.json index 9825a66c4d5f..d9a745b11417 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "oauth-1.0a": "2.2.6", "ofetch": "1.5.1", "otplib": "13.5.0", - "oxc-parser": "0.149.0", + "oxc-parser": "0.150.0", "p-map": "7.0.8", "pac-proxy-agent": "9.1.0", "patchright": "1.63.0", @@ -155,7 +155,7 @@ "@cloudflare/vitest-plugin": "1.1.9", "@cloudflare/workers-types": "5.20260915.1", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.82.0", + "@oxlint/plugins": "1.83.0", "@stylistic/eslint-plugin": "5.10.0", "@types/crypto-js": "4.2.2", "@types/eslint": "9.6.1", @@ -191,9 +191,9 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxfmt": "0.67.0", - "oxlint": "1.82.0", - "oxlint-plugin-eslint": "1.82.0", + "oxfmt": "0.68.0", + "oxlint": "1.83.0", + "oxlint-plugin-eslint": "1.83.0", "oxlint-tsgolint": "7.0.2001", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e240e6393de5..58b6cacbc9f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,8 +197,8 @@ importers: specifier: 13.5.0 version: 13.5.0 oxc-parser: - specifier: 0.149.0 - version: 0.149.0 + specifier: 0.150.0 + version: 0.150.0 p-map: specifier: 7.0.8 version: 7.0.8 @@ -315,8 +315,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) '@oxlint/plugins': - specifier: 1.82.0 - version: 1.82.0 + specifier: 1.83.0 + version: 1.83.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.10.0(jiti@2.7.0)) @@ -423,14 +423,14 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) oxfmt: - specifier: 0.67.0 - version: 0.67.0 + specifier: 0.68.0 + version: 0.68.0 oxlint: - specifier: 1.82.0 - version: 1.82.0(oxlint-tsgolint@7.0.2001) + specifier: 1.83.0 + version: 1.83.0(oxlint-tsgolint@7.0.2001) oxlint-plugin-eslint: - specifier: 1.82.0 - version: 1.82.0 + specifier: 1.83.0 + version: 1.83.0 oxlint-tsgolint: specifier: 7.0.2001 version: 7.0.2001 @@ -1818,106 +1818,106 @@ packages: '@otplib/uri@13.5.0': resolution: {integrity: sha512-LsL1hqTEgJHY40U2eb/Qp6OR1tKkNGwHQTrqqjIuJj0cawGWkLEmeUFK1MHzWBQ4bgy5IdeB5Et7gIDSAH/CRw==} - '@oxc-parser/binding-android-arm-eabi@0.149.0': - resolution: {integrity: sha512-OQmN5XYQ/GNRkbU0Ksb2QSaHOgKWhFw7Bz6lJvANBdwVkLzTlcHXDLj62XrNPtUf3pDxRBr7OoHJrKhHYOb9iA==} + '@oxc-parser/binding-android-arm-eabi@0.150.0': + resolution: {integrity: sha512-oQef2Zu4Prz1KLKznz3HqZzU9uVoA5PMoDZuuLmqms7hKmKSAPzlaMnLllJq3t+rgKmfJJ2siPrpZfFrW06btw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.149.0': - resolution: {integrity: sha512-M7nHwfHY2Xv5Rm1iygvyOTPXrD4d5WRVw+C8eItLwoipzQBnkUMmwh3jvuGLBio2BGetfIH0F7J3Afb1Qn9dJg==} + '@oxc-parser/binding-android-arm64@0.150.0': + resolution: {integrity: sha512-B6ofpoFiAUwIZ0MJ2IgHPvZK8FAtL4qzSZRwOkAKIfxEobE1mQC8nDdiFZj7pUaJiVUVCsfkMsBJKedzO8rZvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.149.0': - resolution: {integrity: sha512-h6NrhqRAVKbgzfShjHoBgjeJ1vQR2o6iNM6/t8JCSej1XRKJyraV08Zz7ey6SsjPzof4ecp48Upg2vidqoDgtQ==} + '@oxc-parser/binding-darwin-arm64@0.150.0': + resolution: {integrity: sha512-J+9IHKzx/bSz1JetOfD4zKXSK9sOm4/a7+0qomJODcTL6JsRXGeV/ZdkAPSIDydfFmWzYCraMlQEosSZEyBYkQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.149.0': - resolution: {integrity: sha512-+WRRZeFYqVznJEEttiWNcI43Ij2iD0yphmMFkAuKNOQTyXQ+lLCAVW6aVLOD6nPO6FOymW7HtDh5KmnxZPi/2A==} + '@oxc-parser/binding-darwin-x64@0.150.0': + resolution: {integrity: sha512-v6IPfcAcSYrBWXV5Tce1DmxDMXLhEYpIIWiRFPJopgCVscZdLaV4MRQOUxvL3usQlw+yrwYOjBFB9IwBx+jUiQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.149.0': - resolution: {integrity: sha512-kG0YUZj/4hdHBQDtIr2UZ7Nyxy3M5Et+w3A0V0eefRgtQWE4wJrNflBJMJfwFPiwMLPlPL6qJR+5JdeIqhzW/g==} + '@oxc-parser/binding-freebsd-x64@0.150.0': + resolution: {integrity: sha512-AoR/4jD02HET0KO0yNT4nbTE+XJUxiO9jB1X/ycEjUE3WrIJ3IjV/Vf+gskhWLcqqeXfvNnkDtBR7epllAm88A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.149.0': - resolution: {integrity: sha512-gOD5NhD8DG7aJ8CLZ+ve5xoDq0y0J0zKZSfkfMGfk96XLhu21IzSFvjpEvVUGodD7Yyy3Tr9rNdiPEdZjkuRqQ==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.150.0': + resolution: {integrity: sha512-A/hycCFjLUrCmLoL5O/vBp40ohlaXO1Ta3v5hqYZxicYFs129wZmgZJggcW4mYGYbZebQLhup+N8JjeQl8qwyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.149.0': - resolution: {integrity: sha512-UtPYQkciY1NzYRtfKBopYfOjSxPFFVyprs0iH+Z5KZKNv9at/71pGd2KNrlmALhRcNocnxEsc+KuhY3nUlhIxg==} + '@oxc-parser/binding-linux-arm-musleabihf@0.150.0': + resolution: {integrity: sha512-pbqahg1Pkz7J4RKmXm30s/iQu99hv64ayXCu8P4p75HcEH5azOdR4eGqiB6lFGkFR3mMpzaYsSKLkS8oJbjmeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.149.0': - resolution: {integrity: sha512-3jWkwykjJJegMD+5dU9n30mzwvTLi99fkUqHU9Pqdzw3fIVfyn6ZnjOl8l053jogr0BGQPxagTjIKBksp288eQ==} + '@oxc-parser/binding-linux-arm64-gnu@0.150.0': + resolution: {integrity: sha512-HV11aRbBQGwqv8bEo+K/6qr88uB4fEe1mhXncMhlVCrj+WpBSraxrUzHaPVmeQRU6x6x8v/3DuCbbo93rpp+fw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.149.0': - resolution: {integrity: sha512-H7n8zPxnLVI0pkutAxyXJNE/IaCuCJS6ghgv2cl8IDIgXCDZWjG/Bs8rF8BHKQ9gsOpA4m+T4lFaFAdnGuk/cw==} + '@oxc-parser/binding-linux-arm64-musl@0.150.0': + resolution: {integrity: sha512-k6pVkJqALwtEuP4zukVhGRhdIy4+ofChUIUUsAHHyqDZlNsknmel3JjbggrJiWGaes6h/dIcWmEXsKW4SmK9oQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.149.0': - resolution: {integrity: sha512-aByOTCdLYp8FjwAUUmN7YYjAmPJ/9t0I1ACuhAkYun87fajIHbwuZ/opaBk0Pnfxzp2vYyD8hdG8OQWEglP5oA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.150.0': + resolution: {integrity: sha512-tEFg39mw/rHO5n8GK2DR4ZMFfsPQZtnQIPbsIpjoQRomJc/2tRfsCSmCT6gNit+NZGoeJG5aH9ATDH5bf0WnoQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.149.0': - resolution: {integrity: sha512-SAdNs+uxMRBLPM6QuLlOxzE4duWia4EMhqGknsJS2EGnWni7i53gSZhaW3VYPxaP8nk/UsMQxMODIFwBHbRWcQ==} + '@oxc-parser/binding-linux-riscv64-gnu@0.150.0': + resolution: {integrity: sha512-0JO7IFkoek6HqV589Menx3hJySNXi6quRhO4tq2P7kpEHKEXoDATnoPVUpn5B7gDH2KLkdo751N0uIrGJFCTCw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.149.0': - resolution: {integrity: sha512-RhmzD62QNtMjuBAeKJ15gzyNRv06Yz1ZFi6lgTiA5S3ixkrSMPJWIPGyqdlC4XiWxwAW9W4jeQNV4/Om9V27gA==} + '@oxc-parser/binding-linux-riscv64-musl@0.150.0': + resolution: {integrity: sha512-7XPzREnyAS5wHU9aB+49Uaqgoo1gVCklHc3DRlc3fJy2tXD/eAJFN1QFz/crj+Ulup5P1+axNREF+/RGaB/IRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.149.0': - resolution: {integrity: sha512-HqEExoeKhlfUacdhj7KEe1m93B1SzCUkF88c1qLQR5Km6GPSoMHMIjK7qmRXLzjsO+ipSTWVsnYMVG0j2/mxNA==} + '@oxc-parser/binding-linux-s390x-gnu@0.150.0': + resolution: {integrity: sha512-7n7ZxcbRWDFaTdyj8M8p0O9OfzoMKm8O6syBAIgcSutbOALq0Bb9G52Me+XH0anMLZV+NgXc726gqgG2q39vcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.149.0': - resolution: {integrity: sha512-FuAv1J7GoSkmxg5glvzlHA88Ld7dOIg23VdAsVLngPZN/ZjZPtCaBZDby1GPn63Yj+7h47/wbcd7i1p2UJTeNQ==} + '@oxc-parser/binding-linux-x64-gnu@0.150.0': + resolution: {integrity: sha512-Vx0GSA9ZCRTUiczoEQnIIyXkMvtEY8uc6IiwLQtMe5ci2sXPqjrry0Ek5fsPP1k2kCzkML3ow9UOOEgnEDi7Bw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.149.0': - resolution: {integrity: sha512-KDFOVgjMIOiMiRUw+jZlAIEZZFG0WgGk+adcldjc+/qz0/kvobnYJqDqNX0+REJbSpSsM1Knj0zrEGTQ6WTIng==} + '@oxc-parser/binding-linux-x64-musl@0.150.0': + resolution: {integrity: sha512-ii4/9m3viDLssMnfXU7/Pni/3nYplApuGayceX4qsVGaqqQJCx3tHIH9M/HWIeSty5i8LjS21zPYT6lVIkwLxw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.149.0': - resolution: {integrity: sha512-O3O+ZBhGEUhtbDXFWRMTuOCBg+fC2hf1wrtZBrd3VJUtV/MDd1LCwZXIHY2nMCnh9Akgjay965pBok2h5AXN0A==} + '@oxc-parser/binding-openharmony-arm64@0.150.0': + resolution: {integrity: sha512-ZtoxX5rez36ZWkwtiEhjkCPnlTPoQ2I7lIL0IYZ1yjxMYohbvoOjBmUIZzrf9W/HouONq0omIfTeCWEY+R380w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -1926,20 +1926,20 @@ packages: resolution: {integrity: sha512-p2X0okYbIkav6eB5zwwVc6eFRycAO4GbGpBCf9IJCI2fUzwoxysWaMXIyZzrq6Pg4/LsEqFb0Vp6TKovK541vA==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} - '@oxc-parser/binding-win32-arm64-msvc@0.149.0': - resolution: {integrity: sha512-9hMvs3GZESAcbUGU3mqXFWRLgBd8TLeosNYfHusHp/hrBnk5lIP2nJtark3hZ6qT+lqSdS79sH8NoWHj1x9iUw==} + '@oxc-parser/binding-win32-arm64-msvc@0.150.0': + resolution: {integrity: sha512-VqeRb5JX/bKYBrff7TUDvJyKY0914UzuCkuXIGxJS4FUmvMNfqkCbc7Sq90iMz9DlmAtlggwaBYQ9eg3h3ltiw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.149.0': - resolution: {integrity: sha512-8hQaS1Urc1NnlutDfhQ+DwijRALGLY+AmqJSBHV4OmyPZDoqgtkmjTHl2tsTLK6PETe6PbIJLKmXcpJuqkf8Lw==} + '@oxc-parser/binding-win32-ia32-msvc@0.150.0': + resolution: {integrity: sha512-EPqJfeZ4Pgg2BJSsCV8GozxV0FDltRzpVtGVa1r2noJu1iu+opOw3gtBWXwIo9odCT/MdFfyHxdy0/CRqs3OqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.149.0': - resolution: {integrity: sha512-xFqDgKSKdY0m2EtHQL7uMuknuEGnvbtxkWllGle5hmhVe5xn5XZ0Zb9iViAsItFAf0CjSiZ5er3XqqAlr5oARA==} + '@oxc-parser/binding-win32-x64-msvc@0.150.0': + resolution: {integrity: sha512-n5YMzbqwPQqozbkrexi0lNZrUz5cnPHalYpFWe6Dau7AmKIWNo+y24IwzDuZEQtEdUHuKhXmMMih/MPjOI+CMw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1947,6 +1947,9 @@ packages: '@oxc-project/types@0.149.0': resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} + '@oxc-project/types@0.150.0': + resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] @@ -2050,124 +2053,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.67.0': - resolution: {integrity: sha512-2olh3ioEmc4gRzQm7jxyB1b/PFBoFvTq8KdgYySeNpysDtA6DEg2Mvya4/I6flhL7G0eOrE8RD7JCNCIMhE16Q==} + '@oxfmt/binding-android-arm-eabi@0.68.0': + resolution: {integrity: sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.67.0': - resolution: {integrity: sha512-ulfw8EHN1MBq/MFFDXw2/M1VAFu5mRUcnuZ8Hqbv9viAnFzO9t1jKSAsDqKYYDGMlytF/uj6Z5z5n/tHupnKhw==} + '@oxfmt/binding-android-arm64@0.68.0': + resolution: {integrity: sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.67.0': - resolution: {integrity: sha512-MfONZx/O2o9M5v2jDFol556G9+A+P9xCuJ4DZ+qhE+RnaCdoscy6Eu5nq1dbuNxhwdJyZ6kLI7fnG9mwEeOeGg==} + '@oxfmt/binding-darwin-arm64@0.68.0': + resolution: {integrity: sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.67.0': - resolution: {integrity: sha512-CYnIx5LvFVJnyJcCqwH2jxMKjFjqo5678MPjdmNFoSGMhlOvZ/xRZqvhDcolKrXc8fezW3AKh+C4wyoFuWOSSg==} + '@oxfmt/binding-darwin-x64@0.68.0': + resolution: {integrity: sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.67.0': - resolution: {integrity: sha512-7/iF1orvIS9mxhKUqnmtMgm+OrSQ5acPwuvdQrm6ECgqbwPmC+Pw9cdke3sNfVN6pT2hbJ58+jP8BCThl5HXOg==} + '@oxfmt/binding-freebsd-x64@0.68.0': + resolution: {integrity: sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': - resolution: {integrity: sha512-yy+OGys07IZOpOmYPZoObKyUQLkfxeQqeCypk+1jaZd8HGo77hzvU1Jg8X3+W75o+9lszOjBfg0nkGtlwYywXw==} + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + resolution: {integrity: sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.67.0': - resolution: {integrity: sha512-wPIeeigXgJpwNw3wydYRt3U9iN9Y/ejpOZuYL9IA7igxWs7LIQMOkhKxTumRvy6dIv0iXKk3RTw3Vmjg0i+2sg==} + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + resolution: {integrity: sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.67.0': - resolution: {integrity: sha512-0+XNxcdbkTfxdcD4qW6Ci9n+mBNJ8xTBumnxKvKBmRFOdx0Wf8/KiHjCJayooXmYkqRpRVd98Q5egvzx5BLSgQ==} + '@oxfmt/binding-linux-arm64-gnu@0.68.0': + resolution: {integrity: sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.67.0': - resolution: {integrity: sha512-I75LKPJyNOYUzkqAiAMIE31+Ye7xtQXZdoty1IXn4B+bw5Zpmez5wfG19ejGpNnS/BzQ7LFS+7jxuTPb+vHiZw==} + '@oxfmt/binding-linux-arm64-musl@0.68.0': + resolution: {integrity: sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.67.0': - resolution: {integrity: sha512-c2M5iRpe1QMZSRE/UvZoPdXBWb5Ic/ycvOyNiKCqPwQ/OyOKIMiJs02ynlNnjb7ZZJnRXYLmGcohoINOcwDK3w==} + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + resolution: {integrity: sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.67.0': - resolution: {integrity: sha512-dQzzYlV24Udhfm5ECuSdgqRvFJU/CGHzcYYEO3dLM6W6+CHiBFrq9OjIllkdCcPhsoSQ8o223Dja84MOSzed9A==} + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + resolution: {integrity: sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.67.0': - resolution: {integrity: sha512-rFNq1CgX4qMJANOq42LkAs90JE80GpiaEohAV2qn/gT2hGjQTW1zBO5zQBxArI4926pM1OSzo3CN0tBszGBIaA==} + '@oxfmt/binding-linux-riscv64-musl@0.68.0': + resolution: {integrity: sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.67.0': - resolution: {integrity: sha512-Sky6rEdz2o5IGq01lPhS12yEvDdChVEcaYrcLHkveh4Fx0qPjljE/Iul6SX/bRMl6lNc8J7J/mDQdzgBdA++Pg==} + '@oxfmt/binding-linux-s390x-gnu@0.68.0': + resolution: {integrity: sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.67.0': - resolution: {integrity: sha512-vPXmlNORV8AZq2Ocxh07pxwMjfENUWCV/eZArnao0qC3NO/hDeTVkQvee7SJJUbIiF5PZbBa4kYmaXnu7Rk58w==} + '@oxfmt/binding-linux-x64-gnu@0.68.0': + resolution: {integrity: sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.67.0': - resolution: {integrity: sha512-x/WAtFqYtVr3vZ9ni8nr4kn9whSitg8fOljq/pZzBpxopRdY1BMLZCZkrbIbaBcYkm46qGbqVea2FCWmtQ2P9w==} + '@oxfmt/binding-linux-x64-musl@0.68.0': + resolution: {integrity: sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.67.0': - resolution: {integrity: sha512-eRw9Neh4/aA6i+q/R3WU1gGQINhVM0J4fXIm6t27caOamkr/37uAkp1IdBx4zlJH97hmXR63z/q9n5c5dN7MzA==} + '@oxfmt/binding-openharmony-arm64@0.68.0': + resolution: {integrity: sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.67.0': - resolution: {integrity: sha512-YIMvb+sGNYN2uc6+QK2HLPeEKM2vl7QZ5onQzpAJRb6pnf0DwUFP5R8tdS9R0l8hdUil2gu4Uxd0Yxrop0iT4w==} + '@oxfmt/binding-win32-arm64-msvc@0.68.0': + resolution: {integrity: sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.67.0': - resolution: {integrity: sha512-LzmU9MyACPzwNDIK0ItMedHPz735Ug7ELWguxo4/kuy6zWuDoeglOAEFCY8jLg0PzRpFO3hDyLFe2Gu2eFDeGA==} + '@oxfmt/binding-win32-ia32-msvc@0.68.0': + resolution: {integrity: sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.67.0': - resolution: {integrity: sha512-sbQOIDNLUEeVZcAJcSL5VURn7kfjvilPviody4Yl5n8lQCDtUm+C9oHTTwZS/m4d/Z6Vv3jNEiAofH932NPPCg==} + '@oxfmt/binding-win32-x64-msvc@0.68.0': + resolution: {integrity: sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2202,130 +2205,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.82.0': - resolution: {integrity: sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA==} + '@oxlint/binding-android-arm-eabi@1.83.0': + resolution: {integrity: sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.82.0': - resolution: {integrity: sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw==} + '@oxlint/binding-android-arm64@1.83.0': + resolution: {integrity: sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.82.0': - resolution: {integrity: sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg==} + '@oxlint/binding-darwin-arm64@1.83.0': + resolution: {integrity: sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.82.0': - resolution: {integrity: sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ==} + '@oxlint/binding-darwin-x64@1.83.0': + resolution: {integrity: sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.82.0': - resolution: {integrity: sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g==} + '@oxlint/binding-freebsd-x64@1.83.0': + resolution: {integrity: sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.82.0': - resolution: {integrity: sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw==} + '@oxlint/binding-linux-arm-gnueabihf@1.83.0': + resolution: {integrity: sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.82.0': - resolution: {integrity: sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ==} + '@oxlint/binding-linux-arm-musleabihf@1.83.0': + resolution: {integrity: sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.82.0': - resolution: {integrity: sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA==} + '@oxlint/binding-linux-arm64-gnu@1.83.0': + resolution: {integrity: sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.82.0': - resolution: {integrity: sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw==} + '@oxlint/binding-linux-arm64-musl@1.83.0': + resolution: {integrity: sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.82.0': - resolution: {integrity: sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw==} + '@oxlint/binding-linux-ppc64-gnu@1.83.0': + resolution: {integrity: sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.82.0': - resolution: {integrity: sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw==} + '@oxlint/binding-linux-riscv64-gnu@1.83.0': + resolution: {integrity: sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.82.0': - resolution: {integrity: sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg==} + '@oxlint/binding-linux-riscv64-musl@1.83.0': + resolution: {integrity: sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.82.0': - resolution: {integrity: sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog==} + '@oxlint/binding-linux-s390x-gnu@1.83.0': + resolution: {integrity: sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.82.0': - resolution: {integrity: sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg==} + '@oxlint/binding-linux-x64-gnu@1.83.0': + resolution: {integrity: sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.82.0': - resolution: {integrity: sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg==} + '@oxlint/binding-linux-x64-musl@1.83.0': + resolution: {integrity: sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.82.0': - resolution: {integrity: sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ==} + '@oxlint/binding-openharmony-arm64@1.83.0': + resolution: {integrity: sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.82.0': - resolution: {integrity: sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ==} + '@oxlint/binding-win32-arm64-msvc@1.83.0': + resolution: {integrity: sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.82.0': - resolution: {integrity: sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A==} + '@oxlint/binding-win32-ia32-msvc@1.83.0': + resolution: {integrity: sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.82.0': - resolution: {integrity: sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ==} + '@oxlint/binding-win32-x64-msvc@1.83.0': + resolution: {integrity: sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.82.0': - resolution: {integrity: sha512-IDF4UXBNOaCeLoEpVxf5Bg85KOWdeuBEkbo1rGzS2vXcbD+GT1asBSeeTC+5BgW6NFTHVByd6N5giHsFPGtwAA==} + '@oxlint/plugins@1.83.0': + resolution: {integrity: sha512-qD64wr6SS1DL7YbJQ8WWjyUfFfrJsvAy9Vo0sG/PYUQy30w0uemN2h6j4xuvj4noyETDpzcGC20sv+FH7GrN5A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -5133,15 +5136,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.149.0: - resolution: {integrity: sha512-iuirONeHJBp6dFuuZKvkCfFvBiF0uFmojIQooGtazR9+GvcwoayGMtal1+cvDUnEw2k435GBish01HOn5iyVqQ==} + oxc-parser@0.150.0: + resolution: {integrity: sha512-zwajKw1GUa57IOdafIS7/yOObGF2NihwIfZfs5yJTRprMSkiwhWOc/k+n8bwnUPbHr2/XsjfBO8KyTPaW3VpWg==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.67.0: - resolution: {integrity: sha512-vV7sSiPsaO0mSxdoUdayipVDFPzW/UQ+hrezEHa20+Tx1dnMdZLSRHMT0PdS67FFbhd74M1n08asW21aLGeCrA==} + oxfmt@0.68.0: + resolution: {integrity: sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5153,16 +5156,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.82.0: - resolution: {integrity: sha512-u86jGed4TZWbar5rXdxROcU+zaRXhKuzdvNwmmFPuhlhxv8LH5ioyjeuE/QgSUjzjFVPU0q5UokWHnkknvnw3w==} + oxlint-plugin-eslint@1.83.0: + resolution: {integrity: sha512-9v2O7rkCkT2MzaAoviunkZ+O/BjhjhHXZKg8P0iAPfLYfziQwTHTJWqlY/uGOPAzjdC+VffHcRliMgJkxIttgg==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@7.0.2001: resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} hasBin: true - oxlint@1.82.0: - resolution: {integrity: sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ==} + oxlint@1.83.0: + resolution: {integrity: sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7467,52 +7470,52 @@ snapshots: dependencies: '@otplib/core': 13.5.0 - '@oxc-parser/binding-android-arm-eabi@0.149.0': + '@oxc-parser/binding-android-arm-eabi@0.150.0': optional: true - '@oxc-parser/binding-android-arm64@0.149.0': + '@oxc-parser/binding-android-arm64@0.150.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.149.0': + '@oxc-parser/binding-darwin-arm64@0.150.0': optional: true - '@oxc-parser/binding-darwin-x64@0.149.0': + '@oxc-parser/binding-darwin-x64@0.150.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.149.0': + '@oxc-parser/binding-freebsd-x64@0.150.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.149.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.150.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.149.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.150.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.149.0': + '@oxc-parser/binding-linux-arm64-gnu@0.150.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.149.0': + '@oxc-parser/binding-linux-arm64-musl@0.150.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.149.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.150.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.149.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.150.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.149.0': + '@oxc-parser/binding-linux-riscv64-musl@0.150.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.149.0': + '@oxc-parser/binding-linux-s390x-gnu@0.150.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.149.0': + '@oxc-parser/binding-linux-x64-gnu@0.150.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.149.0': + '@oxc-parser/binding-linux-x64-musl@0.150.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.149.0': + '@oxc-parser/binding-openharmony-arm64@0.150.0': optional: true '@oxc-parser/binding-wasm32-wasip1@0.0.1': @@ -7521,17 +7524,19 @@ snapshots: '@emnapi/runtime': 2.0.0-alpha.3 '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) - '@oxc-parser/binding-win32-arm64-msvc@0.149.0': + '@oxc-parser/binding-win32-arm64-msvc@0.150.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.149.0': + '@oxc-parser/binding-win32-ia32-msvc@0.150.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.149.0': + '@oxc-parser/binding-win32-x64-msvc@0.150.0': optional: true '@oxc-project/types@0.149.0': {} + '@oxc-project/types@0.150.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7593,61 +7598,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.67.0': + '@oxfmt/binding-android-arm-eabi@0.68.0': optional: true - '@oxfmt/binding-android-arm64@0.67.0': + '@oxfmt/binding-android-arm64@0.68.0': optional: true - '@oxfmt/binding-darwin-arm64@0.67.0': + '@oxfmt/binding-darwin-arm64@0.68.0': optional: true - '@oxfmt/binding-darwin-x64@0.67.0': + '@oxfmt/binding-darwin-x64@0.68.0': optional: true - '@oxfmt/binding-freebsd-x64@0.67.0': + '@oxfmt/binding-freebsd-x64@0.68.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.67.0': + '@oxfmt/binding-linux-arm64-gnu@0.68.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.67.0': + '@oxfmt/binding-linux-arm64-musl@0.68.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.67.0': + '@oxfmt/binding-linux-riscv64-musl@0.68.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.67.0': + '@oxfmt/binding-linux-s390x-gnu@0.68.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.67.0': + '@oxfmt/binding-linux-x64-gnu@0.68.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.67.0': + '@oxfmt/binding-linux-x64-musl@0.68.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.67.0': + '@oxfmt/binding-openharmony-arm64@0.68.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.67.0': + '@oxfmt/binding-win32-arm64-msvc@0.68.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.67.0': + '@oxfmt/binding-win32-ia32-msvc@0.68.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.67.0': + '@oxfmt/binding-win32-x64-msvc@0.68.0': optional: true '@oxlint-tsgolint/darwin-arm64@7.0.2001': @@ -7668,64 +7673,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2001': optional: true - '@oxlint/binding-android-arm-eabi@1.82.0': + '@oxlint/binding-android-arm-eabi@1.83.0': optional: true - '@oxlint/binding-android-arm64@1.82.0': + '@oxlint/binding-android-arm64@1.83.0': optional: true - '@oxlint/binding-darwin-arm64@1.82.0': + '@oxlint/binding-darwin-arm64@1.83.0': optional: true - '@oxlint/binding-darwin-x64@1.82.0': + '@oxlint/binding-darwin-x64@1.83.0': optional: true - '@oxlint/binding-freebsd-x64@1.82.0': + '@oxlint/binding-freebsd-x64@1.83.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + '@oxlint/binding-linux-arm-gnueabihf@1.83.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.82.0': + '@oxlint/binding-linux-arm-musleabihf@1.83.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.82.0': + '@oxlint/binding-linux-arm64-gnu@1.83.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.82.0': + '@oxlint/binding-linux-arm64-musl@1.83.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.82.0': + '@oxlint/binding-linux-ppc64-gnu@1.83.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.82.0': + '@oxlint/binding-linux-riscv64-gnu@1.83.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.82.0': + '@oxlint/binding-linux-riscv64-musl@1.83.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.82.0': + '@oxlint/binding-linux-s390x-gnu@1.83.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.82.0': + '@oxlint/binding-linux-x64-gnu@1.83.0': optional: true - '@oxlint/binding-linux-x64-musl@1.82.0': + '@oxlint/binding-linux-x64-musl@1.83.0': optional: true - '@oxlint/binding-openharmony-arm64@1.82.0': + '@oxlint/binding-openharmony-arm64@1.83.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.82.0': + '@oxlint/binding-win32-arm64-msvc@1.83.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.82.0': + '@oxlint/binding-win32-ia32-msvc@1.83.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.82.0': + '@oxlint/binding-win32-x64-msvc@1.83.0': optional: true - '@oxlint/plugins@1.82.0': {} + '@oxlint/plugins@1.83.0': {} '@pinojs/redact@0.4.0': {} @@ -10657,29 +10662,29 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.149.0: + oxc-parser@0.150.0: dependencies: - '@oxc-project/types': 0.149.0 + '@oxc-project/types': 0.150.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.149.0 - '@oxc-parser/binding-android-arm64': 0.149.0 - '@oxc-parser/binding-darwin-arm64': 0.149.0 - '@oxc-parser/binding-darwin-x64': 0.149.0 - '@oxc-parser/binding-freebsd-x64': 0.149.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.149.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.149.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.149.0 - '@oxc-parser/binding-linux-arm64-musl': 0.149.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.149.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.149.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.149.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.149.0 - '@oxc-parser/binding-linux-x64-gnu': 0.149.0 - '@oxc-parser/binding-linux-x64-musl': 0.149.0 - '@oxc-parser/binding-openharmony-arm64': 0.149.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.149.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.149.0 - '@oxc-parser/binding-win32-x64-msvc': 0.149.0 + '@oxc-parser/binding-android-arm-eabi': 0.150.0 + '@oxc-parser/binding-android-arm64': 0.150.0 + '@oxc-parser/binding-darwin-arm64': 0.150.0 + '@oxc-parser/binding-darwin-x64': 0.150.0 + '@oxc-parser/binding-freebsd-x64': 0.150.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.150.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.150.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.150.0 + '@oxc-parser/binding-linux-arm64-musl': 0.150.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.150.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.150.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.150.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.150.0 + '@oxc-parser/binding-linux-x64-gnu': 0.150.0 + '@oxc-parser/binding-linux-x64-musl': 0.150.0 + '@oxc-parser/binding-openharmony-arm64': 0.150.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.150.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.150.0 + '@oxc-parser/binding-win32-x64-msvc': 0.150.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10703,31 +10708,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.67.0: + oxfmt@0.68.0: dependencies: tinypool: 2.1.2 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.67.0 - '@oxfmt/binding-android-arm64': 0.67.0 - '@oxfmt/binding-darwin-arm64': 0.67.0 - '@oxfmt/binding-darwin-x64': 0.67.0 - '@oxfmt/binding-freebsd-x64': 0.67.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.67.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.67.0 - '@oxfmt/binding-linux-arm64-gnu': 0.67.0 - '@oxfmt/binding-linux-arm64-musl': 0.67.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.67.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.67.0 - '@oxfmt/binding-linux-riscv64-musl': 0.67.0 - '@oxfmt/binding-linux-s390x-gnu': 0.67.0 - '@oxfmt/binding-linux-x64-gnu': 0.67.0 - '@oxfmt/binding-linux-x64-musl': 0.67.0 - '@oxfmt/binding-openharmony-arm64': 0.67.0 - '@oxfmt/binding-win32-arm64-msvc': 0.67.0 - '@oxfmt/binding-win32-ia32-msvc': 0.67.0 - '@oxfmt/binding-win32-x64-msvc': 0.67.0 - - oxlint-plugin-eslint@1.82.0: {} + '@oxfmt/binding-android-arm-eabi': 0.68.0 + '@oxfmt/binding-android-arm64': 0.68.0 + '@oxfmt/binding-darwin-arm64': 0.68.0 + '@oxfmt/binding-darwin-x64': 0.68.0 + '@oxfmt/binding-freebsd-x64': 0.68.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.68.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.68.0 + '@oxfmt/binding-linux-arm64-gnu': 0.68.0 + '@oxfmt/binding-linux-arm64-musl': 0.68.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-musl': 0.68.0 + '@oxfmt/binding-linux-s390x-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-musl': 0.68.0 + '@oxfmt/binding-openharmony-arm64': 0.68.0 + '@oxfmt/binding-win32-arm64-msvc': 0.68.0 + '@oxfmt/binding-win32-ia32-msvc': 0.68.0 + '@oxfmt/binding-win32-x64-msvc': 0.68.0 + + oxlint-plugin-eslint@1.83.0: {} oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -10738,27 +10743,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.82.0(oxlint-tsgolint@7.0.2001): + oxlint@1.83.0(oxlint-tsgolint@7.0.2001): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.82.0 - '@oxlint/binding-android-arm64': 1.82.0 - '@oxlint/binding-darwin-arm64': 1.82.0 - '@oxlint/binding-darwin-x64': 1.82.0 - '@oxlint/binding-freebsd-x64': 1.82.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.82.0 - '@oxlint/binding-linux-arm-musleabihf': 1.82.0 - '@oxlint/binding-linux-arm64-gnu': 1.82.0 - '@oxlint/binding-linux-arm64-musl': 1.82.0 - '@oxlint/binding-linux-ppc64-gnu': 1.82.0 - '@oxlint/binding-linux-riscv64-gnu': 1.82.0 - '@oxlint/binding-linux-riscv64-musl': 1.82.0 - '@oxlint/binding-linux-s390x-gnu': 1.82.0 - '@oxlint/binding-linux-x64-gnu': 1.82.0 - '@oxlint/binding-linux-x64-musl': 1.82.0 - '@oxlint/binding-openharmony-arm64': 1.82.0 - '@oxlint/binding-win32-arm64-msvc': 1.82.0 - '@oxlint/binding-win32-ia32-msvc': 1.82.0 - '@oxlint/binding-win32-x64-msvc': 1.82.0 + '@oxlint/binding-android-arm-eabi': 1.83.0 + '@oxlint/binding-android-arm64': 1.83.0 + '@oxlint/binding-darwin-arm64': 1.83.0 + '@oxlint/binding-darwin-x64': 1.83.0 + '@oxlint/binding-freebsd-x64': 1.83.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.83.0 + '@oxlint/binding-linux-arm-musleabihf': 1.83.0 + '@oxlint/binding-linux-arm64-gnu': 1.83.0 + '@oxlint/binding-linux-arm64-musl': 1.83.0 + '@oxlint/binding-linux-ppc64-gnu': 1.83.0 + '@oxlint/binding-linux-riscv64-gnu': 1.83.0 + '@oxlint/binding-linux-riscv64-musl': 1.83.0 + '@oxlint/binding-linux-s390x-gnu': 1.83.0 + '@oxlint/binding-linux-x64-gnu': 1.83.0 + '@oxlint/binding-linux-x64-musl': 1.83.0 + '@oxlint/binding-openharmony-arm64': 1.83.0 + '@oxlint/binding-win32-arm64-msvc': 1.83.0 + '@oxlint/binding-win32-ia32-msvc': 1.83.0 + '@oxlint/binding-win32-x64-msvc': 1.83.0 oxlint-tsgolint: 7.0.2001 p-cancelable@4.0.1: {} From 1f4ea5d1708d9cd0d1d6e36e808fa9b77fd48094 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:42:51 +0900 Subject: [PATCH 0851/1190] =?UTF-8?q?fix(route/iri-search):=20write=20?= =?UTF-8?q?=E7=A4=BC=E9=87=91=20into=20key=5Fmoney=5Fmonths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail parser already read the 礼金 cell and kept it in `raw.key_money`, but mergeDetail never mapped it onto the normalised field, so `key_money_months` was always null even when the site published a figure. Parse it with parseMonths, the same helper temposmart, temponw and tenant-shop use for 礼金, so 「1ヶ月」 becomes 1, 「なし」 becomes 0 and 「ご相談」 stays null. --- lib/routes/iri-search/estate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/routes/iri-search/estate.ts b/lib/routes/iri-search/estate.ts index 0059d0317fd9..e6b500368987 100644 --- a/lib/routes/iri-search/estate.ts +++ b/lib/routes/iri-search/estate.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import pMap from 'p-map'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseMonths, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -172,6 +172,7 @@ const parseDetail = (html: string): DetailFields => { const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ ...base, deposit_jpy: parseJpy(d.deposit), + key_money_months: parseMonths(d.key_money), fixtures_transfer_jpy: parseJpy(d.fixtures), listed_at: parseYmd(d.listed_at), raw: { ...base.raw, listed_at: d.listed_at, deposit: d.deposit, key_money: d.key_money, fixtures: d.fixtures, status: d.status, note: d.note }, From 41ee55f3b48424746614f57864f677a6ce8315e7 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:43:16 +0900 Subject: [PATCH 0852/1190] =?UTF-8?q?fix(route/sonomama):=20write=20?= =?UTF-8?q?=E7=A4=BC=E9=87=91=20into=20key=5Fmoney=5Fmonths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail parser already read the 礼金 cell and kept it in `raw.key_money`, but mergeDetail never mapped it onto the normalised field, so `key_money_months` was always null even when the site published a figure. Parse it with parseMonths, the helper the other listing routes use for 礼金, so 「1ヶ月」 becomes 1, 「なし」 becomes 0 and 「ご相談」 stays null. --- lib/routes/sonomama/property.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/routes/sonomama/property.ts b/lib/routes/sonomama/property.ts index d27c0348b8ae..4e27a84eb340 100644 --- a/lib/routes/sonomama/property.ts +++ b/lib/routes/sonomama/property.ts @@ -148,6 +148,7 @@ const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { area_m2: area_m2 ?? base.area_m2, line: clean(d.access?.split('(最寄駅)', 1)[0]?.replace('(沿線)', '')), walk_min: parseWalkMin(d.access), + key_money_months: parseMonths(d.key_money), business_limit: d.business_limit, address_hint: d.address ?? base.address_hint, ward: parseWard(d.address) ?? base.ward, From 27f535559ba7f770f67d7306aff01b297b9acc84 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:44:38 +0900 Subject: [PATCH 0853/1190] =?UTF-8?q?fix(route/inuki-ichiba):=20keep=20the?= =?UTF-8?q?=20=E4=B8=81=E7=9B=AE=20in=20address=5Fhint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card title ends with the full address ('浅草橋駅 | 徒歩6分 | 台東区浅草橋5丁目'), but address_hint was built from the エリア links, which carry only 区 + 町 and drop the 丁目. Every item therefore lost the finest address component the card already showed. Prefer the title's trailing segment, and only when it really is an address — it must name a 区/市, checked with parseWard — otherwise fall back to the エリア links as before. On a live 東京都 page this takes address_hint from 0/20 items carrying a 丁目 to 19/20, the remaining one being a listing whose address has none. --- lib/routes/inuki-ichiba/rent.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/routes/inuki-ichiba/rent.ts b/lib/routes/inuki-ichiba/rent.ts index 7423773ed9a0..9e7f05f15a73 100644 --- a/lib/routes/inuki-ichiba/rent.ts +++ b/lib/routes/inuki-ichiba/rent.ts @@ -69,6 +69,9 @@ const parseList = (html: string): ListCard[] => { const [line, station] = links('駅'); const [ward, town] = links('エリア'); + // The title ends with the full address ('… | 台東区浅草橋5丁目'); the エリア links carry only 区 + 町 and drop the 丁目. + const titleAddress = clean(title.split('|').pop()); + const titleWard = parseWard(titleAddress); const [floorText, areaText] = (text('階数/面積') ?? '').split(/\s*\/\s*/, 2); const typeText = text('物件タイプ/現業態'); const [typeKind, typeBusiness] = (typeText ?? '').split(/\s*\/\s*/, 2); @@ -106,8 +109,9 @@ const parseList = (html: string): ListCard[] => { heavy_food_ok: null, business_limit: null, listed_at: null, - ward: ward ?? parseWard(title.split('|').pop()?.trim() ?? null), - address_hint: ward && town ? `${ward}${town}` : (ward ?? null), + ward: ward ?? titleWard, + // Prefer the title's address, but only when it really is one (it must name a 区/市) — otherwise fall back to the エリア links. + address_hint: (titleWard === null ? null : titleAddress) ?? (ward && town ? `${ward}${town}` : (ward ?? null)), tags, raw, }; From 10080880b2804835bda14c9e727b87219ce90958 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:46:49 +0900 Subject: [PATCH 0854/1190] =?UTF-8?q?feat(route/temposmart):=20add=20an=20?= =?UTF-8?q?optional=20=E5=B8=82=E5=8C=BA=E7=94=BA=E6=9D=91=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route only reached prefecture level, so following a single ward meant filtering a whole prefecture's first page client-side and usually missing most of that ward's new listings. Add an optional `:district?` segment carrying the 5-digit JIS X 0402 市区町村 code the site uses (`/estates/pref/13/district/13104` = 新宿区). The segment is optional, so existing /temposmart/estates/tokyo URLs are unchanged. A code whose first two digits are not the prefecture's is rejected: the site answers such a URL with that prefecture's listings instead of an error, which would look like a working ward feed while silently returning the wrong area. The feed title takes the area name from the page's own h1 rather than a hardcoded table of ~1,700 municipalities. --- lib/routes/temposmart/estates.ts | 35 +++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/routes/temposmart/estates.ts b/lib/routes/temposmart/estates.ts index b10587792c5e..ef3949b4f000 100644 --- a/lib/routes/temposmart/estates.ts +++ b/lib/routes/temposmart/estates.ts @@ -33,6 +33,13 @@ interface ListCard { extra: ListingExtra; } +/** The list page's h1 reads '新宿区の居抜き物件…' / '東京都の居抜き物件…', so the area name is whatever precedes 「の居抜き物件」. */ +const areaLabel = (html: string, fallback: string): string => { + const h1 = clean(load(html)('h1').first().text()); + const name = h1?.split('の居抜き物件', 1)[0]; + return name !== undefined && name !== h1 ? name : fallback; +}; + interface DetailFields { listed_at: string | null; updated_at: string | null; @@ -214,9 +221,22 @@ export const handler = async (ctx): Promise => { throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); } const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, PAGE_SIZE); - const listUrl = `${HOST}/estates/pref/${prefecture.code}?sort=new`; - const cards = parseList(await ofetch(listUrl)).slice(0, limit); + // 市区町村 codes are the prefecture's two digits plus three more (新宿区 = 13104). A code from another + // prefecture would silently return that prefecture's listings, so the pair is checked rather than trusted. + const district: string | undefined = ctx.req.param('district'); + if (district !== undefined) { + if (!/^\d{5}$/.test(district)) { + throw new Error(`Invalid district "${district}", expected a 5-digit JIS X 0402 市区町村 code such as 13104`); + } + if (!district.startsWith(prefecture.code)) { + throw new Error(`District "${district}" does not belong to ${prefecture.label} (${prefecture.code})`); + } + } + const listUrl = district === undefined ? `${HOST}/estates/pref/${prefecture.code}?sort=new` : `${HOST}/estates/pref/${prefecture.code}/district/${district}?sort=new`; + + const html: string = await ofetch(listUrl); + const cards = parseList(html).slice(0, limit); const items = await pMap( cards, @@ -236,7 +256,7 @@ export const handler = async (ctx): Promise => { ); return { - title: `テンポスマート 新着物件 (${prefecture.label})`, + title: `テンポスマート 新着物件 (${areaLabel(html, prefecture.label)})`, link: listUrl, language: 'ja', item: items, @@ -244,7 +264,7 @@ export const handler = async (ctx): Promise => { }; export const route: Route = { - path: '/estates/:pref?', + path: '/estates/:pref?/:district?', name: '新着物件', url: 'www.temposmart.jp', maintainers: ['pseudoyu'], @@ -256,8 +276,13 @@ export const route: Route = { default: 'tokyo', options: PREFECTURES.map((p) => ({ value: p.slug, label: `${p.label} (${p.code})` })), }, + district: { + description: 'Optional 市区町村, as a 5-digit JIS X 0402 code (新宿区 `13104`, 港区 `13103`, 横浜市中区 `14104`). Must belong to `pref`; omit for the whole prefecture.', + }, }, - description: `New listings on テンポスマート for one prefecture, sorted by 新着順 (first page, 50 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,保証金,礼金,造作譲渡料,現況,業種制限,登録日,…) parsed from the list and detail pages; unknown values are \`null\`. + description: `New listings on テンポスマート for one prefecture — or one 市区町村 when \`district\` is given — sorted by 新着順 (first page, 50 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,保証金,礼金,造作譲渡料,現況,業種制限,登録日,…) parsed from the list and detail pages; unknown values are \`null\`. + +\`district\` is a 5-digit JIS X 0402 市区町村 code whose first two digits are the prefecture — \`/temposmart/estates/tokyo/13104\` is 新宿区. A code from another prefecture is rejected rather than silently returning that prefecture's listings. | Query | Description | Default | | ------- | ---------------------------------------------------------------------------- | ------- | From 03b7c74c698aaaafa5936bbf5daeeb7843b7b3f4 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:48:59 +0900 Subject: [PATCH 0855/1190] =?UTF-8?q?feat(route/inshokuten):=20add=20an=20?= =?UTF-8?q?optional=20=E5=8C=BA=E5=B8=82=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route only reached area level (東京23区, 神奈川, …), so following a single ward meant filtering a 23-ward page client-side and missing most of that ward's new listings. Add an optional `:region?` segment carrying 飲食店.COM's own numeric 区市 id — not a JIS code — which the site appends as `/region-{n}` under its `local-*` sub-areas. The segment is optional, so existing /inshokuten/bukken/23ward URLs are unchanged. A region on an area without a 区市 breakdown is rejected up front, and the site itself 404s an unknown id, so a wrong value fails loudly rather than quietly returning the parent area's listings. The feed title takes the area name from the page's own h1, so no id-to-name table has to be kept in sync. --- lib/routes/inshokuten/bukken.ts | 40 ++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/routes/inshokuten/bukken.ts b/lib/routes/inshokuten/bukken.ts index e68bead03be3..3fe405f283c8 100644 --- a/lib/routes/inshokuten/bukken.ts +++ b/lib/routes/inshokuten/bukken.ts @@ -22,6 +22,13 @@ const AREAS = [ { slug: 'kyushu', label: '九州', path: 'kyushu/bukkens/list' }, ]; +/** The list h1 reads '【9月最新】新宿区の店舗物件…', so the area name is what precedes 「の店舗物件」 once the 【…】 badge is dropped. */ +const areaLabel = (html: string, fallback: string): string => { + const h1 = clean(load(html)('h1').first().text())?.replace(/^【[^】]*】/, ''); + const name = h1?.split('の店舗物件', 1)[0]; + return name !== undefined && name !== h1 ? name : fallback; +}; + /** * List page cards (`a.bukkenItem`, 20 per page, server-rendered): * title .bukkenItem__title, id input.js-bukkenKeyId, rows table.bukkenItem__detailTable th → td: @@ -120,18 +127,35 @@ export const handler = async (ctx): Promise => { if (!area) { throw new Error(`Unknown area "${slug}", expected one of ${AREAS.map((a) => a.slug).join(', ')}`); } - const listUrl = `${HOST}/bukken/${area.path}/?mode=latest`; + // 区市 ids are the site's own numbering, not JIS codes, and exist only under the `local-*` sub-areas. + // The site 404s an unknown id or a region on a region-less area, so a wrong value fails loudly rather + // than quietly returning the parent area's listings. + const region: string | undefined = ctx.req.param('region'); + if (region !== undefined) { + if (!/^\d+$/.test(region)) { + throw new Error(`Invalid region "${region}", expected 飲食店.COM's numeric 区市 id such as 7 (新宿区)`); + } + if (!area.path.includes('local-')) { + throw new Error( + `Area "${slug}" has no 区市 breakdown; region is only valid for ${AREAS.filter((a) => a.path.includes('local-')) + .map((a) => a.slug) + .join(', ')}` + ); + } + } + const listUrl = `${HOST}/bukken/${area.path}${region === undefined ? '' : `/region-${region}`}/?mode=latest`; + const html: string = await ofetch(listUrl); return { - title: `飲食店.COM 新着物件 (${area.label})`, + title: `飲食店.COM 新着物件 (${areaLabel(html, area.label)})`, link: listUrl, language: 'ja', - item: parseList(await ofetch(listUrl)), + item: parseList(html), }; }; export const route: Route = { - path: '/bukken/:area?', + path: '/bukken/:area?/:region?', name: '新着物件', url: 'www.inshokuten.com', maintainers: ['pseudoyu'], @@ -143,8 +167,14 @@ export const route: Route = { default: 'kanto', options: AREAS.map((a) => ({ value: a.slug, label: a.label })), }, + region: { + description: + "Optional 区市, as 飲食店.COM's own numeric id — **not** a JIS code (新宿区 `7`, 港区 `4`, 横浜市中区 `63`). Only valid for the `local-*` sub-areas (`23ward`, `23ward_out`, `yokohama_kawasaki`, `chiba`, `saitama`); omit for the whole area.", + }, }, - description: `New restaurant-property listings on 飲食店.COM sorted by 登録日 (first page, 20 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,造作譲渡料,現況,前業態,出店可能業態,登録日,…); unknown values are \`null\`. 保証金 and 礼金 are members-only on the site and therefore always \`null\`.`, + description: `New restaurant-property listings on 飲食店.COM sorted by 登録日 (first page, 20 listings) — for a whole area, or for one 区市 when \`region\` is given. Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,造作譲渡料,現況,前業態,出店可能業態,登録日,…); unknown values are \`null\`. 保証金 and 礼金 are members-only on the site and therefore always \`null\`. + +\`region\` is 飲食店.COM's own numeric 区市 id, **not** a JIS code, and applies only to the \`local-*\` sub-areas — \`/inshokuten/bukken/23ward/7\` is 新宿区 and \`/inshokuten/bukken/yokohama_kawasaki/63\` is 横浜市中区。東京 23 区 runs 1–23 and 横浜・川崎 runs 51–72; the site answers an unknown id with a 404, so a wrong value fails loudly instead of silently returning the parent area.`, categories: ['other'], features: { requireConfig: false, From 7fc378fbc43b0250d1eb0d809e3a49122d7286b3 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 03:54:55 +0900 Subject: [PATCH 0856/1190] =?UTF-8?q?feat(route/i-tenpo):=20add=20?= =?UTF-8?q?=E5=B1=85=E6=8A=9C=E3=81=8D=E5=BA=97=E8=88=97.com=20listings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 居抜き店舗.com publishes 居抜き物件 down to 市区町村 and 業態, which the existing listing routes do not reach, and its robots.txt allows the list pages (only /ichiran/ and two factsheet paths are disallowed; the Scrapy and Bytespider blocks are UA-specific). Its 利用規約 第7条 prohibits 過度な負荷 and 不正アクセス but has no 転載, 複製 or 営利利用 clause, so a cached low-rate feed is compatible with it. `_extra` follows the shared ListingExtra shape so the route drops in beside the other listing sources. 敷金, 礼金 and the coordinates are members-only and stay null, the 丁目 appears only on the detail page so address_hint stops at the 町, and the cards carry no 登録日 so items have no pubDate; no detail page is fetched. 引渡状態 combines 営業状況 and 引渡形態, and only the latter maps to `condition` — 現状渡し is neither 居抜き nor スケルトン and yields null rather than being forced into one. The full string stays in raw.handover, where the 閉店済 signal lives. 造作なし / 造作無償 become 0 because they really are zero, while 確認中 stays null. An unknown 市区町村 slug is answered with the whole prefecture at HTTP 200 rather than a 404, so the route compares the area the page actually resolved and fails instead of serving prefecture-wide listings as if they were one ward's. --- lib/routes/i-tenpo/namespace.ts | 12 +++ lib/routes/i-tenpo/property.ts | 185 ++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 lib/routes/i-tenpo/namespace.ts create mode 100644 lib/routes/i-tenpo/property.ts diff --git a/lib/routes/i-tenpo/namespace.ts b/lib/routes/i-tenpo/namespace.ts new file mode 100644 index 000000000000..6d922c00627c --- /dev/null +++ b/lib/routes/i-tenpo/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Inukitenpo', + url: 'www.i-tenpo.com', + description: '居抜き店舗.com — 飲食店向け居抜き物件・貸店舗情報サイト', + lang: 'ja', + + ja: { + name: '居抜き店舗.com', + }, +}; diff --git a/lib/routes/i-tenpo/property.ts b/lib/routes/i-tenpo/property.ts new file mode 100644 index 000000000000..f69707090d81 --- /dev/null +++ b/lib/routes/i-tenpo/property.ts @@ -0,0 +1,185 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.i-tenpo.com'; +const SLUG = /^[a-z\d-]+$/; +/** 所在地 and the fee rows end with this members-only placeholder. */ +const LOGIN_NOTE = '詳細はログイン後に表示'; + +/** + * List page cards (`li.c-propertyList__item`, 20 per page, server-rendered): + * id input.js-propertyCheck@value (detail page `/t{id}`), title/link .c-propertyList__item__headline__ttl__txt a, + * image .c-propertyList__item__body__pic img@src, badges .c-badgeNew / …__headline__ttl__label__vr, + * and seven 見出し/内容 pairs (`…__table__list__ttl` → `…__table__list__body`): + * 最寄駅 '山手線 高田馬場駅 徒歩 2 分', 賃料 '330,000 円(税抜)', 造作価格 '2,000,000円(税抜)' | '造作なし', + * 所在地 '東京都新宿区 高田馬場 詳細はログイン後に表示', 階層 / 面積 '1階 / 33.56㎡(10.15坪)', + * 前業態 | 現業態 'バー(BAR)' (前 when the shop has closed, 現 while it still trades), + * 引渡状態 '閉店済/居抜き' | '確認中/スケルトン'. + * 敷金・礼金 and the coordinates are members-only, so those fields stay null; the 丁目 lives only on the + * detail page's title, and no detail page is fetched. + */ +const parseList = (html: string): DataItem[] => { + const $ = load(html); + return $('li.c-propertyList__item') + .toArray() + .map((el): DataItem | null => { + const $el = $(el); + const a = $el.find('.c-propertyList__item__headline__ttl__txt a').first(); + const href = a.attr('href'); + const title = clean(a.text()); + const id = $el.find('input.js-propertyCheck').attr('value'); + if (!href || !title || !id) { + return null; + } + + const labels = $el.find('.c-propertyList__item__body__table__list__ttl').toArray(); + const values = $el.find('.c-propertyList__item__body__table__list__body').toArray(); + const rows = new Map(); + for (const [i, label] of labels.entries()) { + const key = clean($(label).text()); + if (key !== null) { + rows.set(key, clean($(values[i]).text())?.replace(LOGIN_NOTE, '').trim() || null); + } + } + const row = (...keys: string[]): string | null => keys.map((k) => rows.get(k) ?? null).find((v) => v !== null) ?? null; + + const raw: ListingExtra['raw'] = { + station: row('最寄駅'), + rent: row('賃料'), + fixtures: row('造作価格'), + address: row('所在地'), + floor_area: row('階層 / 面積'), + business: row('前業態', '現業態'), + handover: row('引渡状態'), + }; + + // 最寄駅 links are 路線 then 駅 ('山手線' → '/yamanote-line/', '高田馬場駅' → '/tokyo/takadanobaba-st/'). + const stationLinks = $(values[labels.findIndex((l) => clean($(l).text()) === '最寄駅')]) + .find('a') + .toArray() + .map((x) => clean($(x).text())); + const [floorText, areaText] = (raw.floor_area ?? '').split('/', 2); + const { tsubo, area_m2 } = parseArea(clean(areaText)); + const rentJpy = parseJpy(raw.rent); + const tags = [$el.find('.c-badgeNew').length > 0 ? 'NEW' : null, $el.find('.c-propertyList__item__headline__ttl__label__vr').length > 0 ? 'VR' : null].filter((t): t is string => t !== null); + + const extra: ListingExtra = { + source: 'i-tenpo', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(floorText)?.replaceAll('地下', 'B') ?? null), + station: stationLinks[1]?.replace(/駅$/, '') ?? null, + line: stationLinks[0] ?? null, + walk_min: parseWalkMin(raw.station?.replaceAll(' ', '') ?? null), + // 敷金 / 礼金 are shown only to signed-in users. + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: parseJpy(raw.fixtures), + condition: parseCondition(raw.handover, title), + prev_business: raw.business, + heavy_food_ok: null, + business_limit: null, + // The card shows no 登録日; 'NEW' is the only recency signal. + listed_at: null, + ward: parseWard(raw.address), + address_hint: raw.address?.replaceAll(' ', '') ?? null, + tags, + raw, + }; + + const link = new URL(href, HOST).href; + const image = $el.find('.c-propertyList__item__body__pic img').attr('src'); + return { + title, + link, + guid: link, + description: summarize(extra), + image: image || undefined, + _extra: extra, + }; + }) + .filter((item): item is DataItem => item !== null); +}; + +/** The h1 names the area the site actually resolved ('新宿区で…' / '東京都で…'), which is how a bad slug is caught. */ +const resolvedArea = (html: string): string | null => { + const h1 = clean(load(html)('h1').first().text()); + const name = h1?.split('で', 1)[0]; + return name !== undefined && name !== h1 ? name : null; +}; + +export const handler = async (ctx): Promise => { + const pref: string = ctx.req.param('pref'); + const city: string = ctx.req.param('city'); + const type: string | undefined = ctx.req.param('type'); + const slugs = Object.entries({ pref, city, type }); + for (const [name, value] of slugs) { + if (value !== undefined && !SLUG.test(value)) { + throw new Error(`Invalid ${name} "${value}", expected a lowercase site slug such as tokyo / shinjuku-city / bar`); + } + } + const listUrl = `${HOST}/${pref}/${city}/${type === undefined ? '' : `${type}/`}`; + + const html: string = await ofetch(listUrl, { responseType: 'text' }); + // An unknown 市区町村 slug is answered with the whole prefecture at HTTP 200 rather than a 404, which would + // look like a working ward feed. A real 市区町村 always ends in 区/市/町/村, so a 都道府県 here means it fell back. + const area = resolvedArea(html); + if (area === null || /[都道府県]$/.test(area)) { + throw new Error(`Unknown city "${city}" for ${pref} — the site fell back to ${area ?? 'an unknown area'} instead of a 市区町村`); + } + + return { + title: `居抜き店舗.com ${area}${type === undefined ? '' : ` (${type})`}`, + link: listUrl, + language: 'ja', + item: parseList(html), + }; +}; + +export const route: Route = { + path: '/:pref/:city/:type?', + name: '居抜き物件', + url: 'www.i-tenpo.com', + maintainers: ['pseudoyu'], + handler, + example: '/i-tenpo/tokyo/shinjuku-city', + parameters: { + pref: { + description: '都道府県 slug, e.g. `tokyo`, `kanagawa`', + }, + city: { + description: '市区町村 slug as the site spells it — `shinjuku-city`, `minato-city`, `yokohamashinaka-city`', + }, + type: { + description: 'Optional 業態 slug, e.g. `bar`, `izakaya`, `cafe`, `restaurant`, `other-restaurants`; omit for every 業態', + }, + }, + description: `Listings on 居抜き店舗.com for one 市区町村 (first page, 20 listings), optionally narrowed to one 業態 — \`/i-tenpo/tokyo/shinjuku-city/bar\` is 新宿区のバー. + +Each item's \`_extra\` follows the shared listing shape (賃料,坪,坪単価,階層,最寄駅,造作価格,前業態,引渡状態,…); unknown values are \`null\`. 敷金,礼金 and the coordinates are shown only to signed-in users and are therefore always \`null\`, the 丁目 appears only on the detail page so \`address_hint\` stops at the 町,and the cards carry no 登録日 so items have no \`pubDate\`. + +引渡状態 combines two things — 営業状況 (\`閉店済\` / \`営業中\` / \`確認中\` / \`新築\`) and 引渡形態 (\`居抜き\` / \`スケルトン\` / \`現状渡し\`). Only the 引渡形態 half maps to \`condition\`, so \`現状渡し\` yields \`null\` rather than being forced into 居抜き or スケルトン;the whole string stays in \`raw.handover\`, which is where the 閉店済 closure signal can be read. Likewise 造作価格 \`造作なし\` / \`造作無償\` become \`0\` because they really are zero, while \`確認中\` stays \`null\` because it is unknown. + +An unknown 市区町村 slug is answered by the site with the whole prefecture at HTTP 200 rather than a 404, so the route checks the area the page actually resolved and fails instead of silently serving prefecture-wide listings as if they were one ward's.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.i-tenpo.com/:pref/:city/:type'], + target: '/:pref/:city/:type', + }, + ], +}; From 69dd91bad479b3dcef5c87f9149fa3b54d0b8931 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 05:18:28 +0900 Subject: [PATCH 0857/1190] =?UTF-8?q?feat(route/bukenavi):=20add=20an=20op?= =?UTF-8?q?tional=20=E5=B8=82=E5=8C=BA=E7=94=BA=E6=9D=91=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route only reached prefecture level, so following one ward meant filtering a whole prefecture's ten newest listings client-side and missing most of that ward's. Add an optional `:city?` segment carrying the 5-digit JIS X 0402 code the list endpoint accepts as `city[]`. The site pairs it with `prefecture[]`, so `pref` is required alongside it and a code that does not belong to that prefecture is rejected. The segment is optional, so existing /bukenavi/object/kanto/tokyo URLs are unchanged. Worth recording for whoever looks next: `bukenavi.jp/{region}/area/{日本語}` pages carry no listings at all — they are SEO landing pages — and `location[]` is 立地 (駅チカ / 繁華街), not area. The 市区町村 filter is `city[]` on the list endpoint. Verified against the live site: kanagawa/14104 returns 9 listings all in 横浜市中区 and tokyo/13104 returns 10 all in 新宿区, against a prefecture-wide control that spans nine different wards. --- lib/routes/bukenavi/object.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/routes/bukenavi/object.ts b/lib/routes/bukenavi/object.ts index 7fbdff5343e5..533b0dda8933 100644 --- a/lib/routes/bukenavi/object.ts +++ b/lib/routes/bukenavi/object.ts @@ -172,8 +172,22 @@ export const handler = async (ctx): Promise => { if (prefCode !== undefined && !/^\d{2}$/.test(prefCode)) { throw new Error(`Unknown prefecture "${pref}", expected a slug (${Object.keys(PREFECTURES).join(', ')}) or a two-digit JIS code`); } + // 市区町村 codes are the prefecture's two digits plus three more (横浜市中区 = 14104), and the site pairs + // them with prefecture[], so both are required and the pair is checked rather than trusted. + const city: string | undefined = ctx.req.param('city'); + if (city !== undefined) { + if (prefCode === undefined) { + throw new Error('A city needs its prefecture too, e.g. /bukenavi/object/kanto/kanagawa/14104'); + } + if (!/^\d{5}$/.test(city)) { + throw new Error(`Invalid city "${city}", expected a 5-digit JIS X 0402 市区町村 code such as 14104`); + } + if (!city.startsWith(prefCode)) { + throw new Error(`City "${city}" does not belong to prefecture ${prefCode}`); + } + } // wanted=1 limits the list to 募集中 listings. - const listUrl = `${HOST}/${region.slug}/object/list?wanted=1${prefCode === undefined ? '' : `&prefecture%5B%5D=${prefCode}`}`; + const listUrl = `${HOST}/${region.slug}/object/list?wanted=1${prefCode === undefined ? '' : `&prefecture%5B%5D=${prefCode}`}${city === undefined ? '' : `&city%5B%5D=${city}`}`; const cards = parseList(await ofetch(listUrl), region.slug); const items = await pMap( @@ -195,7 +209,7 @@ export const handler = async (ctx): Promise => { ); return { - title: `ぶけなび 新着物件 (${region.label}${prefCode === undefined ? '' : ` ${pref}`})`, + title: `ぶけなび 新着物件 (${region.label}${prefCode === undefined ? '' : ` ${pref}`}${city === undefined ? '' : ` ${city}`})`, link: listUrl, language: 'ja', item: items, @@ -203,7 +217,7 @@ export const handler = async (ctx): Promise => { }; export const route: Route = { - path: '/object/:region?/:pref?', + path: '/object/:region?/:pref?/:city?', name: '新着物件', url: 'bukenavi.jp', maintainers: ['pseudoyu'], @@ -216,8 +230,15 @@ export const route: Route = { options: REGIONS.map((r) => ({ value: r.slug, label: r.label })), }, pref: 'Prefecture slug (tokyo, kanagawa, saitama, chiba, osaka, kyoto, hyogo, aichi) or two-digit JIS X 0401 code; omit for the whole region', + city: { + description: 'Optional 市区町村, as a 5-digit JIS X 0402 code (横浜市中区 `14104`, 新宿区 `13104`). Requires `pref` and must belong to it; omit for the whole prefecture.', + }, }, - description: `New 居抜き listings on ぶけなび that are currently 募集中,newest first (first page, 10 listings). Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,前業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`.`, + description: `New 居抜き listings on ぶけなび that are currently 募集中,newest first (first page, 10 listings) — for a region, a prefecture, or one 市区町村 when \`city\` is given. Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,前業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`. + +\`city\` is a 5-digit JIS X 0402 code and the site pairs it with the prefecture, so both are required — \`/bukenavi/object/kanto/kanagawa/14104\` is 横浜市中区. A code that does not belong to \`pref\` is rejected rather than sent on. + +Note that \`bukenavi.jp/{region}/area/{日本語}\` pages are SEO landing pages carrying no listings; the 市区町村 filter is the \`city[]\` parameter on the list endpoint, which is what this route uses.`, categories: ['other'], features: { requireConfig: false, From 947c70cae31242b49976f4a7b8b5731750dfa2d0 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 05:26:38 +0900 Subject: [PATCH 0858/1190] =?UTF-8?q?feat(route/canaeru):=20add=20canaeru?= =?UTF-8?q?=EF=BC=88USEN=EF=BC=89=E5=B1=85=E6=8A=9C=E3=81=8D=E3=83=BB?= =?UTF-8?q?=E8=B2=B8=E5=BA=97=E8=88=97=E7=89=A9=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit canaeru publishes down to 市区町村 and carries two things the other listing sources do not: 住所 to the 番地 rather than the 町, and map coordinates on the detail page. robots.txt allows it — the `User-agent: *` block disallows only /api/, /joseikin/, featurecount.php and the history/favourites/register paths, while /bukken/ is explicitly sitemapped. `_extra` follows the shared ListingExtra shape. Coordinates have no field in that contract, so they pass through verbatim as `raw.lat` / `raw.lng`. `tags` are the site's own feature flags, keeping only the ones a listing actually has — the markup renders every flag and greys the rest with `class="off"` — and 居抜き / スケルトン among them is what sets `condition`, which beats guessing from prose. The site's 「ー」 placeholder is read as unknown rather than kept as text. Two labels differ between the pages: 保証金 is 保証金・敷金 on the list and 保証金/敷金 on the detail page, and parseMonthsSum adds up its '6ヶ月 / 0ヶ月' form. Only the regions reachable under a verified base path are offered — 関東 via /bukken/, 北海道 via /bukken_h/ and 東海 via /bukken_tokai/. 関西 404s under /bukken/osaka, /bukken_k/osaka and /bukken_o/osaka alike, so it is left out rather than guessed at. 造作価格 is occasionally published with 万円 appended to a figure already in 円 (one listing reads '6,050,000万円'). The route parses what is published instead of second-guessing it, and the description says so and points at raw.fixtures. --- lib/routes/canaeru/namespace.ts | 12 ++ lib/routes/canaeru/property.ts | 311 ++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 lib/routes/canaeru/namespace.ts create mode 100644 lib/routes/canaeru/property.ts diff --git a/lib/routes/canaeru/namespace.ts b/lib/routes/canaeru/namespace.ts new file mode 100644 index 000000000000..2f9ce18461f9 --- /dev/null +++ b/lib/routes/canaeru/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Canaeru', + url: 'canaeru.usen.com', + description: 'canaeru(USEN)— 飲食店開業向け 居抜き・貸店舗物件情報サイト', + lang: 'ja', + + ja: { + name: 'カナエル', + }, +}; diff --git a/lib/routes/canaeru/property.ts b/lib/routes/canaeru/property.ts new file mode 100644 index 000000000000..713602335eb7 --- /dev/null +++ b/lib/routes/canaeru/property.ts @@ -0,0 +1,311 @@ +import { load } from 'cheerio'; +import pMap from 'p-map'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseMonths, parseMonthsSum, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const HOST = 'https://canaeru.usen.com'; +const DETAIL_CONCURRENCY = 2; +const DEFAULT_LIMIT = 10; +const PAGE_SIZE = 10; + +/** + * The site splits its stock across regional base paths; 関西 is not reachable under any of them + * (`/bukken_k/osaka`, `/bukken_o/osaka` and `/bukken/osaka` all 404), so it is not offered. + */ +const PREFECTURES = [ + { slug: 'tokyo', base: 'bukken', label: '東京都', code: '13' }, + { slug: 'kanagawa', base: 'bukken', label: '神奈川県', code: '14' }, + { slug: 'saitama', base: 'bukken', label: '埼玉県', code: '11' }, + { slug: 'chiba', base: 'bukken', label: '千葉県', code: '12' }, + { slug: 'hokkaido', base: 'bukken_h', label: '北海道', code: '01' }, + { slug: 'aichi', base: 'bukken_tokai', label: '愛知県', code: '23' }, + { slug: 'shizuoka', base: 'bukken_tokai', label: '静岡県', code: '22' }, +]; + +interface ListCard { + title: string; + link: string; + image?: string; + extra: ListingExtra; +} + +interface DetailFields { + listed_at: string | null; + updated_at: string | null; + fixtures: string | null; // 造作価格 + key_money: string | null; // 礼金 + deposit: string | null; // 保証金 + contract_kind: string | null; + purpose: string | null; // 可能用途 + lat: string | null; + lng: string | null; +} + +/** The site prints 「ー」 (and plain dashes) for "not stated"; those must not survive as a value. */ +const value = (text: string | null): string | null => { + const s = clean(text); + return s === null || /^[ー—–--]+$/.test(s) ? null : s; +}; + +/** 'JR山手線新宿駅' → ['JR山手線', '新宿']; a station with no 線 prefix yields a null line. */ +const splitStation = (text: string | null): { line: string | null; station: string | null } => { + const s = clean(text?.split('徒歩', 1)[0] ?? null); + if (s === null) { + return { line: null, station: null }; + } + // Split at the last 線 rather than with a regex, which would backtrack on a line name containing 線 twice. + const cut = s.lastIndexOf('線'); + const station = (cut === -1 ? s : s.slice(cut + 1)).replace(/駅$/, ''); + return { line: cut === -1 ? null : s.slice(0, cut + 1), station: station || null }; +}; + +/** + * List cards (`div.result_item`, 10 per page): + * title + id `.title label` ('【…】物販・サービス優先店舗物件 (220168)'), detail link `.btn.detail a@href` + * ('/bukken/p220168/'), image `.imgLiquid img@src` ('/img/noimage.png' when absent), and a th/td table: + * 住所 '東京都新宿区新宿三丁目23-12' (down to 番地), 最寄駅 'JR山手線新宿駅
    徒歩2分', 現況, 賃料 ('ご相談'), + * 保証金・敷金, 坪面積/階数 '18.53坪/地下2階'. `div.feature li` lists every flag; the ones without + * `class="off"` are the ones this listing actually has (NEW / 居抜き / スケルトン / 飲食可 / 駅近 / …). + */ +const parseList = (html: string): ListCard[] => { + const $ = load(html); + return $('div.result_item') + .toArray() + .map((el): ListCard | null => { + const $el = $(el); + const heading = clean($el.find('.title label').first().text()); + const href = $el.find('.btn.detail a').attr('href'); + const id = href?.match(/\/p(\d+)/)?.[1] ?? heading?.match(/\((\d+)\)\s*$/)?.[1]; + if (!heading || !href || !id) { + return null; + } + const title = heading.replace(/\s*\(\d+\)\s*$/, ''); + + const th = (label: string) => + $el + .find('th') + .toArray() + .find((t) => clean($(t).text()) === label); + const cell = (label: string): string | null => { + const el = th(label); + return el ? value($(el).next('td').text()) : null; + }; + // `
    ` separates 駅 from 徒歩, and cheerio's .text() would run them together. + const stationEl = th('最寄駅'); + const stationHtml = stationEl ? $(stationEl).next('td').html() : null; + const stationText = value(stationHtml?.replaceAll(//gi, ' ').replaceAll(/<[^>]+>/g, '') ?? null); + + const raw: ListingExtra['raw'] = { + address: cell('住所'), + station: stationText, + purpose: cell('現況'), + rent: cell('賃料'), + deposit: cell('保証金・敷金'), + size: cell('坪面積/階数'), + }; + const tags = $el + .find('div.feature li') + .toArray() + .filter((li) => !($(li).attr('class') ?? '').split(/\s+/).includes('off')) + .map((li) => clean($(li).text())) + .filter((t): t is string => t !== null); + + const [tsuboText, floorText] = (raw.size ?? '').split('/', 2); + const { tsubo, area_m2 } = parseArea(clean(tsuboText)); + const rentJpy = parseJpy(raw.rent); + const { line, station } = splitStation(raw.station); + const image = $el.find('.imgLiquid img').attr('src'); + + const extra: ListingExtra = { + source: 'canaeru', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(clean(floorText)?.replaceAll('地下', 'B') ?? null), + station, + line, + walk_min: parseWalkMin(raw.station), + deposit_months: parseMonthsSum(raw.deposit), + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + // The 居抜き / スケルトン flags are the site's own classification, so they beat guessing from prose. + condition: parseCondition(tags.join(' '), title), + prev_business: raw.purpose, + heavy_food_ok: null, + business_limit: null, + listed_at: null, + ward: parseWard(raw.address), + address_hint: raw.address, + tags, + raw, + }; + return { + title, + link: new URL(href, HOST).href, + image: image && !image.includes('noimage') ? new URL(image, HOST).href : undefined, + extra, + }; + }) + .filter((c): c is ListCard => c !== null); +}; + +/** Detail page (`/bukken/p{id}/`): `p.date` holds both dates, a th/td table holds the fees, and the map coordinates are hidden inputs. */ +const parseDetail = (html: string): DetailFields => { + const $ = load(html); + const cell = (label: string): string | null => { + const el = $('th') + .toArray() + .find((t) => clean($(t).text()) === label); + return el ? value($(el).next('td').text()) : null; + }; + const dates = $('p.date').first().text(); + const dateOf = (label: string): string | null => value(new RegExp(`${label}[::]\\s*(\\d{4}年\\d{1,2}月\\d{1,2}日)`).exec(dates)?.[1] ?? null); + + return { + listed_at: dateOf('情報公開日'), + updated_at: dateOf('情報更新日'), + fixtures: cell('造作価格'), + key_money: cell('礼金'), + // The list writes this label with ・ and the detail page with /; parseMonthsSum adds up '6ヶ月 / 0ヶ月'. + deposit: cell('保証金/敷金'), + contract_kind: cell('契約種別'), + purpose: cell('可能用途'), + lat: value($('#lat').attr('value') ?? null), + lng: value($('#lng').attr('value') ?? null), + }; +}; + +/** List fields + detail fields; a value already known from the list is never overwritten with null. */ +const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ + ...base, + deposit_months: parseMonthsSum(d.deposit) ?? base.deposit_months, + key_money_months: parseMonths(d.key_money), + fixtures_transfer_jpy: parseJpy(d.fixtures), + business_limit: d.purpose, + listed_at: parseYmd(d.listed_at), + // ListingExtra has no coordinate fields, so the pair stays in `raw` exactly as published. + raw: { + ...base.raw, + listed_at: d.listed_at, + updated_at: d.updated_at, + fixtures: d.fixtures, + key_money: d.key_money, + deposit_detail: d.deposit, + contract_kind: d.contract_kind, + available_purpose: d.purpose, + lat: d.lat, + lng: d.lng, + }, +}); + +/** A failed detail page keeps the list fields instead of breaking the feed. */ +const enrich = async (card: ListCard): Promise => { + try { + return mergeDetail(card.extra, parseDetail(await ofetch(card.link, { responseType: 'text' }))); + } catch (error) { + logger.warn(`canaeru: detail fetch failed for ${card.link}: ${String(error)}`); + return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; + } +}; + +export const handler = async (ctx): Promise => { + const pref: string = ctx.req.param('pref') ?? 'tokyo'; + const prefecture = PREFECTURES.find((p) => p.slug === pref); + if (!prefecture) { + throw new Error(`Unknown prefecture "${pref}", expected one of ${PREFECTURES.map((p) => p.slug).join(', ')}`); + } + // 市区町村 codes are the prefecture's two digits plus three more (新宿区 = 13104), written with an `A` + // prefix in the path. A code from another prefecture would quietly widen the search, so the pair is checked. + const city: string | undefined = ctx.req.param('city'); + if (city !== undefined) { + if (!/^\d{5}$/.test(city)) { + throw new Error(`Invalid city "${city}", expected a 5-digit JIS X 0402 市区町村 code such as 13104`); + } + if (!city.startsWith(prefecture.code)) { + throw new Error(`City "${city}" does not belong to ${prefecture.label} (${prefecture.code})`); + } + } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, PAGE_SIZE); + const listUrl = `${HOST}/${prefecture.base}/${prefecture.slug}/search/${city === undefined ? '' : `A${city}/`}`; + + const cards = parseList(await ofetch(listUrl, { responseType: 'text' })).slice(0, limit); + const items = await pMap( + cards, + (card) => + cache.tryGet(card.link, async (): Promise => { + const extra = await enrich(card); + return { + title: card.title, + link: card.link, + guid: card.link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: summarize(extra), + image: card.image, + _extra: extra, + }; + }) as Promise, + { concurrency: DETAIL_CONCURRENCY } + ); + + return { + title: `canaeru 居抜き・貸店舗物件 (${prefecture.label}${city === undefined ? '' : ` ${city}`})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/:pref?/:city?', + name: '居抜き・貸店舗物件', + url: 'canaeru.usen.com', + maintainers: ['pseudoyu'], + handler, + example: '/canaeru/tokyo/13104', + parameters: { + pref: { + description: 'Prefecture', + default: 'tokyo', + options: PREFECTURES.map((p) => ({ value: p.slug, label: p.label })), + }, + city: { + description: 'Optional 市区町村, as a 5-digit JIS X 0402 code (新宿区 `13104`, 港区 `13103`, 横浜市中区 `14104`). Must belong to `pref`; omit for the whole prefecture.', + }, + }, + description: `Listings on canaeru(USEN)for one prefecture — or one 市区町村 when \`city\` is given (\`/canaeru/tokyo/13104\` is 新宿区). Each item's \`_extra\` carries the shared listing fields (賃料,坪,坪単価,階,最寄駅,保証金,礼金,造作価格,現況,…) from the list and detail pages; unknown values are \`null\`, and the site's 「ー」 placeholder is treated as unknown rather than kept as text. + +Two things this source does better than most: 住所 is published down to the 番地 rather than the 町,and the detail page carries map coordinates. \`ListingExtra\` has no coordinate fields, so they are passed through verbatim as \`raw.lat\` / \`raw.lng\`. + +\`tags\` are the site's own feature flags, keeping only those a listing actually has — the markup lists every flag and greys the rest out with \`class="off"\` — and 居抜き / スケルトン among them is what sets \`condition\`. + +One caveat on 造作価格: the publisher occasionally appends 万円 to a figure that is already in 円 (one listing reads \`6,050,000万円\`), so \`fixtures_transfer_jpy\` can carry an implausible value. The route parses what is published rather than second-guessing it, so treat \`raw.fixtures\` as the ground truth when the number looks wrong. + +関西 is not offered: the site serves it from a separate base path that could not be reached (\`/bukken/osaka\`, \`/bukken_k/osaka\` and \`/bukken_o/osaka\` all 404). + +| Query | Description | Default | +| ------- | ---------------------------------------------------------------------------- | ------- | +| \`limit\` | Number of listings to process (detail pages are fetched per listing), max 10 | 10 |`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['canaeru.usen.com/bukken/:pref/search/:city', 'canaeru.usen.com/bukken/:pref/search'], + target: '/:pref', + }, + ], +}; From 85a14455d0a94f541cb51bb24ce0e3af016bfc57 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 05:41:38 +0900 Subject: [PATCH 0859/1190] =?UTF-8?q?feat(route/athome):=20add=20=E3=82=A2?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=83=9B=E3=83=BC=E3=83=A0=20=E8=B2=B8?= =?UTF-8?q?=E5=BA=97=E8=88=97=20listings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit アットホーム reaches 市区町村 and publishes 住所 down to the 丁目, which the other listing sources do not, so it widens ward-level coverage. The page is Angular Universal SSR behind a JavaScript interstitial, so it needs a browser: `requirePuppeteer` and `antiCrawler` are set and the route uses getPlaywrightPage. The first paint can be an 「認証中」 screen that clears itself once the browser finishes loading, and only the settled page carries `#serverApp-state`. Waiting for that element is therefore both the readiness and the interstitial check, and the route throws when it never attaches — an unsettled page must surface as an error, never as a silently empty feed. Following the pattern of the existing Playwright routes, the HTML is taken with page.content(), the browser is released immediately, and parsing happens outside it with cheerio rather than in page.evaluate. Reading the payload needs three unwraps that are not obvious: the SSR state keeps the whole HTTP response the server made (hence the `G.text.…` keys), its `body` is held as text rather than as an object, and the listings sit under that payload's `data`. `condition` and `prev_business` come from the site's own 店舗プラス block (isInuki / isSkeleton / lastTenanto) rather than being inferred from prose, though most listings leave them unset. 造作価格 is not published here so fixtures_transfer_jpy is always null, deposit_months prefers 保証金 and falls back to 敷金, and listed_at is null because 情報公開日 appears only on the detail page, which this route does not open. --- lib/routes/athome/namespace.ts | 12 ++ lib/routes/athome/rent-store.ts | 206 ++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 lib/routes/athome/namespace.ts create mode 100644 lib/routes/athome/rent-store.ts diff --git a/lib/routes/athome/namespace.ts b/lib/routes/athome/namespace.ts new file mode 100644 index 000000000000..cfef8bc72e7d --- /dev/null +++ b/lib/routes/athome/namespace.ts @@ -0,0 +1,12 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'at home', + url: 'www.athome.co.jp', + description: 'アットホーム — 不動産情報サイト(貸店舗・事業用物件)', + lang: 'ja', + + ja: { + name: 'アットホーム', + }, +}; diff --git a/lib/routes/athome/rent-store.ts b/lib/routes/athome/rent-store.ts new file mode 100644 index 000000000000..c91ad165cc96 --- /dev/null +++ b/lib/routes/athome/rent-store.ts @@ -0,0 +1,206 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import { getPlaywrightPage } from '@/utils/playwright'; + +const HOST = 'https://www.athome.co.jp'; +const SLUG = /^[a-z\d_-]+$/; +/** The SSR state is keyed by the internal BFF call that produced it. */ +const LIST_KEY = '/bukken/list/first-view'; +const STATE_TIMEOUT = 45000; + +interface Bukken { + id?: string | number; + title?: string; + tatemonoNm?: string; + location?: string; + type?: string; + traffic?: Array<{ lineName?: string; stationName?: string; tohoJikan?: string }>; + accessInfo?: Array<{ walkingTime?: string }>; + contract?: { price?: string; managementFee?: string; deposit?: string; keyMoney?: string; guaranteeDeposit?: string }; + areaInfo?: { area?: string; tsubo?: string; unitPrice?: string }; + bukkenInfo?: { kaidateKai?: string; chikunengetsu?: string; construction?: string }; + /** 店舗プラス: the site's own 居抜き / スケルトン flags ('0' | '1') and previous tenant. */ + tenpoPlus?: { isInuki?: string; isSkeleton?: string; lastTenanto?: { name?: string; detail?: string } }; + mainImage?: { url?: string }; + recommendComment?: { comment?: string }; +} + +/** + * The page is Angular Universal SSR behind a JS interstitial: the first paint can be an 「認証中」 screen that + * clears itself once the browser finishes loading, and only the settled page carries `#serverApp-state`. + * Waiting for that element is therefore both the readiness check and the interstitial check — if it never + * attaches the route throws, so an unsettled page surfaces as an error rather than as an empty feed. + */ +const fetchState = async (url: string): Promise => { + const { page, destroy } = await getPlaywrightPage(url, { gotoConfig: { waitUntil: 'domcontentloaded' } }); + try { + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + return await page.content(); + } catch (error) { + throw new Error('athome: the listing page did not settle — it is probably still showing the 認証中 interstitial', { cause: error }); + } finally { + // Release the browser as soon as the HTML is in hand; parsing happens outside it. + await destroy(); + } +}; + +const listOf = (html: string): Bukken[] => { + const raw = load(html)('#serverApp-state').text(); + if (!raw) { + throw new Error('athome: #serverApp-state is missing from the settled page'); + } + const state: Record = JSON.parse(raw); + const key = Object.keys(state).find((k) => k.includes(LIST_KEY)); + if (key === undefined) { + throw new Error('athome: the SSR state holds no listing response; the internal API may have been renamed'); + } + // The state keeps the whole HTTP response the SSR pass made (`G.text.…` keys), so unwrap three layers: + // the response envelope, its `body` — held as text, not an object — and that payload's `data`. + const envelope = state[key] as { body?: unknown } | null; + const body = envelope?.body ?? envelope; + const parsed = typeof body === 'string' ? JSON.parse(body) : body; + const payload = (parsed as { data?: unknown } | null)?.data ?? parsed; + const list = (payload as { bukkenData?: { bukkenList?: Bukken[] } } | null)?.bukkenData?.bukkenList; + return Array.isArray(list) ? list : []; +}; + +/** '6階建 / 4階' → the occupied floor ('4階'); a value with no separator is taken as-is. */ +const occupiedFloor = (kaidateKai: string | undefined): string | null => { + const parts = (kaidateKai ?? '').split('/'); + return clean(parts.length > 1 ? parts.at(-1) : parts[0]); +}; + +const toItem = (b: Bukken): DataItem | null => { + const id = b.id === undefined ? null : String(b.id); + const title = clean(b.title) ?? clean(b.tatemonoNm); + if (id === null || title === null) { + return null; + } + const t = b.traffic?.[0]; + const rentJpy = parseJpy(clean(b.contract?.price)); + const { tsubo, area_m2 } = parseArea([clean(b.areaInfo?.area), clean(b.areaInfo?.tsubo)].filter(Boolean).join(' ')); + + const raw: ListingExtra['raw'] = { + location: clean(b.location), + building: clean(b.tatemonoNm), + price: clean(b.contract?.price), + management_fee: clean(b.contract?.managementFee), + deposit: clean(b.contract?.deposit), + key_money: clean(b.contract?.keyMoney), + guarantee_deposit: clean(b.contract?.guaranteeDeposit), + area: clean(b.areaInfo?.area), + tsubo: clean(b.areaInfo?.tsubo), + unit_price: clean(b.areaInfo?.unitPrice), + floors: clean(b.bukkenInfo?.kaidateKai), + built: clean(b.bukkenInfo?.chikunengetsu), + construction: clean(b.bukkenInfo?.construction), + // tohoJikan is the bare number ('8'); accessInfo carries the same walk with its 分 unit. + station: [clean(t?.lineName), clean(t?.stationName), clean(b.accessInfo?.[0]?.walkingTime)].filter(Boolean).join(' ') || null, + comment: clean(b.recommendComment?.comment), + prev_tenant: clean(b.tenpoPlus?.lastTenanto?.name), + inuki: clean(b.tenpoPlus?.isInuki), + skeleton: clean(b.tenpoPlus?.isSkeleton), + type: clean(b.type), + }; + + const extra: ListingExtra = { + source: 'athome', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(raw.unit_price) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(occupiedFloor(b.bukkenInfo?.kaidateKai)?.replaceAll('地下', 'B') ?? null), + station: clean(t?.stationName)?.replace(/駅$/, '') ?? null, + line: clean(t?.lineName), + walk_min: parseWalkMin(raw.station), + // 保証金 is the closer analogue of the other sources' deposit; 敷金 stands in when it is absent. + deposit_months: parseMonths(raw.guarantee_deposit) ?? parseMonths(raw.deposit), + deposit_jpy: null, + key_money_months: parseMonths(raw.key_money), + // 造作価格 is not published on this site. + fixtures_transfer_jpy: null, + // 店舗プラス carries the site's own classification, so it is read rather than guessed from prose. + condition: b.tenpoPlus?.isInuki === '1' ? 'inuki' : b.tenpoPlus?.isSkeleton === '1' ? 'skeleton' : null, + prev_business: raw.prev_tenant, + heavy_food_ok: null, + business_limit: null, + // 情報公開日 lives on the detail page only, which this route does not open. + listed_at: null, + ward: parseWard(raw.location), + address_hint: raw.location, + tags: [], + raw, + }; + + const link = `${HOST}/rent_store/${id}/`; + return { + title, + link, + guid: link, + description: [raw.comment, summarize(extra)].filter(Boolean).join(' / '), + image: b.mainImage?.url ? new URL(b.mainImage.url, HOST).href : undefined, + _extra: extra, + }; +}; + +export const handler = async (ctx): Promise => { + const pref: string = ctx.req.param('pref'); + const city: string = ctx.req.param('city'); + const slugs = Object.entries({ pref, city }); + for (const [name, v] of slugs) { + if (!SLUG.test(v)) { + throw new Error(`Invalid ${name} "${v}", expected a lowercase site slug such as tokyo / shinjuku-city / yokohama_naka-city`); + } + } + const listUrl = `${HOST}/rent_store/${pref}/${city}/list/`; + + const items = listOf(await fetchState(listUrl)) + .map((b) => toItem(b)) + .filter((i): i is DataItem => i !== null); + + return { + title: `アットホーム 貸店舗 (${city})`, + link: listUrl, + language: 'ja', + item: items, + }; +}; + +export const route: Route = { + path: '/rent-store/:pref/:city', + name: '貸店舗', + url: 'www.athome.co.jp', + maintainers: ['pseudoyu'], + handler, + example: '/athome/rent-store/tokyo/shinjuku-city', + parameters: { + pref: { + description: '都道府県 slug, e.g. `tokyo`, `kanagawa`', + }, + city: { + description: '市区町村 slug as the site spells it — `shinjuku-city`, `minato-city`, `yokohama_naka-city`. Note the underscore in 政令指定都市 slugs; a hyphen there 404s.', + }, + }, + description: `貸店舗 listings on アットホーム for one 市区町村 (first page, 30 listings). + +This route needs a browser. The page is Angular Universal SSR sitting behind a JavaScript interstitial, so its first paint can be an 「認証中」 screen that clears itself once the browser finishes loading; only the settled page carries the \`#serverApp-state\` payload the route reads. The route waits for that element and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. Expect it to be slower and less reliable than the plain-HTML listing routes, and cache it generously. + +\`_extra\` follows the shared listing shape. \`condition\` and \`prev_business\` come from the site's own 店舗プラス block (\`isInuki\` / \`isSkeleton\` / \`lastTenanto\`) rather than being guessed from prose, though most listings leave those unset. 造作価格 is not published here, so \`fixtures_transfer_jpy\` is always \`null\`, and \`deposit_months\` prefers 保証金 with 敷金 as the fallback. \`address_hint\` reaches the 丁目,and \`listed_at\` is \`null\` because 情報公開日 is published only on the detail page, which this route does not open.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportRadar: true, + }, + radar: [ + { + source: ['www.athome.co.jp/rent_store/:pref/:city/list'], + target: '/rent-store/:pref/:city', + }, + ], +}; From 15dea70e89749df2fd3c49fbe6cf8a30fd3d28f5 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:00:26 +0900 Subject: [PATCH 0860/1190] =?UTF-8?q?feat(route/athome):=20read=20?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E5=85=AC=E9=96=8B=E6=97=A5,=20coordinates=20?= =?UTF-8?q?and=20fee=20detail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The list payload carries no publication date, and the site's default ordering is not chronological — only 7 of the 30 listings on the first 新宿区 page carried a NEW mark, and neither the default sort nor `?sort=1` is newest-first. A monitor watching the first page could therefore miss a newly published listing entirely, which matters more than the missing date did. 情報公開日, the coordinates and the fee detail exist only on each listing's own page, so the route now visits them and re-sorts the feed by 情報公開日, newest first. To keep that affordable it reuses a single browser tab instead of opening a browser per listing, caches per listing so a repeated poll only pays for listings it has not seen, and defaults `limit` to 10 of the 30. That also fills fields that were previously null, all from published values rather than inference: listed_at and pubDate from 情報公開日, heavy_food_ok and business_limit from the notice flags (「飲食店不可」), fixtures_transfer_jpy from 造作譲渡 — which this site does publish, contrary to the earlier comment — and tags from the site's 特徴 list. address_hint now uses the detail page's 所在地, which includes the 都道府県 the list omits. raw gains what the shared contract has no field for: lat/lng, the full 所在地, 建物名+部屋番号, 設備, 築年月, 敷引, 償却, その他一時金 and the 定期借家 flag. --- lib/routes/athome/rent-store.ts | 199 ++++++++++++++++++++++++++------ 1 file changed, 161 insertions(+), 38 deletions(-) diff --git a/lib/routes/athome/rent-store.ts b/lib/routes/athome/rent-store.ts index c91ad165cc96..24994fbb079d 100644 --- a/lib/routes/athome/rent-store.ts +++ b/lib/routes/athome/rent-store.ts @@ -1,15 +1,41 @@ import { load } from 'cheerio'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import { parseDate } from '@/utils/parse-date'; import { getPlaywrightPage } from '@/utils/playwright'; +import timezone from '@/utils/timezone'; const HOST = 'https://www.athome.co.jp'; const SLUG = /^[a-z\d_-]+$/; /** The SSR state is keyed by the internal BFF call that produced it. */ const LIST_KEY = '/bukken/list/first-view'; +const DETAIL_KEY = '/bukken/detail/'; const STATE_TIMEOUT = 45000; +const DEFAULT_LIMIT = 10; +const PAGE_SIZE = 30; + +/** Fields the list payload does not carry; all of them live on the detail page's own SSR payload. */ +interface Detail { + listed_at: string | null; // 情報公開日 + next_update: string | null; // 次回更新予定日 + lat: string | null; + lng: string | null; + location: string | null; // includes the 都道府県, unlike the list's value + building: string | null; // 建物名 + 部屋番号 + notices: string[]; // 飲食店不可 etc. + features: string[]; // 特徴 (19時以降も接客可, 駅徒歩5分以内, …) + facilities: string[]; // 設備 + fixtures: string | null; // 造作譲渡 + built: string | null; // 築年月 + key_money_amortised: string | null; // 償却 + deposit_deduction: string | null; // 敷引 + other_lump_sum: string | null; // その他一時金 + fixed_term: boolean | null; // 定期借家 +} interface Bukken { id?: string | number; @@ -31,49 +57,92 @@ interface Bukken { /** * The page is Angular Universal SSR behind a JS interstitial: the first paint can be an 「認証中」 screen that * clears itself once the browser finishes loading, and only the settled page carries `#serverApp-state`. - * Waiting for that element is therefore both the readiness check and the interstitial check — if it never - * attaches the route throws, so an unsettled page surfaces as an error rather than as an empty feed. + * Waiting for that element is therefore both the readiness check and the interstitial check. + * + * The state keeps the whole HTTP response the SSR pass made (`G.text.…` keys), so the payload is three + * layers down: the response envelope, its `body` — held as text, not an object — and that payload's `data`. */ -const fetchState = async (url: string): Promise => { - const { page, destroy } = await getPlaywrightPage(url, { gotoConfig: { waitUntil: 'domcontentloaded' } }); - try { - await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); - return await page.content(); - } catch (error) { - throw new Error('athome: the listing page did not settle — it is probably still showing the 認証中 interstitial', { cause: error }); - } finally { - // Release the browser as soon as the HTML is in hand; parsing happens outside it. - await destroy(); - } -}; - -const listOf = (html: string): Bukken[] => { +const payloadOf = (html: string, keyPart: string): Record | null => { const raw = load(html)('#serverApp-state').text(); if (!raw) { throw new Error('athome: #serverApp-state is missing from the settled page'); } const state: Record = JSON.parse(raw); - const key = Object.keys(state).find((k) => k.includes(LIST_KEY)); + const key = Object.keys(state).find((k) => k.includes(keyPart)); if (key === undefined) { - throw new Error('athome: the SSR state holds no listing response; the internal API may have been renamed'); + return null; } - // The state keeps the whole HTTP response the SSR pass made (`G.text.…` keys), so unwrap three layers: - // the response envelope, its `body` — held as text, not an object — and that payload's `data`. const envelope = state[key] as { body?: unknown } | null; const body = envelope?.body ?? envelope; const parsed = typeof body === 'string' ? JSON.parse(body) : body; - const payload = (parsed as { data?: unknown } | null)?.data ?? parsed; - const list = (payload as { bukkenData?: { bukkenList?: Bukken[] } } | null)?.bukkenData?.bukkenList; + return ((parsed as { data?: unknown } | null)?.data ?? parsed) as Record | null; +}; + +const listOf = (html: string): Bukken[] => { + const payload = payloadOf(html, LIST_KEY); + if (payload === null) { + throw new Error('athome: the SSR state holds no listing response; the internal API may have been renamed'); + } + const list = (payload as { bukkenData?: { bukkenList?: Bukken[] } }).bukkenData?.bukkenList; return Array.isArray(list) ? list : []; }; +interface PropertyData { + kokaiDate?: string; + kokaiKoshinDate?: string; + ido?: string; + keido?: string; + location?: string; + tatemonoHeyaNo?: string; + notices?: Array<{ flagName?: string } | null>; + tokutyou?: Array<{ name?: string } | null>; + facility?: Array<{ items?: Array<{ flagName?: string } | null> } | null>; + zosakuJoto?: string; + shikibiki?: string; + hoshokinShokyaku?: string; + sonotaIchijikin?: string; + teiki_shakka_fl?: boolean; + bukkenInfo?: { chikunengetsu?: string }; +} + +/** The site writes 「-」 for "not stated", which must not survive as a value. */ +const value = (text: string | undefined | null): string | null => { + const s = clean(text); + return s === null || /^[-ー—–-]+$/.test(s) ? null : s; +}; + +const names = (list: Array<{ flagName?: string; name?: string } | null> | undefined): string[] => (list ?? []).map((x) => clean(x?.flagName ?? x?.name)).filter((x): x is string => x !== null); + +/** 情報公開日, the coordinates and the fee/feature detail exist only on the detail page's own SSR payload. */ +const detailOf = (html: string): Detail => { + const p = ((payloadOf(html, DETAIL_KEY) as { propertyData?: PropertyData } | null)?.propertyData ?? {}) as PropertyData; + return { + listed_at: value(p.kokaiDate), + next_update: value(p.kokaiKoshinDate), + lat: value(p.ido), + lng: value(p.keido), + location: value(p.location), + building: value(p.tatemonoHeyaNo), + notices: names(p.notices), + // `tokutyou` is a sparse array: every possible 特徴 has a slot and the absent ones are null. + features: names(p.tokutyou), + facilities: (p.facility ?? []).flatMap((g) => names(g?.items)), + fixtures: value(p.zosakuJoto), + built: value(p.bukkenInfo?.chikunengetsu), + key_money_amortised: value(p.hoshokinShokyaku), + deposit_deduction: value(p.shikibiki), + other_lump_sum: value(p.sonotaIchijikin), + fixed_term: p.teiki_shakka_fl ?? null, + }; +}; + /** '6階建 / 4階' → the occupied floor ('4階'); a value with no separator is taken as-is. */ const occupiedFloor = (kaidateKai: string | undefined): string | null => { const parts = (kaidateKai ?? '').split('/'); return clean(parts.length > 1 ? parts.at(-1) : parts[0]); }; -const toItem = (b: Bukken): DataItem | null => { +const toItem = (b: Bukken, d: Detail | null): DataItem | null => { const id = b.id === undefined ? null : String(b.id); const title = clean(b.title) ?? clean(b.tatemonoNm); if (id === null || title === null) { @@ -104,6 +173,20 @@ const toItem = (b: Bukken): DataItem | null => { inuki: clean(b.tenpoPlus?.isInuki), skeleton: clean(b.tenpoPlus?.isSkeleton), type: clean(b.type), + // Detail-page only; null when the detail fetch was skipped or failed. + listed_at: d?.listed_at ?? null, + next_update: d?.next_update ?? null, + lat: d?.lat ?? null, + lng: d?.lng ?? null, + location_full: d?.location ?? null, + building_room: d?.building ?? null, + notices: d?.notices.join(' / ') || null, + facilities: d?.facilities.join(' / ') || null, + fixtures: d?.fixtures ?? null, + deposit_deduction: d?.deposit_deduction ?? null, + key_money_amortised: d?.key_money_amortised ?? null, + other_lump_sum: d?.other_lump_sum ?? null, + fixed_term: d?.fixed_term === null || d?.fixed_term === undefined ? null : String(d.fixed_term), }; const extra: ListingExtra = { @@ -121,18 +204,18 @@ const toItem = (b: Bukken): DataItem | null => { deposit_months: parseMonths(raw.guarantee_deposit) ?? parseMonths(raw.deposit), deposit_jpy: null, key_money_months: parseMonths(raw.key_money), - // 造作価格 is not published on this site. - fixtures_transfer_jpy: null, + fixtures_transfer_jpy: parseJpy(raw.fixtures), // 店舗プラス carries the site's own classification, so it is read rather than guessed from prose. condition: b.tenpoPlus?.isInuki === '1' ? 'inuki' : b.tenpoPlus?.isSkeleton === '1' ? 'skeleton' : null, prev_business: raw.prev_tenant, - heavy_food_ok: null, - business_limit: null, - // 情報公開日 lives on the detail page only, which this route does not open. - listed_at: null, - ward: parseWard(raw.location), - address_hint: raw.location, - tags: [], + // 「飲食店不可」 is published as a notice flag, so it is read rather than inferred. + heavy_food_ok: d?.notices.includes('飲食店不可') ? false : parseHeavyFood(raw.notices), + business_limit: raw.notices, + listed_at: parseYmd(raw.listed_at), + ward: parseWard(raw.location_full ?? raw.location), + // The detail page spells the address with its 都道府県; the list omits it. + address_hint: raw.location_full ?? raw.location, + tags: d?.features ?? [], raw, }; @@ -141,6 +224,7 @@ const toItem = (b: Bukken): DataItem | null => { title, link, guid: link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), description: [raw.comment, summarize(extra)].filter(Boolean).join(' / '), image: b.mainImage?.url ? new URL(b.mainImage.url, HOST).href : undefined, _extra: extra, @@ -156,11 +240,40 @@ export const handler = async (ctx): Promise => { throw new Error(`Invalid ${name} "${v}", expected a lowercase site slug such as tokyo / shinjuku-city / yokohama_naka-city`); } } + const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, PAGE_SIZE); const listUrl = `${HOST}/rent_store/${pref}/${city}/list/`; - const items = listOf(await fetchState(listUrl)) - .map((b) => toItem(b)) - .filter((i): i is DataItem => i !== null); + // One browser for the whole run: the list page, then each detail page by navigating the same tab. + // Opening a browser per listing would be an order of magnitude more expensive. + const { page, destroy } = await getPlaywrightPage(listUrl, { gotoConfig: { waitUntil: 'domcontentloaded' } }); + const items: DataItem[] = []; + try { + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + const bukken = listOf(await page.content()).slice(0, limit); + + for (const b of bukken) { + const link = `${HOST}/rent_store/${b.id}/`; + // Cached per listing, so a monitoring poll only pays for listings it has not seen before. + // eslint-disable-next-line no-await-in-loop -- one shared tab, so the detail pages must be visited in turn + const detail = (await cache.tryGet(`athome:detail:${b.id}`, async () => { + await page.goto(link, { waitUntil: 'domcontentloaded' }); + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + return detailOf(await page.content()); + })) as Detail | null; + const item = toItem(b, detail); + if (item !== null) { + items.push(item); + } + } + } catch (error) { + logger.warn(`athome: ${listUrl} failed: ${String(error)}`); + throw new Error('athome: the listing page did not settle — it is probably still showing the 認証中 interstitial', { cause: error }); + } finally { + await destroy(); + } + + // The site's default order is not chronological, so the feed is sorted by 情報公開日 itself, newest first. + items.sort((a, b) => ((b._extra as ListingExtra).listed_at ?? '').localeCompare((a._extra as ListingExtra).listed_at ?? '')); return { title: `アットホーム 貸店舗 (${city})`, @@ -185,11 +298,21 @@ export const route: Route = { description: '市区町村 slug as the site spells it — `shinjuku-city`, `minato-city`, `yokohama_naka-city`. Note the underscore in 政令指定都市 slugs; a hyphen there 404s.', }, }, - description: `貸店舗 listings on アットホーム for one 市区町村 (first page, 30 listings). + description: `貸店舗 listings on アットホーム for one 市区町村,newest first. This route needs a browser. The page is Angular Universal SSR sitting behind a JavaScript interstitial, so its first paint can be an 「認証中」 screen that clears itself once the browser finishes loading; only the settled page carries the \`#serverApp-state\` payload the route reads. The route waits for that element and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. Expect it to be slower and less reliable than the plain-HTML listing routes, and cache it generously. -\`_extra\` follows the shared listing shape. \`condition\` and \`prev_business\` come from the site's own 店舗プラス block (\`isInuki\` / \`isSkeleton\` / \`lastTenanto\`) rather than being guessed from prose, though most listings leave those unset. 造作価格 is not published here, so \`fixtures_transfer_jpy\` is always \`null\`, and \`deposit_months\` prefers 保証金 with 敷金 as the fallback. \`address_hint\` reaches the 丁目,and \`listed_at\` is \`null\` because 情報公開日 is published only on the detail page, which this route does not open.`, +情報公開日,the coordinates and the fee detail exist only on each listing's own page, so the route visits them — reusing one browser tab rather than opening a browser per listing, and caching per listing so a repeated poll only pays for listings it has not seen before. That is why \`limit\` defaults to 10 rather than the full page of 30. + +**The site's own ordering is not chronological**, so the feed is re-sorted by 情報公開日,newest first. Without that a newly published listing could sit well down the list and never reach a monitor watching the first page. + +\`_extra\` follows the shared listing shape: \`listed_at\` and \`pubDate\` from 情報公開日,\`heavy_food_ok\` and \`business_limit\` from the published notice flags (「飲食店不可」 etc.), \`fixtures_transfer_jpy\` from 造作譲渡,\`tags\` from the site's 特徴 list, and \`condition\` / \`prev_business\` from the 店舗プラス block (\`isInuki\` / \`isSkeleton\` / \`lastTenanto\`) rather than guessed from prose — though most listings leave those two unset. \`deposit_months\` prefers 保証金 and falls back to 敷金. + +\`raw\` additionally carries what the shared contract has no field for: \`lat\` / \`lng\`, the full 所在地 including its 都道府県,建物名 + 部屋番号,設備,築年月,敷引,償却,その他一時金 and the 定期借家 flag. + +| Query | Description | Default | +| ------- | --------------------------------------------------------- | ------- | +| \`limit\` | Listings to enrich (one detail page each, cached), max 30 | 10 |`, categories: ['other'], features: { requireConfig: false, From b811484c5bd1cc6b7a19bec00adc7482219cafc9 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:13:07 +0900 Subject: [PATCH 0861/1190] fix(route/sonomama): report the site's maintenance window explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 店舗そのままオークション takes itself down daily — published as 03:00–06:30 JST — and redirects every page to a notice. The route parsed that notice as a listing page, found no cards, and yielded an empty feed, which RSSHub then reported as "this route is empty". That wording is misleading in a way that matters to anything counting 新着 over time: an empty feed is indistinguishable from "no new listings", so a daily three-and-a-half hour outage would read as a genuine run of zeros rather than as missing data. Detect the notice and throw instead, so the failure is a failure. Verified against the live site inside the window: the route now answers "sonomama: the site is under maintenance (published daily 03:00-06:30 JST)" rather than an empty feed. --- lib/routes/sonomama/property.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/routes/sonomama/property.ts b/lib/routes/sonomama/property.ts index 4e27a84eb340..a5233eff9712 100644 --- a/lib/routes/sonomama/property.ts +++ b/lib/routes/sonomama/property.ts @@ -179,7 +179,14 @@ export const handler = async (ctx): Promise => { ? `${HOST}/app/?action=public_property_list_search&view=1&page_index=0&page_num=${PAGE_SIZE}&sort_id=0&sort_type=1` : `${HOST}/app/?action=public_property_list_search&Btn_start.x=1&Btn_start.y=1&pref%5B%5D=${prefCode}`; - const cards = parseList(await ofetch(listUrl)).slice(0, limit); + const html: string = await ofetch(listUrl, { responseType: 'text' }); + // The site takes itself down daily for maintenance (published as 03:00–06:30 JST) and redirects every + // page to a notice. Failing loudly matters here: an empty feed is indistinguishable from "no new + // listings", which would read as a genuine zero in anything counting 新着 over time. + if (/メンテナンス中/.test(html)) { + throw new Error('sonomama: the site is under maintenance (published daily 03:00–06:30 JST); no listings could be read'); + } + const cards = parseList(html).slice(0, limit); const items = await pMap( cards, (card) => From afc335e9cbbb6a75a25242a6fc76f5d562d999b3 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:20:22 +0900 Subject: [PATCH 0862/1190] feat(route/i-tenpo): add a single-listing detail route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ward routes cannot reach the 丁目: 居抜き店舗.com truncates its 所在地 field to the 町 on the list and on the detail page alike ('東京都新宿区高田馬場 詳細はログイン 後に表示'). The 丁目 is published in exactly one place — the document title, '新宿区高田馬場2丁目/高田馬場駅徒歩2分/…' — so this route reads the address from there and falls back to the 所在地 field when the title does not parse as one. A per-listing route rather than detail fetching inside the ward route: a consumer that needs the 丁目 for a handful of listings can ask for exactly those, instead of the ward route paying a page load per listing on every poll. A listing's 賃料, 引渡状態 and availability all change over its life, so following one is a real feed rather than a lookup dressed as one. 敷金 and 礼金 stay null, being members-only here as on the list. The site publishes 更新日 but no 掲載日, so listed_at is a last-modified date and the description says so rather than letting it be read as a publication date. Verified: /i-tenpo/detail/99523 yields address_hint 新宿区高田馬場2丁目 with rent, 坪, ㎡, floor, line, station, walk, 造作価格 and 引渡状態 all populated; an unknown id fails with the site's 404 rather than an empty feed. --- lib/routes/i-tenpo/detail.ts | 164 +++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 lib/routes/i-tenpo/detail.ts diff --git a/lib/routes/i-tenpo/detail.ts b/lib/routes/i-tenpo/detail.ts new file mode 100644 index 000000000000..db0d0899aac3 --- /dev/null +++ b/lib/routes/i-tenpo/detail.ts @@ -0,0 +1,164 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const HOST = 'https://www.i-tenpo.com'; +/** 所在地 and the fee rows end with this members-only placeholder. */ +const LOGIN_NOTE = '詳細はログイン後に表示'; + +/** + * One listing, from `/t{id}`. + * + * The page's own 所在地 field stops at the 町 exactly as the list does ('東京都新宿区高田馬場 詳細はログイン後に表示'), + * and the 丁目 appears **only** in the document title, which reads + * '新宿区高田馬場2丁目/高田馬場駅徒歩2分/バー(BAR)/1階/10.15坪/居抜き・貸店舗(物件No…)の物件情報【居抜き店舗.com】'. + * The first `/`-separated segment is therefore the finest address the site publishes without a login. + */ +const parseDetail = (html: string, id: string): DataItem | null => { + const $ = load(html); + + const rows = new Map(); + const labels = $('[class*="__item__ttl"]').toArray(); + const values = $('[class*="__item__text"]').toArray(); + for (const [i, label] of labels.entries()) { + const key = clean($(label).text()); + const v = clean($(values[i]).text())?.replace(LOGIN_NOTE, '').trim(); + if (key !== null && !rows.has(key)) { + rows.set(key, v ?? ''); + } + } + const row = (label: string): string | null => clean(rows.get(label) ?? null); + + const title = clean($('meta[property="og:title"]').attr('content')) ?? clean($('title').text()); + if (title === null) { + return null; + } + // '新宿区高田馬場2丁目/高田馬場駅徒歩2分/…' — the address is the first segment. + const titleAddress = clean(title.split('/', 1)[0]); + const titleWard = parseWard(titleAddress); + const address = titleWard === null ? row('所在地') : titleAddress; + + // '更新日:2026年09月18日' sits in the page body rather than in a labelled row. + const updated = clean(/更新日[::]\s*(\d{4}年\d{1,2}月\d{1,2}日)/.exec($('body').text())?.[1] ?? null); + + const raw: ListingExtra['raw'] = { + station: row('最寄駅'), + rent: row('賃料'), + address: row('所在地'), + title_address: titleAddress, + building: row('建物名'), + fixtures: row('造作価格'), + floor: row('階層'), + area: row('面積'), + business: row('前業態') ?? row('現業態'), + handover: row('引渡状態'), + updated_at: updated, + }; + + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.area); + const stationLinks = $('[class*="__item__text"] a') + .toArray() + .map((a) => clean($(a).text())) + .filter((t): t is string => t !== null); + + const extra: ListingExtra = { + source: 'i-tenpo', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor?.replaceAll('地下', 'B') ?? null), + station: stationLinks[1]?.replace(/駅$/, '') ?? null, + line: stationLinks[0] ?? null, + walk_min: parseWalkMin(raw.station?.replaceAll(' ', '') ?? null), + // 敷金 / 礼金 are shown only to signed-in users, here as on the list page. + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: parseJpy(raw.fixtures), + condition: parseCondition(raw.handover, title), + prev_business: raw.business, + heavy_food_ok: null, + business_limit: null, + // The site publishes 更新日 but no 掲載日, so this is a last-modified date, not a first-listed one. + listed_at: parseYmd(updated), + ward: parseWard(address), + address_hint: address, + tags: [], + raw, + }; + + const link = `${HOST}/t${id}`; + return { + title: clean(title.split('の物件情報', 1)[0]) ?? title, + link, + guid: link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), + description: summarize(extra), + _extra: extra, + }; +}; + +export const handler = async (ctx): Promise => { + const id: string = ctx.req.param('id'); + if (!/^\d+$/.test(id)) { + throw new Error(`Invalid id "${id}", expected the numeric 物件 id from a /t{id} URL`); + } + const link = `${HOST}/t${id}`; + + const item = (await cache.tryGet(`i-tenpo:detail:${id}`, async () => { + const html: string = await ofetch(link, { responseType: 'text' }); + return parseDetail(html, id); + })) as DataItem | null; + + if (item === null) { + throw new Error(`i-tenpo: listing ${id} could not be read; it may have been delisted`); + } + + return { + title: `居抜き店舗.com ${item.title}`, + link, + language: 'ja', + item: [item], + }; +}; + +export const route: Route = { + path: '/detail/:id', + name: '物件詳細', + url: 'www.i-tenpo.com', + maintainers: ['pseudoyu'], + handler, + example: '/i-tenpo/detail/99523', + parameters: { + id: { + description: 'The numeric 物件 id, i.e. the `99523` in `https://www.i-tenpo.com/t99523`', + }, + }, + description: `One listing on 居抜き店舗.com, for following a single property rather than a whole ward — a listing's 賃料,引渡状態 and availability all change over its life. + +Its one advantage over the ward routes is the address. The page's own 所在地 field stops at the 町 exactly as the list does (\`東京都新宿区高田馬場 詳細はログイン後に表示\`), but the document title carries the 丁目 — \`新宿区高田馬場2丁目/高田馬場駅徒歩2分/…\` — so \`address_hint\` reaches the 丁目 here and only here. + +\`_extra\` follows the shared listing shape. 敷金 and 礼金 are shown only to signed-in users, here as on the list page, so they stay \`null\`. The site publishes 更新日 but no 掲載日,so \`listed_at\` and \`pubDate\` are a last-modified date rather than a first-listed one — do not read them as a publication date.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.i-tenpo.com/t:id'], + target: '/detail/:id', + }, + ], +}; From bff08c01b2ecc15a7916ee5f8145a3c67985fcfd Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:27:56 +0900 Subject: [PATCH 0863/1190] feat(route/bukenavi): add a single-listing detail route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds what the area route's cards omit: 乗降者数 for the nearest station, 構造, 竣工年月, 立地, 間口, 業種, 不可業態, 営業年数 and 特記事項, plus exact 面積 and 階数. It deliberately does not try to improve the address. ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせ ください(住所詳細は会員限定)」 — so the 丁目 is unreachable without an account and this route is no better than the area route on that field. The description says so rather than leaving a caller to discover it. 敷金, 礼金 and 造作 are absent from the guest view too, and the site publishes no listing date, so those stay null. Verified: /bukenavi/detail/54379 yields 東京都新宿区歌舞伎町, ¥1,419,000, 30坪 / 99.15㎡, 4F, 東京メトロ副都心線 東新宿 徒歩5分, 前の業態 クラブ, 乗降者数 139,516人/日, 竣工 1982年05月, 立地 オフィス街・繁華街・駅チカ. --- lib/routes/bukenavi/detail.ts | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 lib/routes/bukenavi/detail.ts diff --git a/lib/routes/bukenavi/detail.ts b/lib/routes/bukenavi/detail.ts new file mode 100644 index 000000000000..dc22f822431f --- /dev/null +++ b/lib/routes/bukenavi/detail.ts @@ -0,0 +1,163 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://bukenavi.jp'; +const REGIONS = new Set(['kanto', 'kansai', 'tokai', 'kyushu']); + +/** + * One listing, from `/{region}/object/{id}` — the id-only URL serves the same page as the station-named one. + * + * The address is deliberately **not** improved here: ぶけなび truncates 住所 to the 町 for guests and says so + * ('東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)'), so the 丁目 is unreachable without + * an account and this route cannot do better than the area route on that field. What it does add is everything + * the list card omits: 乗降者数, 構造, 竣工年月, 立地, 間口, 業種, 不可業態, 営業年数 and 特記事項. + */ +const parseDetail = (html: string, region: string, id: string): DataItem | null => { + const $ = load(html); + + const rows = new Map(); + for (const tr of $('table.box__property__table tr').toArray()) { + const key = clean($(tr).find('th').first().text()); + const value = clean($(tr).find('td').first().text()); + if (key !== null && !rows.has(key)) { + rows.set(key, value ?? ''); + } + } + const row = (label: string): string | null => { + const v = clean(rows.get(label) ?? null); + return v === null || /^[ー—–--]+$/.test(v) ? null : v; + }; + + const title = clean($('h1').first().text()) ?? clean($('title').text()); + if (title === null || rows.size === 0) { + return null; + } + + const raw: ListingExtra['raw'] = { + address: row('住所'), + rent: row('賃料'), + station: row('最寄り駅'), + line: row('沿線'), + size: row('面積'), + floor: row('階数'), + passengers: row('乗降者数'), + structure: row('構造'), + built: row('竣工年月'), + location_type: row('立地'), + frontage: row('間口'), + prev_business: row('前の業態'), + years_open: row('営業年数'), + business_types: row('業種'), + excluded_business: row('不可業態'), + notes: row('特記事項'), + }; + + // '1,419,000円 @4.73万円' — the 坪単価 follows the rent after an @. + const [rentText, unitText] = (raw.rent ?? '').split('@', 2); + const { tsubo, area_m2 } = parseArea(raw.size); + const rentJpy = parseJpy(clean(rentText)); + + const extra: ListingExtra = { + source: 'bukenavi', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: parseJpy(clean(unitText)) ?? tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor?.replaceAll('地下', 'B') ?? null), + station: clean(raw.station?.split('(', 1)[0] ?? null)?.replace(/駅$/, '') ?? null, + line: raw.line, + walk_min: parseWalkMin(raw.station), + // 敷金 / 礼金 are not published on the guest view. + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: null, + prev_business: raw.prev_business, + heavy_food_ok: null, + business_limit: [raw.business_types, raw.excluded_business === null ? null : `不可: ${raw.excluded_business}`].filter((p): p is string => p !== null).join(' / ') || null, + // The site publishes no listing date. + listed_at: null, + ward: parseWard(raw.address), + // Town-level only: the 丁目 is behind the site's own 会員限定 notice. + address_hint: raw.address?.split('※', 1)[0]?.trim() ?? null, + tags: [], + raw, + }; + + const link = `${HOST}/${region}/object/${id}`; + return { + title, + link, + guid: link, + description: [raw.notes, summarize(extra)].filter(Boolean).join(' / '), + _extra: extra, + }; +}; + +export const handler = async (ctx): Promise => { + const region: string = ctx.req.param('region') ?? 'kanto'; + const id: string = ctx.req.param('id'); + if (!REGIONS.has(region)) { + throw new Error(`Unknown region "${region}", expected one of ${[...REGIONS].join(', ')}`); + } + if (!/^\d+$/.test(id)) { + throw new Error(`Invalid id "${id}", expected the numeric 物件 id from a /object/{id} URL`); + } + const link = `${HOST}/${region}/object/${id}`; + + const item = (await cache.tryGet(`bukenavi:detail:${region}:${id}`, async () => parseDetail(await ofetch(link, { responseType: 'text' }), region, id))) as DataItem | null; + if (item === null) { + throw new Error(`bukenavi: listing ${id} could not be read; it may have been delisted`); + } + + return { + title: `ぶけなび ${item.title}`, + link, + language: 'ja', + item: [item], + }; +}; + +export const route: Route = { + path: '/detail/:id/:region?', + name: '物件詳細', + url: 'bukenavi.jp', + maintainers: ['pseudoyu'], + handler, + example: '/bukenavi/detail/54379', + parameters: { + id: { + description: 'The numeric 物件 id, i.e. the `54379` in `https://bukenavi.jp/kanto/object/54379`', + }, + region: { + description: 'Region the listing belongs to; defaults to `kanto`', + default: 'kanto', + options: [...REGIONS].map((r) => ({ value: r, label: r })), + }, + }, + description: `One listing on ぶけなび,for following a single property rather than a whole area — a listing's 賃料 and availability change over its life. + +It adds what the area route's cards omit: 乗降者数 for the nearest station, 構造,竣工年月,立地,間口,業種,不可業態,営業年数 and 特記事項,plus exact 面積 and 階数. + +**It does not improve the address.** ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」 — so the 丁目 is unreachable without an account, and \`address_hint\` stops at the 町 here exactly as it does on the area route. 敷金,礼金 and 造作 are likewise absent from the guest view, and the site publishes no listing date, so those fields stay \`null\`.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['bukenavi.jp/:region/object/:id'], + target: '/detail/:id/:region', + }, + ], +}; From 330107818142f5329c3c078db6f566f22807c94f Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:30:18 +0900 Subject: [PATCH 0864/1190] feat(route/abc-tenpo): add a single-listing detail route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The listing route can only reach the ward ('東京都文京区') because the visible 所在地 field is truncated and marked 会員限定. The document title, however, carries the address in full — '東京都文京区湯島2-31-17・都営大江戸線 本郷三丁目駅・…' — so a guest can read the 丁目 and the 番地 there and nowhere else on the page. That makes this one of the few sources publishing a 番地 at all, alongside iri-search, tenant-shop and canaeru. Two fields needed care. '現況' is not used as a fallback for 前業態: on this page that label also heads a tag-cloud block, and taking it yielded a run of unrelated navigation text rather than a business type. The station cell reads '都営大江戸線 本郷三丁目駅 徒歩5分', so the 駅-suffixed token is the station and the first token the line, instead of treating the whole string as a station name. 保証金, 礼金 and 造作譲渡料 are members-only here and stay null, and the detail page publishes no listing date, so listed_at is null too. Verified: /abc-tenpo/detail/62829 yields 東京都文京区湯島2-31-17, ¥420,000, 52.89㎡, 1F, 都営大江戸線 本郷三丁目 徒歩5分, 居抜き. --- lib/routes/abc-tenpo/detail.ts | 152 +++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 lib/routes/abc-tenpo/detail.ts diff --git a/lib/routes/abc-tenpo/detail.ts b/lib/routes/abc-tenpo/detail.ts new file mode 100644 index 000000000000..b212d681bbe4 --- /dev/null +++ b/lib/routes/abc-tenpo/detail.ts @@ -0,0 +1,152 @@ +import { load } from 'cheerio'; + +import type { ListingExtra } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://www.abc-tenpo.com'; + +/** + * One listing, from `/property/view/{id}`. + * + * The visible 所在地 field is truncated and marked 会員限定, but the document title carries the address in + * full — '東京都文京区湯島2-31-17・都営大江戸線 本郷三丁目駅・中華料理店|…' — so this is the one place the + * site publishes the 丁目 **and the 番地** to a guest. The first `・`-separated segment is that address. + */ +const parseDetail = (html: string, id: string): DataItem | null => { + const $ = load(html); + + const rows = new Map(); + for (const el of $('th, dt').toArray()) { + const key = clean($(el).text()); + const value = clean($(el).next('td, dd').text()); + if (key !== null && !rows.has(key)) { + rows.set(key, value ?? ''); + } + } + const row = (...labels: string[]): string | null => { + for (const label of labels) { + const v = clean(rows.get(label) ?? null); + if (v !== null && !/^[ー—–--]+$/.test(v)) { + return v; + } + } + return null; + }; + + const ogTitle = clean($('meta[property="og:title"]').attr('content')) ?? clean($('title').text()); + if (ogTitle === null) { + return null; + } + // '東京都文京区湯島2-31-17・都営大江戸線 本郷三丁目駅・中華料理店|…' — the address is the first segment. + const titleAddress = clean(ogTitle.split('・', 1)[0]); + const address = parseWard(titleAddress) === null ? row('所在地', '住所') : titleAddress; + + const raw: ListingExtra['raw'] = { + title_address: titleAddress, + address: row('所在地', '住所'), + rent: row('賃料'), + station: row('最寄駅', '最寄り駅', '交通'), + size: row('面積'), + floor: row('階数', '階層'), + // '現況' is not used as a fallback: on this page that label also heads a tag-cloud block. + prev_business: row('前業態'), + handover: row('引渡し', '引渡状態', '現状'), + notes: row('備考', '特記事項'), + }; + + const rentJpy = parseJpy(raw.rent); + const { tsubo, area_m2 } = parseArea(raw.size); + + const extra: ListingExtra = { + source: 'abc-tenpo', + listing_id: id, + rent_jpy: rentJpy, + tsubo, + area_m2, + tsubo_unit_jpy: tsuboUnit(rentJpy, tsubo), + floor: normalizeFloor(raw.floor?.replaceAll('地下', 'B') ?? null), + // '都営大江戸線 本郷三丁目駅 徒歩5分' — the 駅-suffixed token is the station, the first one the line. + station: clean(raw.station?.split(/\s+/).find((t) => t.endsWith('駅')) ?? null)?.replace(/駅$/, '') ?? null, + line: clean(raw.station?.split(/\s+/, 1)[0] ?? null), + walk_min: parseWalkMin(raw.station), + // 保証金, 礼金 and 造作譲渡料 are members-only on this site. + deposit_months: null, + deposit_jpy: null, + key_money_months: null, + fixtures_transfer_jpy: null, + condition: parseCondition(raw.handover, ogTitle), + prev_business: raw.prev_business, + heavy_food_ok: null, + business_limit: null, + // The site publishes no listing date on the detail page. + listed_at: null, + ward: parseWard(address), + address_hint: address, + tags: [], + raw, + }; + + const link = `${HOST}/property/view/${id}`; + return { + title: clean(ogTitle.split('|', 1)[0]) ?? ogTitle, + link, + guid: link, + description: [raw.notes, summarize(extra)].filter(Boolean).join(' / '), + _extra: extra, + }; +}; + +export const handler = async (ctx): Promise => { + const id: string = ctx.req.param('id'); + if (!/^\d+$/.test(id)) { + throw new Error(`Invalid id "${id}", expected the numeric 物件 id from a /property/view/{id} URL`); + } + const link = `${HOST}/property/view/${id}`; + + const item = (await cache.tryGet(`abc-tenpo:detail:${id}`, async () => parseDetail(await ofetch(link, { responseType: 'text' }), id))) as DataItem | null; + if (item === null) { + throw new Error(`abc-tenpo: listing ${id} could not be read; it may have been delisted`); + } + + return { + title: `ABC 店舗 ${item.title}`, + link, + language: 'ja', + item: [item], + }; +}; + +export const route: Route = { + path: '/detail/:id', + name: '物件詳細', + url: 'www.abc-tenpo.com', + maintainers: ['pseudoyu'], + handler, + example: '/abc-tenpo/detail/62829', + parameters: { + id: { + description: 'The numeric 物件 id, i.e. the `62829` in `https://www.abc-tenpo.com/property/view/62829`', + }, + }, + description: `One listing on ABC 店舗,for following a single property rather than a whole prefecture. + +Its reason to exist is the address. The listing route can only reach the ward (\`東京都文京区\`) because the visible 所在地 field is truncated and marked 会員限定 — but the document title carries the address in full, \`東京都文京区湯島2-31-17・…\`, so \`address_hint\` here reaches the 丁目 **and the 番地**. That makes this one of the few sources that publishes a 番地 to a guest at all. + +\`_extra\` follows the shared listing shape. 保証金,礼金 and 造作譲渡料 are members-only on this site and stay \`null\`, and the detail page publishes no listing date, so \`listed_at\` is \`null\` too.`, + categories: ['other'], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportRadar: true, + }, + radar: [ + { + source: ['www.abc-tenpo.com/property/view/:id'], + target: '/detail/:id', + }, + ], +}; From 8a580b9fbef782429bb1266ce09cae00bff81a55 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:55:46 +0900 Subject: [PATCH 0865/1190] =?UTF-8?q?fix(route/abc-tenpo):=20read=20?= =?UTF-8?q?=E7=8F=BE=E6=A5=AD=E6=85=8B,=20=E6=A5=AD=E7=A8=AE=E5=88=B6?= =?UTF-8?q?=E9=99=90=20and=20=E6=83=85=E5=A0=B1=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=20on=20the=20detail=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail route was reading four fields as null that the page does publish, and said so in a comment that was simply wrong — the listing route already reads the same page (/property/view/{id}) for them: - 前業態 is not a label this site uses; it is 現業態, so prev_business was always null - 業種制限 and 飲食条件 were never read, so business_limit was always null - 情報更新日 sits in .p-property-updated-date__def, so listed_at was always null (the comment claimed the detail page publishes no date at all) - the 居抜き / 重飲食可 tags were never read, so tags was always empty listed_at now also drives pubDate. It is a last-modified date, not a first-listed one, and the description says so. 構造, 階建, 席数 and 初期費用 are kept in raw. Verified against /property/view/62829: prev_business 中華料理店, listed_at 2026-09-18, tags [居抜き]. heavy_food_ok stays null there because the page says 重飲食相談可能 — 相談 is not a yes. --- lib/routes/abc-tenpo/detail.ts | 44 +++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/routes/abc-tenpo/detail.ts b/lib/routes/abc-tenpo/detail.ts index b212d681bbe4..0fde051ac896 100644 --- a/lib/routes/abc-tenpo/detail.ts +++ b/lib/routes/abc-tenpo/detail.ts @@ -1,10 +1,12 @@ import { load } from 'cheerio'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, parseYmd, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; const HOST = 'https://www.abc-tenpo.com'; @@ -44,19 +46,38 @@ const parseDetail = (html: string, id: string): DataItem | null => { const titleAddress = clean(ogTitle.split('・', 1)[0]); const address = parseWard(titleAddress) === null ? row('所在地', '住所') : titleAddress; + const tags = $('.c-tag-property__item') + .toArray() + .map((t) => clean($(t).text())) + .filter((t): t is string => t !== null); + // The page carries two `.p-property-updated-date__def` blocks — 情報更新日 first, 次回情報更新日 second. + const updatedAt = clean($('.p-property-updated-date__def dd').first().text()); + const raw: ListingExtra['raw'] = { title_address: titleAddress, address: row('所在地', '住所'), rent: row('賃料'), + initial_cost: row('初期費用'), station: row('最寄駅', '最寄り駅', '交通'), size: row('面積'), - floor: row('階数', '階層'), - // '現況' is not used as a fallback: on this page that label also heads a tag-cloud block. - prev_business: row('前業態'), - handover: row('引渡し', '引渡状態', '現状'), + floor: row('階層', '階数'), + floors: row('階建'), + structure: row('構造'), + seats: row('席数'), + // '現業態' is the label this site uses; '現況' is not a fallback because it also heads a tag-cloud block. + prev_business: row('現業態', '前業態'), + handover: row('引渡状態', '引渡し', '現状'), + business_limit: row('業種制限'), + food_condition: row('飲食条件'), notes: row('備考', '特記事項'), + updated_at: updatedAt, }; + // '無し' is how the site says there is no restriction, so it is not carried as one. + const limitParts = [raw.business_limit === null || /^(?:無し|なし|[-ー])$/.test(raw.business_limit) ? null : raw.business_limit, raw.food_condition === null ? null : `飲食条件: ${raw.food_condition}`].filter( + (p): p is string => p !== null + ); + const rentJpy = parseJpy(raw.rent); const { tsubo, area_m2 } = parseArea(raw.size); @@ -79,13 +100,13 @@ const parseDetail = (html: string, id: string): DataItem | null => { fixtures_transfer_jpy: null, condition: parseCondition(raw.handover, ogTitle), prev_business: raw.prev_business, - heavy_food_ok: null, - business_limit: null, - // The site publishes no listing date on the detail page. - listed_at: null, + heavy_food_ok: tags.includes('重飲食可') ? true : parseHeavyFood(raw.business_limit, raw.food_condition), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, + // 情報更新日 is the only date the site publishes, so this is a last-modified date, not a first-listed one. + listed_at: parseYmd(updatedAt), ward: parseWard(address), address_hint: address, - tags: [], + tags, raw, }; @@ -94,6 +115,7 @@ const parseDetail = (html: string, id: string): DataItem | null => { title: clean(ogTitle.split('|', 1)[0]) ?? ogTitle, link, guid: link, + pubDate: extra.listed_at === null ? undefined : timezone(parseDate(extra.listed_at, 'YYYY-MM-DD'), 9), description: [raw.notes, summarize(extra)].filter(Boolean).join(' / '), _extra: extra, }; @@ -135,7 +157,7 @@ export const route: Route = { Its reason to exist is the address. The listing route can only reach the ward (\`東京都文京区\`) because the visible 所在地 field is truncated and marked 会員限定 — but the document title carries the address in full, \`東京都文京区湯島2-31-17・…\`, so \`address_hint\` here reaches the 丁目 **and the 番地**. That makes this one of the few sources that publishes a 番地 to a guest at all. -\`_extra\` follows the shared listing shape. 保証金,礼金 and 造作譲渡料 are members-only on this site and stay \`null\`, and the detail page publishes no listing date, so \`listed_at\` is \`null\` too.`, +\`_extra\` follows the shared listing shape, including 現業態,業種制限,飲食条件 and the 居抜き / 重飲食可 tags; 構造,階建,席数 and 初期費用 are kept in \`raw\`. 保証金,礼金 and 造作譲渡料 are members-only on this site and stay \`null\`. The only date the site publishes is 情報更新日,so \`listed_at\` and \`pubDate\` are a last-modified date rather than a first-listed one — do not read them as a publication date.`, categories: ['other'], features: { requireConfig: false, From 70f15795bdd0059f2f0d018fd20b2aa5cdb6175b Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:55:59 +0900 Subject: [PATCH 0866/1190] =?UTF-8?q?fix(route/bukenavi):=20align=20the=20?= =?UTF-8?q?detail=20route's=20=E7=8F=BE=E6=B3=81=20and=20=E6=A5=AD?= =?UTF-8?q?=E7=A8=AE=E5=88=B6=E9=99=90=20with=20the=20area=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two bukenavi routes describe the same listings but disagreed on three fields, so a listing read through /detail looked different from the same listing read through /object: - condition was hardcoded null although the title carries 居抜き / スケルトン, which the area route already reads with parseCondition - heavy_food_ok was hardcoded null although 不可業態 is parsed into raw; the area route derives it from that same list - business_limit joined 業種 without the 可: prefix the area route uses, so the two routes emitted different strings for identical data Verified against /kanto/object/54379: condition now inuki, business_limit now 可: バー、クラブ・キャバクラ. --- lib/routes/bukenavi/detail.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/routes/bukenavi/detail.ts b/lib/routes/bukenavi/detail.ts index dc22f822431f..e1ec9d3d9ff4 100644 --- a/lib/routes/bukenavi/detail.ts +++ b/lib/routes/bukenavi/detail.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; @@ -59,6 +59,8 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null // '1,419,000円 @4.73万円' — the 坪単価 follows the rent after an @. const [rentText, unitText] = (raw.rent ?? '').split('@', 2); + // Same shape as the area route, so the two bukenavi routes stay comparable. + const limitParts = [raw.business_types === null ? null : `可: ${raw.business_types}`, raw.excluded_business === null ? null : `不可: ${raw.excluded_business}`].filter((p): p is string => p !== null); const { tsubo, area_m2 } = parseArea(raw.size); const rentJpy = parseJpy(clean(rentText)); @@ -78,10 +80,11 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null deposit_jpy: null, key_money_months: null, fixtures_transfer_jpy: null, - condition: null, + condition: parseCondition(title), prev_business: raw.prev_business, - heavy_food_ok: null, - business_limit: [raw.business_types, raw.excluded_business === null ? null : `不可: ${raw.excluded_business}`].filter((p): p is string => p !== null).join(' / ') || null, + // As on the area route: the site states 不可業態 rather than 重飲食可否, so this is read off that list. + heavy_food_ok: raw.excluded_business === null ? null : !/飲食/.test(raw.excluded_business), + business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, // The site publishes no listing date. listed_at: null, ward: parseWard(raw.address), From 45ecd62e661848901f67074e0a44da68d5979fb7 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:56:13 +0900 Subject: [PATCH 0867/1190] =?UTF-8?q?fix(route/temponw):=20drop=20a=20dead?= =?UTF-8?q?=20=E9=81=A9=E7=94=A8=E6=A5=AD=E7=A8=AE=20selector,=20read=20?= =?UTF-8?q?=E7=8F=BE=E6=B3=81=20from=20the=20blurb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detail parser scraped .applicable-industry-items > .grey-div for the 適用業種 / 特長 / 立地 blocks. Neither class exists on the site any more, so the extraction silently produced an empty array and every field derived from it — condition, heavy_food_ok, business_limit and tags — was permanently null across the whole feed, with nothing in raw to show the parse had failed. Checked all 20 listings of both the Tokyo and the nationwide feed: no listing has those classes, and the words 業種, 居抜き and スケルトン appear only in the page's meta tags and footer boilerplate, never in listing content. The block is gone, not moved, so the code goes with it rather than being repointed. 現況 and 造作譲渡料 are real fields that the site publishes as '-' throughout, so the PR blurb is now the only place condition can come from; that is 1 of 20 listings, but a real 1 rather than a silent 0. business_limit keeps reading 不可業態 out of 備考, whose selector does still work. --- lib/routes/temponw/estates.ts | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/routes/temponw/estates.ts b/lib/routes/temponw/estates.ts index 9e29b9898814..ce1db0f64b6d 100644 --- a/lib/routes/temponw/estates.ts +++ b/lib/routes/temponw/estates.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import pMap from 'p-map'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -30,7 +30,6 @@ interface DetailFields { status: string | null; // 現況 '空' contract: string | null; // 契約形態 '普通借家' term: string | null; // 契約期間 '3年' - industry_blocks: string[][]; // [適用業種, 特長, 立地] notes: string[]; // 備考 '不可業態: 重飲食不可/24時間営業不可' } @@ -136,15 +135,6 @@ const parseDetail = (html: string): DetailFields => { status: field('現況'), contract: field('契約形態'), term: field('契約期間'), - industry_blocks: $('.applicable-industry-items') - .toArray() - .map((block) => - $(block) - .find('.grey-div p') - .toArray() - .map((p) => clean($(p).text())) - .filter((t): t is string => t !== null) - ), notes: $('.detail-note li') .toArray() .map((li) => clean($(li).text())) @@ -153,17 +143,16 @@ const parseDetail = (html: string): DetailFields => { }; const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { - const [applicable = [], features = [], location = []] = d.industry_blocks; const restriction = clean(d.notes.find((n) => n.includes('不可業態'))?.replace(/^不可業態[::]\s*/, '')); - const limitParts = [applicable.length > 0 ? `可: ${applicable.join('、')}` : null, restriction === null ? null : `不可: ${restriction}`].filter((p): p is string => p !== null); return { ...base, walk_min: parseWalkMin(d.access), fixtures_transfer_jpy: parseJpy(d.fixtures), - condition: features.includes('スケルトン') ? 'skeleton' : base.condition, - heavy_food_ok: parseHeavyFood(restriction) ?? (applicable.length > 0 ? applicable.includes('重飲食') : null), - business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, - tags: [...features, ...location], + // 現況 is a real field but is published as '-' throughout, so the PR blurb is the only place + // this site says 居抜き or スケルトン at all. + condition: parseCondition(d.status, base.raw.comment), + heavy_food_ok: parseHeavyFood(restriction), + business_limit: restriction === null ? null : `不可: ${restriction}`, raw: { ...base.raw, access: d.access, fixtures: d.fixtures, status: d.status, contract: d.contract, term: d.term, notes: d.notes.join(' / ') || null }, }; }; From 747100c511eef92d88587db96d8fc5b2950ecaca Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 06:56:24 +0900 Subject: [PATCH 0868/1190] =?UTF-8?q?fix(route/tenant-shop):=20read=20?= =?UTF-8?q?=E5=B1=85=E6=8A=9C=E3=81=8D=20/=20=E3=82=B9=E3=82=B1=E3=83=AB?= =?UTF-8?q?=E3=83=88=E3=83=B3=20from=20the=20blurb=20and=20=E5=82=99?= =?UTF-8?q?=E8=80=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit condition was taken only from the detail page's 居抜き facility tag, which the site sets on some listings but not all. The rest say it in the PR blurb or 備考 instead — '倉庫居抜です', 'スケルトン渡し', '新築、飲食店可、スケルトン' — and those listings came through with condition null. Feeding both into parseCondition, as the other listing routes already do with their titles, takes condition from 7 of 20 to 13 of 20 on /chintai/tokyo/food. The facility tag still wins when it is present. --- lib/routes/tenant-shop/chintai.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/routes/tenant-shop/chintai.ts b/lib/routes/tenant-shop/chintai.ts index 5bb13412c910..633e49416394 100644 --- a/lib/routes/tenant-shop/chintai.ts +++ b/lib/routes/tenant-shop/chintai.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import pMap from 'p-map'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseJpy, parseMonths, parseWalkMin, parseWard, parseYmd, sumKnown, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseMonths, parseWalkMin, parseWard, parseYmd, sumKnown, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -169,7 +169,9 @@ const parseDetail = (html: string): DetailFields => { const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => ({ ...base, - condition: d.facilities.includes('居抜き') ? 'inuki' : base.condition, + // The 居抜き facility tag is set on only some listings; for the rest the PR blurb and 備考 are + // where the site says 居抜き or スケルトン. + condition: d.facilities.includes('居抜き') ? 'inuki' : parseCondition(base.raw.comment, d.remarks), listed_at: parseYmd(d.registered_at), tags: [...base.tags, ...d.facilities], raw: { ...base.raw, status: d.status, registered_at: d.registered_at, updated_at: d.updated_at, remarks: d.remarks }, From b9be3621e4ac128dcd6a620ff90a916df4662330 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 07:08:40 +0900 Subject: [PATCH 0869/1190] =?UTF-8?q?fix(route/bukenavi):=20only=20assert?= =?UTF-8?q?=20=E9=87=8D=E9=A3=B2=E9=A3=9F=E5=8F=AF=20when=20the=20site=20s?= =?UTF-8?q?ays=20so?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit heavy_food_ok was derived as "不可業態 does not mention 飲食", which inverts the answer whenever the site bans heavy food without using that word. Measured over /object/kanto/tokyo, 2 of 10 listings were affected and both were wrong: 不可業態 '中華・焼肉・焼き鳥・油の多い業態不可' -> heavy_food_ok true 不可業態 '風営法不可' -> heavy_food_ok true The first is a heavy-food ban written out in full; the second is about licensing and says nothing on the subject. Both now read null. parseHeavyFood, already used by the other listing routes, replaces the heuristic: true only on an explicit 重飲食可, false only on an explicit 重飲食不可 / 飲食不可, null otherwise. Null is the honest answer when the site did not say, and no consumer treats it as false. The original 不可業態 text stays in raw either way. --- lib/routes/bukenavi/object.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/routes/bukenavi/object.ts b/lib/routes/bukenavi/object.ts index 533b0dda8933..fc8ff08f6676 100644 --- a/lib/routes/bukenavi/object.ts +++ b/lib/routes/bukenavi/object.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import pMap from 'p-map'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -143,7 +143,10 @@ const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { area_m2: area_m2 ?? base.area_m2, line: base.line ?? d.line, prev_business: d.prev_business, - heavy_food_ok: d.ng_business === null ? null : !/飲食/.test(d.ng_business), + // Only an explicit 重飲食可 / 不可 counts. An NG list that does not use the word 飲食 is not a + // yes: '中華・焼肉・焼き鳥・油の多い業態不可' is a heavy-food ban written out in full, and + // '風営法不可' is about something else entirely. Both used to come through as `true`. + heavy_food_ok: parseHeavyFood(d.ng_business, d.business_types), business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, ward: parseWard(address), address_hint: address, From 4297dc0b7df13f6d501a7fea320fab0fdbb1ffaa Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 07:08:53 +0900 Subject: [PATCH 0870/1190] =?UTF-8?q?fix(route/bukenavi):=20only=20assert?= =?UTF-8?q?=20=E9=87=8D=E9=A3=B2=E9=A3=9F=E5=8F=AF=20on=20the=20detail=20r?= =?UTF-8?q?oute=20too?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same correction as the area route, kept in step so the two bukenavi routes do not disagree about a listing: heavy_food_ok now comes from parseHeavyFood over 不可業態 and 業種, so it is true only on an explicit 重飲食可 and null when the site's NG list simply does not use the word. --- lib/routes/bukenavi/detail.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/routes/bukenavi/detail.ts b/lib/routes/bukenavi/detail.ts index e1ec9d3d9ff4..7f9701483863 100644 --- a/lib/routes/bukenavi/detail.ts +++ b/lib/routes/bukenavi/detail.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; @@ -82,8 +82,9 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null fixtures_transfer_jpy: null, condition: parseCondition(title), prev_business: raw.prev_business, - // As on the area route: the site states 不可業態 rather than 重飲食可否, so this is read off that list. - heavy_food_ok: raw.excluded_business === null ? null : !/飲食/.test(raw.excluded_business), + // As on the area route: only an explicit 重飲食可 / 不可 counts, never the absence of 飲食 from + // the NG list, which says nothing about heavy food either way. + heavy_food_ok: parseHeavyFood(raw.excluded_business, raw.business_types), business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, // The site publishes no listing date. listed_at: null, From ecfb246771cce147d5826a7b029d47902fad7e62 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 07:13:55 +0900 Subject: [PATCH 0871/1190] =?UTF-8?q?fix(route/sonomama):=20stop=20reporti?= =?UTF-8?q?ng=20=E5=B1=85=E6=8A=9C=E3=81=8D=20for=20listings=20that=20neve?= =?UTF-8?q?r=20said=20so?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit condition was `isSkeleton ? 'skeleton' : 'inuki'`, so every listing whose 業態 was not the literal スケルトン was reported as 居抜き — including ones classified 'サービス業 / 美容室・理容室' and 'その他 / その他'. The site never said 居抜き for those; the route inferred it from the site's business taxonomy, which says nothing about the handover state. Measured over /property/tokyo, 20 listings: 4 of them were asserting 居抜き with no basis. They now read null. The remaining 13 keep inuki because the title says so in the site's own words ('1階肉まん店居抜き物件☆'), and the 3 スケルトン ones are unchanged. The detail page also has a 店舗の状態 row, which is now read as a further source and kept in raw; it is '−' on every listing checked, so the title is doing the work today, but the field is there when the site fills it in. Only explicit wording counts either way — a missing 居抜き is reported as unknown, never as スケルトン. --- lib/routes/sonomama/property.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/routes/sonomama/property.ts b/lib/routes/sonomama/property.ts index a5233eff9712..29057812b5ad 100644 --- a/lib/routes/sonomama/property.ts +++ b/lib/routes/sonomama/property.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import pMap from 'p-map'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -41,6 +41,7 @@ interface DetailFields { area: string | null; // 建坪数(店舗坪数) '14.97坪 (49.5平米)' key_money: string | null; // 礼金 business_limit: string | null; // 業種制限 + shop_status: string | null; // 店舗の状態(現況); '−' on most listings description: string | null; // 出展タイトル / 物件説明 } @@ -106,7 +107,10 @@ const parseList = (html: string): ListCard[] => { deposit_jpy: /[万円]/.test(raw.bond ?? '') ? parseJpy(raw.bond) : null, key_money_months: null, fixtures_transfer_jpy: parseJpy(raw.fixtures), - condition: isSkeleton ? 'skeleton' : 'inuki', + // Only the site's own words count. 業態 '飲食店' or 'その他' says nothing about the + // handover state, so anything but an explicit スケルトン is read off the title, + // which is where this site writes 居抜き ('1階肉まん店居抜き物件☆'). + condition: isSkeleton ? 'skeleton' : parseCondition(title), prev_business: isSkeleton ? null : (subCategory ?? null), heavy_food_ok: isSkeleton ? parseHeavyFood(subCategory ?? null) : null, business_limit: null, @@ -137,6 +141,7 @@ const parseDetail = (html: string): DetailFields => { area: cell('建坪数(店舗坪数)'), key_money: cell('礼金'), business_limit: cell('業種制限'), + shop_status: cell('店舗の状態'), description: clean($('div.detail_body div.shop_text p').first().text()), }; }; @@ -149,10 +154,11 @@ const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { line: clean(d.access?.split('(最寄駅)', 1)[0]?.replace('(沿線)', '')), walk_min: parseWalkMin(d.access), key_money_months: parseMonths(d.key_money), + condition: base.condition ?? parseCondition(d.shop_status), business_limit: d.business_limit, address_hint: d.address ?? base.address_hint, ward: parseWard(d.address) ?? base.ward, - raw: { ...base.raw, address: d.address, access: d.access, area_detail: d.area, key_money: d.key_money, business_limit: d.business_limit, description: d.description }, + raw: { ...base.raw, address: d.address, access: d.access, area_detail: d.area, key_money: d.key_money, business_limit: d.business_limit, shop_status: d.shop_status, description: d.description }, }; }; From 479ee33650772b8a860d0b3f9083dd34b4fdc067 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 07:30:35 +0900 Subject: [PATCH 0872/1190] fix(route/athome): bound the browser waits, add detail=0, degrade instead of failing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route could not finish a cold run and blamed the wrong thing when it failed. Measured against the live site. A plain HTTP client is served the real page for about four requests and then gets the 認証中 interstitial on everything after that; pacing 3s apart does not lift it, and no cookie is involved. A browser is served normally throughout — 8 consecutive detail navigations, no interstitial. So the Playwright dependency is not removable, and this commit stops pretending the problem is anything else. Three faults, all of which showed up as one misleading error: - The initial navigation used Playwright's default 30s timeout, and the interstitial calls window.location.reload(true), so a `domcontentloaded` goto on a blocked client never settles and dies at ~31s. Raising RSSHub's own REQUEST_TIMEOUT cannot help, because the ceiling was never RSSHub's. The page is now opened with `commit` and every wait is bounded here, inside the 30s default request budget. - Detail enrichment ran unbounded: one full browser navigation per listing, ~0.7s locally but several seconds on a modest VPS, so a cold cache at limit=30 could not finish. It now runs against a budget measured from the start of the request; listings past it keep their list-page fields instead of failing the feed. A cache hit needs no navigation and never draws on the budget, so a warm poll still returns everything enriched. - Every detail failure was reported as 'the listing page did not settle', which named the interstitial even when the list page had loaded perfectly. Only the list page is fatal now, and the message distinguishes a rate-limited client from a merely slow one. Adds `detail=0`, which skips the per-listing visits. The list payload already carries 所在地 to the 丁目, 階, 面積, 賃料 and the ward, so consumers that do not need 情報公開日 or the coordinates can skip 30 page loads per poll. Measured 丁目 coverage from the list page alone: 港区 30/30, 横浜市中区 30/30, 新宿区 19/30 — the 新宿区 misses are addresses such as 新宿区築地町 that have no 丁目 to publish. Verified on /rent-store/tokyo/shinjuku-city: enriched limit=3 in 4.4s; detail=0 limit=30 in 2.3s with ward 30/30; enriched limit=30 in 22.8s returning all 30 items with 27 enriched and 3 degraded rather than the run failing. --- lib/routes/athome/rent-store.ts | 110 +++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/lib/routes/athome/rent-store.ts b/lib/routes/athome/rent-store.ts index 24994fbb079d..d069bd35223f 100644 --- a/lib/routes/athome/rent-store.ts +++ b/lib/routes/athome/rent-store.ts @@ -14,9 +14,23 @@ const SLUG = /^[a-z\d_-]+$/; /** The SSR state is keyed by the internal BFF call that produced it. */ const LIST_KEY = '/bukken/list/first-view'; const DETAIL_KEY = '/bukken/detail/'; -const STATE_TIMEOUT = 45000; +/** + * Bounded so the whole run fits inside RSSHub's 30s default request timeout. The interstitial reloads + * itself (`window.location.reload(true)`), which keeps a `domcontentloaded` navigation from ever + * settling, so the page is opened with `commit` and the waiting is done here where it can be reported. + */ +const NAV_TIMEOUT = 12000; +const STATE_TIMEOUT = 12000; const DEFAULT_LIMIT = 10; const PAGE_SIZE = 30; +/** + * Each detail page is a full browser navigation — ~0.7s on a developer machine but ~4.7s on a modest + * VPS — so a cold cache at the default limit can outlast RSSHub's own 30s request timeout. Enrichment + * runs against this budget and falls back to list-page fields instead of failing the whole feed. + */ +const DETAIL_BUDGET_MS = 20000; +/** Whole-run ceiling for enrichment, measured from the start of the request, not from the list page. */ +const TOTAL_BUDGET_MS = 22000; /** Fields the list payload does not carry; all of them live on the detail page's own SSR payload. */ interface Detail { @@ -241,38 +255,85 @@ export const handler = async (ctx): Promise => { } } const limit = Math.min(ctx.req.query('limit') ? Number(ctx.req.query('limit')) : DEFAULT_LIMIT, PAGE_SIZE); + // 所在地 (to the 丁目), 階, 面積, 賃料 and the ward all come from the list page. The detail visit only + // adds 情報公開日, the coordinates and the fee detail, so a consumer that needs none of those can + // skip it and make this an ordinary fast route. + const wantDetail = ctx.req.query('detail') !== '0'; const listUrl = `${HOST}/rent_store/${pref}/${city}/list/`; // One browser for the whole run: the list page, then each detail page by navigating the same tab. - // Opening a browser per listing would be an order of magnitude more expensive. - const { page, destroy } = await getPlaywrightPage(listUrl, { gotoConfig: { waitUntil: 'domcontentloaded' } }); + // Opening a browser per listing would be an order of magnitude more expensive. The browser is not + // optional — the site answers a plain HTTP client for about four requests and then serves it the + // 認証中 interstitial indefinitely, while a real browser keeps being served normally. + const startedAt = Date.now(); + const { page, destroy } = await getPlaywrightPage(listUrl, { gotoConfig: { waitUntil: 'commit', timeout: NAV_TIMEOUT } }); const items: DataItem[] = []; try { - await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); - const bukken = listOf(await page.content()).slice(0, limit); + let bukken: Bukken[]; + try { + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + bukken = listOf(await page.content()).slice(0, limit); + } catch (error) { + // Only the list page is fatal: an empty feed is indistinguishable from "no new listings". + // Say which failure it was — a blocked client and a slow one need different responses. + let blocked = false; + try { + blocked = (await page.content()).includes('認証中'); + } catch { + // The page may already be gone; the message below still names the failure. + } + logger.warn(`athome: ${listUrl} failed after ${Date.now() - startedAt}ms (blocked=${blocked}): ${String(error)}`); + throw new Error( + blocked + ? 'athome: the site is serving the 認証中 interstitial instead of the listing page — this client has been rate-limited; poll less often rather than retrying' + : `athome: the listing page did not produce #serverApp-state within ${STATE_TIMEOUT}ms (no interstitial was shown, so the page was merely slow)`, + { cause: error } + ); + } + const deadline = Math.min(Date.now() + DETAIL_BUDGET_MS, startedAt + TOTAL_BUDGET_MS); + let skipped = 0; for (const b of bukken) { - const link = `${HOST}/rent_store/${b.id}/`; - // Cached per listing, so a monitoring poll only pays for listings it has not seen before. - // eslint-disable-next-line no-await-in-loop -- one shared tab, so the detail pages must be visited in turn - const detail = (await cache.tryGet(`athome:detail:${b.id}`, async () => { - await page.goto(link, { waitUntil: 'domcontentloaded' }); - await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); - return detailOf(await page.content()); - })) as Detail | null; + let detail: Detail | null = null; + if (wantDetail) { + const key = `athome:detail:${b.id}`; + // A cache hit costs no navigation, so it never draws on the budget: a warm poll still + // returns every listing fully enriched however little time is left. + // eslint-disable-next-line no-await-in-loop -- one shared tab, so the detail pages must be visited in turn + const cached = await cache.get(key); + if (cached) { + detail = JSON.parse(cached) as Detail; + } else if (Date.now() < deadline) { + try { + // eslint-disable-next-line no-await-in-loop -- same shared tab + await page.goto(`${HOST}/rent_store/${b.id}/`, { waitUntil: 'domcontentloaded' }); + // eslint-disable-next-line no-await-in-loop -- same shared tab + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + // eslint-disable-next-line no-await-in-loop -- same shared tab + detail = detailOf(await page.content()); + cache.set(key, JSON.stringify(detail)); + } catch (error) { + // One unreadable listing must not cost the other 29. + logger.warn(`athome: detail page for ${b.id} failed: ${String(error)}`); + } + } else { + skipped += 1; + } + } const item = toItem(b, detail); if (item !== null) { items.push(item); } } - } catch (error) { - logger.warn(`athome: ${listUrl} failed: ${String(error)}`); - throw new Error('athome: the listing page did not settle — it is probably still showing the 認証中 interstitial', { cause: error }); + if (skipped > 0) { + logger.warn(`athome: ${listUrl} ran out of its ${DETAIL_BUDGET_MS}ms enrichment budget; ${skipped} of ${bukken.length} listings carry list-page fields only`); + } } finally { await destroy(); } - // The site's default order is not chronological, so the feed is sorted by 情報公開日 itself, newest first. + // The site's default order is not chronological, so the feed is sorted by 情報公開日 itself, newest + // first. With `detail=0` nothing carries a date and the site's own order is kept. items.sort((a, b) => ((b._extra as ListingExtra).listed_at ?? '').localeCompare((a._extra as ListingExtra).listed_at ?? '')); return { @@ -300,9 +361,13 @@ export const route: Route = { }, description: `貸店舗 listings on アットホーム for one 市区町村,newest first. -This route needs a browser. The page is Angular Universal SSR sitting behind a JavaScript interstitial, so its first paint can be an 「認証中」 screen that clears itself once the browser finishes loading; only the settled page carries the \`#serverApp-state\` payload the route reads. The route waits for that element and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. Expect it to be slower and less reliable than the plain-HTML listing routes, and cache it generously. +This route needs a browser, and the browser is not a convenience. The page is Angular Universal SSR behind a JavaScript interstitial, and only the settled page carries the \`#serverApp-state\` payload the route reads. A plain HTTP client is served normally for about four requests and then gets the 「認証中」 interstitial on everything afterwards — pacing the requests 3s apart does not lift it — while a real browser keeps being served throughout. The interstitial reloads itself, which stops a \`domcontentloaded\` navigation from ever settling, so the page is opened with \`commit\` and every wait is bounded here; a blocked client is reported as such rather than as a slow page. **Being blocked is a volume problem, not a retry problem** — poll less often rather than retrying, and keep the cache warm. The route waits for \`#serverApp-state\` on the **list** page and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. + +情報公開日,the coordinates and the fee detail exist only on each listing's own page, so the route visits them — reusing one browser tab rather than opening a browser per listing, and caching per listing so a repeated poll only pays for listings it has not seen before. + +Each of those visits is a full browser navigation, and on a modest VPS one can take several seconds, so at the default \`limit\` a cold cache can outrun RSSHub's own 30s request timeout. Enrichment therefore runs on a 20s budget: listings reached within it are enriched, the rest are returned with their list-page fields and a warning is logged. A cache hit needs no navigation and so never draws on the budget, which means a warm poll still returns everything fully enriched. A listing whose own page fails is logged and returned with list-page fields too — only the list page failing is fatal. -情報公開日,the coordinates and the fee detail exist only on each listing's own page, so the route visits them — reusing one browser tab rather than opening a browser per listing, and caching per listing so a repeated poll only pays for listings it has not seen before. That is why \`limit\` defaults to 10 rather than the full page of 30. +**Everything the listing itself states — 所在地 down to the 丁目,階,面積,賃料 and the ward — is already on the list page.** If that is all you need, \`detail=0\` skips the per-listing visits entirely and makes this an ordinary fast route; \`listed_at\`,\`pubDate\`, the coordinates and the fee detail are then \`null\`. **The site's own ordering is not chronological**, so the feed is re-sorted by 情報公開日,newest first. Without that a newly published listing could sit well down the list and never reach a monitor watching the first page. @@ -310,9 +375,10 @@ This route needs a browser. The page is Angular Universal SSR sitting behind a J \`raw\` additionally carries what the shared contract has no field for: \`lat\` / \`lng\`, the full 所在地 including its 都道府県,建物名 + 部屋番号,設備,築年月,敷引,償却,その他一時金 and the 定期借家 flag. -| Query | Description | Default | -| ------- | --------------------------------------------------------- | ------- | -| \`limit\` | Listings to enrich (one detail page each, cached), max 30 | 10 |`, +| Query | Description | Default | +| -------- | ------------------------------------------------------------------------- | ------- | +| \`limit\` | Listings to return, max 30 | 10 | +| \`detail\` | \`0\` skips the per-listing detail visits and returns list-page fields only | \`1\` |`, categories: ['other'], features: { requireConfig: false, From 45f5cd63f700869f8bb9fd393d283f4860c78824 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 12:17:21 +0900 Subject: [PATCH 0873/1190] feat(route/bukenavi): read the listing's exact coordinates from its map pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route reported that the 丁目 was unreachable without an account, and that was true of the address but not of the page. 住所 is truncated to the 町 for guests with the site's own 会員限定 notice, but the detail page then calls googleMap('map', 区名, lat, lng, false) from an inline script, and only the label it passes is ward-level — the pin is not. Checked rather than assumed, because the claim it replaces was assumed: five listings all in 東京都新宿区歌舞伎町 carry five distinct coordinate pairs, spread over roughly 265m × 440m, which is the size of that 町. 大久保, 西早稲田 and 住吉町 each give distinct pairs too. So these are per-property positions, not a geocode of the town, and they are finer than the 丁目 the address withholds. raw.lat / raw.lng now carry them, matching where the athome route keeps the same two fields. 10 of 10 listings in 新宿区 have them. --- lib/routes/bukenavi/object.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/routes/bukenavi/object.ts b/lib/routes/bukenavi/object.ts index fc8ff08f6676..abbcd96fca31 100644 --- a/lib/routes/bukenavi/object.ts +++ b/lib/routes/bukenavi/object.ts @@ -9,6 +9,12 @@ import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; const HOST = 'https://bukenavi.jp'; +/** + * `initMap()` pins the map at the listing's own coordinates — googleMap('map', 区名, lat, lng, false). + * The label is only the ward, but the pin is per-property: five 歌舞伎町 listings carry five different + * pairs spread over ~265m × 440m, so this is finer than the 丁目 the 住所 field withholds. + */ +const MAP_CALL = /googleMap\(\s*'[^']*',\s*'[^']*',\s*'([\d.-]+)',\s*'([\d.-]+)'/; const DETAIL_CONCURRENCY = 2; const REGIONS = [ @@ -44,6 +50,8 @@ interface DetailFields { business_types: string | null; // 業種(可能) ng_business: string | null; // 不可業態 note: string | null; // 特記事項 + lat: string | null; // map pin, per-property + lng: string | null; } /** @@ -127,6 +135,8 @@ const parseDetail = (html: string): DetailFields => { address: cell('住所'), line: cell('沿線'), area: cell('面積'), + lat: MAP_CALL.exec(html)?.[1] ?? null, + lng: MAP_CALL.exec(html)?.[2] ?? null, prev_business: cell('前の業態'), business_types: cell('業種'), ng_business: cell('不可業態'), @@ -150,7 +160,7 @@ const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, ward: parseWard(address), address_hint: address, - raw: { ...base.raw, address: d.address, line: d.line, area: d.area, prev_business: d.prev_business, business_types: d.business_types, ng_business: d.ng_business, note: d.note }, + raw: { ...base.raw, address: d.address, line: d.line, area: d.area, prev_business: d.prev_business, business_types: d.business_types, ng_business: d.ng_business, note: d.note, lat: d.lat, lng: d.lng }, }; }; @@ -239,6 +249,8 @@ export const route: Route = { }, description: `New 居抜き listings on ぶけなび that are currently 募集中,newest first (first page, 10 listings) — for a region, a prefecture, or one 市区町村 when \`city\` is given. Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,前業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`. +**The exact location is in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests (「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」), but the page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. They are finer than the 丁目 the address withholds, and no account is needed for them. + \`city\` is a 5-digit JIS X 0402 code and the site pairs it with the prefecture, so both are required — \`/bukenavi/object/kanto/kanagawa/14104\` is 横浜市中区. A code that does not belong to \`pref\` is rejected rather than sent on. Note that \`bukenavi.jp/{region}/area/{日本語}\` pages are SEO landing pages carrying no listings; the 市区町村 filter is the \`city[]\` parameter on the list endpoint, which is what this route uses.`, From f183f42d635d1800f5f616e857b1a11c4307b8d8 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 12:17:26 +0900 Subject: [PATCH 0874/1190] feat(route/bukenavi): read the map pin's coordinates on the detail route too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same correction as the area route, kept in step: raw.lat / raw.lng come from the page's own initMap() call, so a listing followed one at a time also gets its exact position rather than only the 町. The header comment and the route description said the detail route "does not improve the address" and that the 丁目 was unreachable without an account. The first half stands — address_hint still stops at the 町 — but the second was wrong about the page as a whole, and both now say where the precise location actually is. --- lib/routes/bukenavi/detail.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/routes/bukenavi/detail.ts b/lib/routes/bukenavi/detail.ts index 7f9701483863..de5789ccaaad 100644 --- a/lib/routes/bukenavi/detail.ts +++ b/lib/routes/bukenavi/detail.ts @@ -7,18 +7,26 @@ import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; const HOST = 'https://bukenavi.jp'; +/** + * `initMap()` pins the map at the listing's own coordinates — googleMap('map', 区名, lat, lng, false). + * The label it passes is only the ward, which is why the page looks ward-level, but the pin is not: + * five 歌舞伎町 listings carry five different pairs, spread over ~265m × 440m. So the exact location is + * published to guests even though 住所 stops at the 町. + */ +const MAP_CALL = /googleMap\(\s*'[^']*',\s*'[^']*',\s*'([\d.-]+)',\s*'([\d.-]+)'/; const REGIONS = new Set(['kanto', 'kansai', 'tokai', 'kyushu']); /** * One listing, from `/{region}/object/{id}` — the id-only URL serves the same page as the station-named one. * - * The address is deliberately **not** improved here: ぶけなび truncates 住所 to the 町 for guests and says so - * ('東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)'), so the 丁目 is unreachable without - * an account and this route cannot do better than the area route on that field. What it does add is everything - * the list card omits: 乗降者数, 構造, 竣工年月, 立地, 間口, 業種, 不可業態, 営業年数 and 特記事項. + * 住所 is truncated to the 町 for guests ('東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)'), + * so `address_hint` stops there — but the page's own map pin does not, and `raw.lat` / `raw.lng` carry the + * listing's exact coordinates without an account. It also adds everything the list card omits: 乗降者数, + * 構造, 竣工年月, 立地, 間口, 業種, 不可業態, 営業年数 and 特記事項. */ const parseDetail = (html: string, region: string, id: string): DataItem | null => { const $ = load(html); + const map = MAP_CALL.exec(html); const rows = new Map(); for (const tr of $('table.box__property__table tr').toArray()) { @@ -55,6 +63,8 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null business_types: row('業種'), excluded_business: row('不可業態'), notes: row('特記事項'), + lat: map?.[1] ?? null, + lng: map?.[2] ?? null, }; // '1,419,000円 @4.73万円' — the 坪単価 follows the rent after an @. @@ -89,7 +99,8 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null // The site publishes no listing date. listed_at: null, ward: parseWard(raw.address), - // Town-level only: the 丁目 is behind the site's own 会員限定 notice. + // Town-level: the 丁目 is behind the site's 会員限定 notice, so raw.lat / raw.lng are the precise + // location here, not this string. Some listings do end in a bare 丁目 number ('浅草 1'). address_hint: raw.address?.split('※', 1)[0]?.trim() ?? null, tags: [], raw, @@ -150,7 +161,7 @@ export const route: Route = { It adds what the area route's cards omit: 乗降者数 for the nearest station, 構造,竣工年月,立地,間口,業種,不可業態,営業年数 and 特記事項,plus exact 面積 and 階数. -**It does not improve the address.** ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」 — so the 丁目 is unreachable without an account, and \`address_hint\` stops at the 町 here exactly as it does on the area route. 敷金,礼金 and 造作 are likewise absent from the guest view, and the site publishes no listing date, so those fields stay \`null\`.`, +**The exact location is in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」 — so \`address_hint\` stops at the 町. The page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. That makes them finer than the 丁目 the address withholds, and no account is needed for them. 敷金,礼金 and 造作 are absent from the guest view, and the site publishes no listing date, so those stay \`null\`.`, categories: ['other'], features: { requireConfig: false, From 2d36b85f81af00c346a8b25c96f5f8c0a478bb0f Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 12:58:29 +0900 Subject: [PATCH 0875/1190] =?UTF-8?q?feat(route/bukenavi):=20optional=20si?= =?UTF-8?q?gn-in=20for=20the=20=E4=BC=9A=E5=93=A1=E9=99=90=E5=AE=9A=20fiel?= =?UTF-8?q?ds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds BUKENAVI_EMAIL / BUKENAVI_PASSWORD and the session helper the two bukenavi routes use. Both variables are optional: with no credentials the routes read the public view exactly as before, which is what an upstream user without an account gets. Only the credential names live here; the route files read config. Sign-in is a Laravel form — GET the login page for its _token, POST it with the address and password. The POST answers 404, because the site redirects to whatever `transitions` holds and that is empty, but it still sets the session, so success is judged by the session cookie rather than by the status code. The session is cached for an hour so a poll costs no extra requests, and a failed sign-in is never cached, which would otherwise pin a bad login for the full TTL. --- lib/config.ts | 10 ++++ lib/routes/bukenavi/utils.ts | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 lib/routes/bukenavi/utils.ts diff --git a/lib/config.ts b/lib/config.ts index 0385672f5c0d..6ad3b09de2ac 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -94,6 +94,8 @@ type ConfigEnvKeys = | 'BTBYR_HOST' | 'BTBYR_COOKIE' | 'BUPT_PORTAL_COOKIE' + | 'BUKENAVI_EMAIL' + | 'BUKENAVI_PASSWORD' | 'CAIXIN_COOKIE' | 'CIVITAI_COOKIE' | 'DIANPING_COOKIE' @@ -384,6 +386,10 @@ export type Config = { host?: string; cookies?: string; }; + bukenavi: { + email?: string; + password?: string; + }; bupt: { portal_cookie?: string; }; @@ -904,6 +910,10 @@ const calculateValue = () => { bupt: { portal_cookie: envs.BUPT_PORTAL_COOKIE, }, + bukenavi: { + email: envs.BUKENAVI_EMAIL, + password: envs.BUKENAVI_PASSWORD, + }, caixin: { cookie: envs.CAIXIN_COOKIE, }, diff --git a/lib/routes/bukenavi/utils.ts b/lib/routes/bukenavi/utils.ts new file mode 100644 index 000000000000..5ed37c92b8fd --- /dev/null +++ b/lib/routes/bukenavi/utils.ts @@ -0,0 +1,90 @@ +import { config } from '@/config'; +import cache from '@/utils/cache'; +import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; + +const HOST = 'https://bukenavi.jp'; +const SESSION_KEY = 'bukenavi:session'; +/** The site keeps a session far longer than this; a fresh login costs two requests, a stale one costs a feed. */ +const SESSION_TTL = 3600; + +/** Laravel form: `_token` on the login page, then POST email + password to `/kanto/login`. */ +const TOKEN = /name="_token"[^>]*value="([^"]+)"/; + +const cookiesFrom = (headers: Headers, into: Map): Map => { + const lines = headers.getSetCookie?.() ?? []; + for (const line of lines) { + const [pair] = line.split(';', 1); + const eq = pair.indexOf('='); + if (eq > 0) { + into.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim()); + } + } + return into; +}; + +const serialise = (jar: Map): string => [...jar].map(([k, v]) => `${k}=${v}`).join('; '); + +/** + * Signs in and returns the cookie header, or null when it did not take. + * + * The POST answers 404 — the site redirects to whatever `transitions` holds and that is empty here — but it + * still sets the session, so the status is deliberately ignored and success is judged by the session cookie. + */ +const login = async (email: string, password: string): Promise => { + const jar = new Map(); + const page = await ofetch.raw(`${HOST}/kanto/user/login`, { responseType: 'text' }); + cookiesFrom(page.headers, jar); + const token = TOKEN.exec(page._data ?? '')?.[1]; + if (token === undefined) { + logger.warn('bukenavi: the login page carried no _token; the form has probably changed'); + return null; + } + + const body = new URLSearchParams({ _token: token, email, password, auto: '1', transitions: '' }); + const res = await ofetch.raw(`${HOST}/kanto/login`, { + method: 'POST', + body, + headers: { Cookie: serialise(jar), Referer: `${HOST}/kanto/user/login`, 'Content-Type': 'application/x-www-form-urlencoded' }, + redirect: 'manual', + ignoreResponseError: true, + responseType: 'text', + }); + cookiesFrom(res.headers, jar); + if (!jar.has('bukenavi_session')) { + logger.warn('bukenavi: sign-in did not return a session cookie; check BUKENAVI_EMAIL / BUKENAVI_PASSWORD'); + return null; + } + return serialise(jar); +}; + +/** + * The cookie header for a signed-in session, or null when no credentials are configured or sign-in failed. + * + * Signing in is optional on purpose. 住所 down to the 番地, 物件名, 保証金・敷金, 礼金, 償却, 共益費, 造作譲渡金額, + * 契約年数 and 座席 are 会員限定; without credentials the routes stay guests and simply leave those `null`, + * which is what an upstream user with no account gets. + */ +export const memberCookie = async (): Promise => { + const { email, password } = config.bukenavi ?? {}; + if (!email || !password) { + return null; + } + const cached = await cache.get(SESSION_KEY); + if (cached) { + return cached; + } + const cookie = await login(email, password); + if (cookie !== null) { + cache.set(SESSION_KEY, cookie, SESSION_TTL); + } + return cookie; +}; + +/** Drops the cached session so the next call signs in again. */ +export const forgetSession = (): void => { + cache.set(SESSION_KEY, '', 1); +}; + +/** A page fetched as a member says so in its header; a guest page does not. */ +export const isSignedIn = (html: string): boolean => html.includes('ログアウト'); From 31b967f9c0f4bd6e7a6e6b9bf655e0d0a53f7961 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 12:58:48 +0900 Subject: [PATCH 0876/1190] =?UTF-8?q?feat(route/bukenavi):=20read=20the=20?= =?UTF-8?q?=E4=BC=9A=E5=93=A1=E9=99=90=E5=AE=9A=20fields=20when=20credenti?= =?UTF-8?q?als=20are=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With BUKENAVI_EMAIL / BUKENAVI_PASSWORD the area route reads what the site shows a member: 住所 down to the 番地 rather than the 町, 物件名, 保証金・敷金, 礼金, 償却, 共益費, 造作譲渡金額, 契約年数, 座席, and an explicit 居抜き / スケルトン instead of one inferred from the title. Measured on /object/kanto/tokyo/13104, 10 listings, the same ward either way: guest deposit 0/10 key money 0/10 fixtures 0/10 building 0/10 東京都新宿区歌舞伎町 member deposit 10/10 key money 9/10 fixtures 5/10 building 10/10 東京都新宿区歌舞伎町 2-9-10 Three things this had to get right. The member view is a different document: it puts 物件名 and 居抜き・スケルトン in an unclassed table and the fee rows in `table.condition`, so the scan covers every table rather than only `box__property__table`, first match winning so labels shared with the guest view keep their guest-view shape. It also renames 不可業態 to 不可業種, so both are read. And a member's 住所 ends in the 物件名, which is trimmed back off so the address stays an address and the building keeps its own field. Guest and member pages of one listing are different documents, so the cache key carries which one it is; without that a guest-cached entry would be served to a member and the reverse. Nothing about the public view changes. --- lib/routes/bukenavi/object.ts | 98 +++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 16 deletions(-) diff --git a/lib/routes/bukenavi/object.ts b/lib/routes/bukenavi/object.ts index abbcd96fca31..3b02da0884ad 100644 --- a/lib/routes/bukenavi/object.ts +++ b/lib/routes/bukenavi/object.ts @@ -1,8 +1,9 @@ import { load } from 'cheerio'; import pMap from 'p-map'; +import { memberCookie } from '@/routes/bukenavi/utils'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; @@ -50,6 +51,16 @@ interface DetailFields { business_types: string | null; // 業種(可能) ng_business: string | null; // 不可業態 note: string | null; // 特記事項 + /** 会員限定 — all null for a guest. */ + building: string | null; // 物件名 + deposit: string | null; // 保証金・敷金 + key_money: string | null; // 礼金 + fixtures: string | null; // 造作譲渡金額 + condition_text: string | null; // 居抜き・スケルトン + amortisation: string | null; // 償却 + common_fee: string | null; // 共益費 + contract_years: string | null; // 契約年数 + seats: string | null; // 座席 lat: string | null; // map pin, per-property lng: string | null; } @@ -122,14 +133,25 @@ const parseList = (html: string, region: string): ListCard[] => { .filter((c): c is ListCard => c !== null); }; -/** Detail page: `table.box__property__table tr > th + td`; 住所 is town-level for guests. */ +/** + * Detail page: `tr > th + td`. 住所 is town-level for a guest and reaches the 番地 for a member. + * Every table is scanned because the member view puts 物件名 and 居抜き・スケルトン in an unclassed + * table and the fee rows in `table.condition`; the first match wins and `box__property__table` + * comes first, so labels shared with the guest view keep their guest-view shape. + */ const parseDetail = (html: string): DetailFields => { const $ = load(html); - const cell = (label: string): string | null => { - const th = $('table.box__property__table th') - .toArray() - .find((el) => clean($(el).text()) === label); - return th ? clean($(th).next('td').text()) : null; + const cell = (...labels: string[]): string | null => { + for (const label of labels) { + const th = $('table th') + .toArray() + .find((el) => clean($(el).text()) === label); + const v = th ? clean($(th).next('td').text()) : null; + if (v !== null && !/^[ー—–--]+$/.test(v)) { + return v; + } + } + return null; }; return { address: cell('住所'), @@ -139,19 +161,36 @@ const parseDetail = (html: string): DetailFields => { lng: MAP_CALL.exec(html)?.[2] ?? null, prev_business: cell('前の業態'), business_types: cell('業種'), - ng_business: cell('不可業態'), + ng_business: cell('不可業態', '不可業種'), note: cell('特記事項'), + building: cell('物件名'), + deposit: cell('保証金・敷金'), + key_money: cell('礼金'), + fixtures: cell('造作譲渡金額'), + condition_text: cell('居抜き・スケルトン'), + amortisation: cell('償却'), + common_fee: cell('共益費'), + contract_years: cell('契約年数'), + seats: cell('座席'), }; }; const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { - const address = clean(d.address?.replace(/※.*$/, '')) ?? null; + const full = clean(d.address?.replace(/※.*$/, '')) ?? null; + // A member's 住所 ends in the 物件名 ('… 2-9-10 G1ビル'); the building keeps its own field so the + // address stays an address. + const address = d.building !== null && full?.endsWith(d.building) ? clean(full.slice(0, -d.building.length)) : full; const { area_m2 } = parseArea(d.area); const limitParts = [d.business_types === null ? null : `可: ${d.business_types}`, d.ng_business === null ? null : `不可: ${d.ng_business}`].filter((p): p is string => p !== null); return { ...base, area_m2: area_m2 ?? base.area_m2, line: base.line ?? d.line, + // 会員限定, so these stay null for a guest. + deposit_months: parseMonths(d.deposit), + key_money_months: parseMonths(d.key_money), + fixtures_transfer_jpy: parseJpy(d.fixtures), + condition: parseCondition(d.condition_text) ?? base.condition, prev_business: d.prev_business, // Only an explicit 重飲食可 / 不可 counts. An NG list that does not use the word 飲食 is not a // yes: '中華・焼肉・焼き鳥・油の多い業態不可' is a heavy-food ban written out in full, and @@ -160,14 +199,33 @@ const mergeDetail = (base: ListingExtra, d: DetailFields): ListingExtra => { business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, ward: parseWard(address), address_hint: address, - raw: { ...base.raw, address: d.address, line: d.line, area: d.area, prev_business: d.prev_business, business_types: d.business_types, ng_business: d.ng_business, note: d.note, lat: d.lat, lng: d.lng }, + raw: { + ...base.raw, + address: d.address, + line: d.line, + area: d.area, + prev_business: d.prev_business, + business_types: d.business_types, + ng_business: d.ng_business, + note: d.note, + lat: d.lat, + lng: d.lng, + building: d.building, + deposit: d.deposit, + key_money: d.key_money, + fixtures: d.fixtures, + amortisation: d.amortisation, + common_fee: d.common_fee, + contract_years: d.contract_years, + seats: d.seats, + }, }; }; /** A failed detail page keeps the list fields instead of breaking the feed. */ -const enrich = async (card: ListCard): Promise => { +const enrich = async (card: ListCard, cookie: string | null): Promise => { try { - return mergeDetail(card.extra, parseDetail(await ofetch(card.link))); + return mergeDetail(card.extra, parseDetail(await ofetch(card.link, { responseType: 'text', headers: cookie === null ? undefined : { Cookie: cookie } }))); } catch (error) { logger.warn(`bukenavi: detail fetch failed for ${card.link}: ${String(error)}`); return { ...card.extra, raw: { ...card.extra.raw, detail_error: String(error) } }; @@ -203,11 +261,14 @@ export const handler = async (ctx): Promise => { const listUrl = `${HOST}/${region.slug}/object/list?wanted=1${prefCode === undefined ? '' : `&prefecture%5B%5D=${prefCode}`}${city === undefined ? '' : `&city%5B%5D=${city}`}`; const cards = parseList(await ofetch(listUrl), region.slug); + const cookie = await memberCookie(); const items = await pMap( cards, (card) => - cache.tryGet(card.link, async (): Promise => { - const extra = await enrich(card); + // A guest page and a member page of the same listing are different documents, so they + // cannot share a cache key. + cache.tryGet(`${cookie === null ? 'guest' : 'member'}:${card.link}`, async (): Promise => { + const extra = await enrich(card, cookie); return { title: card.title, link: card.link, @@ -249,14 +310,19 @@ export const route: Route = { }, description: `New 居抜き listings on ぶけなび that are currently 募集中,newest first (first page, 10 listings) — for a region, a prefecture, or one 市区町村 when \`city\` is given. Each item's \`_extra\` carries the structured listing fields (賃料,坪,坪単価,階,最寄駅,前業態,業種制限,…) parsed from the list and detail pages; unknown values are \`null\`. The site does not publish listing dates, so items have no \`pubDate\`. -**The exact location is in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests (「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」), but the page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. They are finer than the 丁目 the address withholds, and no account is needed for them. +**With an account it also reads the 会員限定 fields.** Set \`BUKENAVI_EMAIL\` and \`BUKENAVI_PASSWORD\` and 住所 comes through to the 番地 (\`東京都新宿区歌舞伎町 2-9-10\`), plus 物件名,保証金・敷金,礼金,償却,共益費,造作譲渡金額,契約年数,座席 and an explicit 居抜き / スケルトン. Both variables are optional and the route is fully usable without them — it stays a guest and leaves those \`null\`. + +**Without an account the exact location is still in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests (「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」), but the page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. They are finer than the 丁目 the address withholds, and no account is needed for them. \`city\` is a 5-digit JIS X 0402 code and the site pairs it with the prefecture, so both are required — \`/bukenavi/object/kanto/kanagawa/14104\` is 横浜市中区. A code that does not belong to \`pref\` is rejected rather than sent on. Note that \`bukenavi.jp/{region}/area/{日本語}\` pages are SEO landing pages carrying no listings; the 市区町村 filter is the \`city[]\` parameter on the list endpoint, which is what this route uses.`, categories: ['other'], features: { - requireConfig: false, + requireConfig: [ + { name: 'BUKENAVI_EMAIL', optional: true, description: 'ぶけなび account e-mail. Optional — without it the route reads the public view.' }, + { name: 'BUKENAVI_PASSWORD', optional: true, description: 'ぶけなび account password. Optional — without it the route reads the public view.' }, + ], requirePuppeteer: false, antiCrawler: false, supportRadar: true, From 7ff8a53f5af5581a15d09409d6953f716dbbade5 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 12:58:53 +0900 Subject: [PATCH 0877/1190] =?UTF-8?q?feat(route/bukenavi):=20read=20the=20?= =?UTF-8?q?=E4=BC=9A=E5=93=A1=E9=99=90=E5=AE=9A=20fields=20on=20the=20deta?= =?UTF-8?q?il=20route=20too?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same as the area route, kept in step so one listing reads the same through both: with credentials, 住所 to the 番地, 物件名, the fee fields, 契約年数, 座席, 引渡し時期, 厨房設備 and an explicit 居抜き / スケルトン; without them, the public view unchanged. Verified on /detail/54379: guest 東京都新宿区歌舞伎町 deposit null key null fixtures null member 東京都新宿区歌舞伎町 2-9-10 deposit 4 key 2 fixtures 1650000 building G1ビル --- lib/routes/bukenavi/detail.ts | 72 ++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/lib/routes/bukenavi/detail.ts b/lib/routes/bukenavi/detail.ts index de5789ccaaad..979eeab010bb 100644 --- a/lib/routes/bukenavi/detail.ts +++ b/lib/routes/bukenavi/detail.ts @@ -1,7 +1,8 @@ import { load } from 'cheerio'; +import { memberCookie } from '@/routes/bukenavi/utils'; import type { ListingExtra } from '@/routes/temposmart/utils'; -import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; +import { clean, normalizeFloor, parseArea, parseCondition, parseHeavyFood, parseJpy, parseMonths, parseWalkMin, parseWard, summarize, tsuboUnit } from '@/routes/temposmart/utils'; import type { Data, DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; @@ -29,16 +30,24 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null const map = MAP_CALL.exec(html); const rows = new Map(); - for (const tr of $('table.box__property__table tr').toArray()) { + // The member view puts 物件名 and 居抜き・スケルトン in an unclassed table and the fee rows in + // `table.condition`, so every table is scanned. First label wins, and `box__property__table` + // comes first in the document, so shared labels such as 賃料 keep their guest-view shape. + for (const tr of $('table tr').toArray()) { const key = clean($(tr).find('th').first().text()); const value = clean($(tr).find('td').first().text()); if (key !== null && !rows.has(key)) { rows.set(key, value ?? ''); } } - const row = (label: string): string | null => { - const v = clean(rows.get(label) ?? null); - return v === null || /^[ー—–--]+$/.test(v) ? null : v; + const row = (...labels: string[]): string | null => { + for (const label of labels) { + const v = clean(rows.get(label) ?? null); + if (v !== null && !/^[ー—–--]+$/.test(v)) { + return v; + } + } + return null; }; const title = clean($('h1').first().text()) ?? clean($('title').text()); @@ -61,8 +70,22 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null prev_business: row('前の業態'), years_open: row('営業年数'), business_types: row('業種'), - excluded_business: row('不可業態'), + excluded_business: row('不可業態', '不可業種'), notes: row('特記事項'), + // 会員限定: null for a guest, which is exactly what an upstream user with no account sees. + building: row('物件名'), + deposit: row('保証金・敷金'), + key_money: row('礼金'), + amortisation: row('償却'), + common_fee: row('共益費'), + fixtures: row('造作譲渡金額'), + contract_years: row('契約年数'), + handover: row('引渡し時期'), + seats: row('座席'), + condition_text: row('居抜き・スケルトン'), + kitchen: row('厨房設備'), + kitchen_waterproof: row('厨房防水'), + aircon: row('エアコン'), lat: map?.[1] ?? null, lng: map?.[2] ?? null, }; @@ -73,6 +96,10 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null const limitParts = [raw.business_types === null ? null : `可: ${raw.business_types}`, raw.excluded_business === null ? null : `不可: ${raw.excluded_business}`].filter((p): p is string => p !== null); const { tsubo, area_m2 } = parseArea(raw.size); const rentJpy = parseJpy(clean(rentText)); + // A guest sees '東京都新宿区歌舞伎町 ※詳細は…'; a member sees '東京都新宿区歌舞伎町 2-9-10 G1ビル'. + // The 物件名 is trimmed back off so the address stays an address — the building keeps its own field. + const fullAddress = raw.address?.split('※', 1)[0]?.trim() ?? null; + const address = raw.building !== null && fullAddress?.endsWith(raw.building) ? clean(fullAddress.slice(0, -raw.building.length)) : fullAddress; const extra: ListingExtra = { source: 'bukenavi', @@ -85,12 +112,13 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null station: clean(raw.station?.split('(', 1)[0] ?? null)?.replace(/駅$/, '') ?? null, line: raw.line, walk_min: parseWalkMin(raw.station), - // 敷金 / 礼金 are not published on the guest view. - deposit_months: null, + // 会員限定, so null for a guest. + deposit_months: parseMonths(raw.deposit), deposit_jpy: null, - key_money_months: null, - fixtures_transfer_jpy: null, - condition: parseCondition(title), + key_money_months: parseMonths(raw.key_money), + fixtures_transfer_jpy: parseJpy(raw.fixtures), + // 居抜き・スケルトン is a 会員限定 field; the title is the guest's only statement of it. + condition: parseCondition(raw.condition_text, title), prev_business: raw.prev_business, // As on the area route: only an explicit 重飲食可 / 不可 counts, never the absence of 飲食 from // the NG list, which says nothing about heavy food either way. @@ -98,10 +126,9 @@ const parseDetail = (html: string, region: string, id: string): DataItem | null business_limit: limitParts.length > 0 ? limitParts.join(' / ') : null, // The site publishes no listing date. listed_at: null, - ward: parseWard(raw.address), - // Town-level: the 丁目 is behind the site's 会員限定 notice, so raw.lat / raw.lng are the precise - // location here, not this string. Some listings do end in a bare 丁目 number ('浅草 1'). - address_hint: raw.address?.split('※', 1)[0]?.trim() ?? null, + ward: parseWard(address), + // Town-level for a guest, down to the 番地 for a member. raw.lat / raw.lng are precise either way. + address_hint: address, tags: [], raw, }; @@ -127,7 +154,11 @@ export const handler = async (ctx): Promise => { } const link = `${HOST}/${region}/object/${id}`; - const item = (await cache.tryGet(`bukenavi:detail:${region}:${id}`, async () => parseDetail(await ofetch(link, { responseType: 'text' }), region, id))) as DataItem | null; + const cookie = await memberCookie(); + // A guest page and a member page of the same listing are different documents, so they cannot share a key. + const item = (await cache.tryGet(`bukenavi:detail:${cookie === null ? 'guest' : 'member'}:${region}:${id}`, async () => + parseDetail(await ofetch(link, { responseType: 'text', headers: cookie === null ? undefined : { Cookie: cookie } }), region, id) + )) as DataItem | null; if (item === null) { throw new Error(`bukenavi: listing ${id} could not be read; it may have been delisted`); } @@ -161,10 +192,15 @@ export const route: Route = { It adds what the area route's cards omit: 乗降者数 for the nearest station, 構造,竣工年月,立地,間口,業種,不可業態,営業年数 and 特記事項,plus exact 面積 and 階数. -**The exact location is in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」 — so \`address_hint\` stops at the 町. The page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. That makes them finer than the 丁目 the address withholds, and no account is needed for them. 敷金,礼金 and 造作 are absent from the guest view, and the site publishes no listing date, so those stay \`null\`.`, +**With an account it also reads the 会員限定 fields.** Set \`BUKENAVI_EMAIL\` and \`BUKENAVI_PASSWORD\` and 住所 comes through to the 番地 (\`東京都新宿区歌舞伎町 2-9-10\`), plus 物件名,保証金・敷金,礼金,償却,共益費,造作譲渡金額,契約年数,座席,引渡し時期 and an explicit 居抜き / スケルトン. Both variables are optional and the route is fully usable without them — it simply stays a guest and leaves those \`null\`, which is the behaviour described below. + +**Without an account the exact location is still in \`raw.lat\` / \`raw.lng\`, not in the address.** ぶけなび truncates 住所 to the 町 for guests and says so on the page — 「東京都新宿区歌舞伎町 ※詳細はお問い合わせください(住所詳細は会員限定)」 — so \`address_hint\` stops at the 町. The page's own map pin does not: \`initMap()\` is called with the listing's coordinates, and five 歌舞伎町 listings carry five different pairs spread over roughly 265m × 440m, so these are per-property positions rather than a geocode of the town. That makes them finer than the 丁目 the address withholds, and no account is needed for them. 敷金,礼金 and 造作 are absent from the guest view, and the site publishes no listing date, so those stay \`null\`.`, categories: ['other'], features: { - requireConfig: false, + requireConfig: [ + { name: 'BUKENAVI_EMAIL', optional: true, description: 'ぶけなび account e-mail. Optional — without it the route reads the public view.' }, + { name: 'BUKENAVI_PASSWORD', optional: true, description: 'ぶけなび account password. Optional — without it the route reads the public view.' }, + ], requirePuppeteer: false, antiCrawler: false, supportRadar: true, From 1ae3097e39f41b772b096174217207d738d3f348 Mon Sep 17 00:00:00 2001 From: pseudoyu Date: Sat, 19 Sep 2026 15:23:12 +0900 Subject: [PATCH 0878/1190] fix(route/athome): tell a refused client apart from a slow one, and stop fetching what it does not need MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit bounded the waits but paid for it in the wrong place. It opened the page with `commit`, which returns on the first byte, so the 12s window started before any of the document had arrived — and `#serverApp-state` sits about 62% of the way into a 2.4MB response, so roughly 1.5MB has to land before the selector can match. A refused client and a merely throttled one both waited the full 12s and both got the same message. Three changes, all measured against the live site: - One predicate now watches for `#serverApp-state` and for the 認証中 title at once, so a refused client is reported the moment the interstitial renders rather than after the whole window. Measured on a blocked client: 312ms, 320ms, 291ms, against 13s before and ~31s before that. - Because being refused is detected in milliseconds, the window for a slow client no longer has to be short to keep failures quick. It is 26s, measured from the start of the request rather than from the navigation, so a slow goto eats into the wait instead of adding to it and the whole run still fits inside RSSHub's 30s default. - The page's images, fonts, stylesheets and media are no longer fetched, leaving the connection to the one response that matters. Scripts are deliberately kept: the interstitial clears itself with one. Also corrects a claim in the route's own description. It said a real browser "keeps being served throughout", which was true at the volume I first measured and false at a higher one — my own browser was refused for over an hour after a day of probing. A browser gets a far larger allowance than a plain client, not an exemption, so this is a matter of cadence and the description now says so. Polling during a block extends it, which is why the error text tells the operator to poll less often rather than to retry. Verified on /rent-store/tokyo/shinjuku-city: detail=0 limit=5 in 1.96s, enriched limit=3 in 5.7s, blocked path 291-320ms, full-routes green. --- lib/routes/athome/rent-store.ts | 60 +++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/lib/routes/athome/rent-store.ts b/lib/routes/athome/rent-store.ts index d069bd35223f..ceb0f0605d56 100644 --- a/lib/routes/athome/rent-store.ts +++ b/lib/routes/athome/rent-store.ts @@ -20,7 +20,22 @@ const DETAIL_KEY = '/bukken/detail/'; * settling, so the page is opened with `commit` and the waiting is done here where it can be reported. */ const NAV_TIMEOUT = 12000; -const STATE_TIMEOUT = 12000; +/** + * Ceiling for the list page as a whole, measured from the start of the request so that a slow + * navigation eats into the wait rather than adding to it. `#serverApp-state` sits about 62% of the + * way into a 2.4MB document, so a throttled client needs to receive ~1.5MB before it can appear — + * hence a window this wide. Being blocked is detected separately and fails in about a second, so + * the width costs a refused client nothing. + */ +const LIST_DEADLINE_MS = 26000; +/** + * Only the document is needed. Blocking the page's images, fonts, stylesheets and media leaves the + * whole connection to the one response that matters, which is what a bandwidth-starved client is + * short of. Scripts are deliberately **not** blocked: the interstitial clears itself with one. + */ +const SKIP_RESOURCES = new Set(['image', 'media', 'font', 'stylesheet']); +/** A detail document is a fraction of the list page's size, so it needs nothing like the same window. */ +const DETAIL_STATE_TIMEOUT = 10000; const DEFAULT_LIMIT = 10; const PAGE_SIZE = 30; /** @@ -266,27 +281,48 @@ export const handler = async (ctx): Promise => { // optional — the site answers a plain HTTP client for about four requests and then serves it the // 認証中 interstitial indefinitely, while a real browser keeps being served normally. const startedAt = Date.now(); - const { page, destroy } = await getPlaywrightPage(listUrl, { gotoConfig: { waitUntil: 'commit', timeout: NAV_TIMEOUT } }); + const { page, destroy } = await getPlaywrightPage(listUrl, { + gotoConfig: { waitUntil: 'commit', timeout: NAV_TIMEOUT }, + onBeforeLoad: async (p) => { + await p.route('**/*', (r) => (SKIP_RESOURCES.has(r.request().resourceType()) ? r.abort() : r.continue())); + }, + }); const items: DataItem[] = []; try { let bukken: Bukken[]; try { - await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + // One predicate for both outcomes, so a refused client is reported the moment its title + // says 認証中 rather than after the full wait: the interstitial arrives immediately, the + // payload does not, and waiting the same time for each would punish only the slow one. + const stateTimeout = Math.max(3000, LIST_DEADLINE_MS - (Date.now() - startedAt)); + let outcome: 'ready' | 'blocked' | 'slow'; + try { + const handle = await page.waitForFunction(() => (document.querySelector('#serverApp-state') ? 'ready' : document.title.includes('認証中') ? 'blocked' : false), { timeout: stateTimeout }); + outcome = (await handle.jsonValue()) as 'ready' | 'blocked'; + } catch { + // A reload mid-check destroys the execution context; the content check below decides. + outcome = 'slow'; + } + if (outcome !== 'ready') { + throw new Error(outcome === 'blocked' ? 'interstitial' : `no #serverApp-state within ${stateTimeout}ms`); + } bukken = listOf(await page.content()).slice(0, limit); } catch (error) { // Only the list page is fatal: an empty feed is indistinguishable from "no new listings". // Say which failure it was — a blocked client and a slow one need different responses. - let blocked = false; - try { - blocked = (await page.content()).includes('認証中'); - } catch { - // The page may already be gone; the message below still names the failure. + let blocked = String(error).includes('interstitial'); + if (!blocked) { + try { + blocked = (await page.content()).includes('認証中'); + } catch { + // The page may already be gone; the message below still names the failure. + } } logger.warn(`athome: ${listUrl} failed after ${Date.now() - startedAt}ms (blocked=${blocked}): ${String(error)}`); throw new Error( blocked ? 'athome: the site is serving the 認証中 interstitial instead of the listing page — this client has been rate-limited; poll less often rather than retrying' - : `athome: the listing page did not produce #serverApp-state within ${STATE_TIMEOUT}ms (no interstitial was shown, so the page was merely slow)`, + : `athome: the listing page did not produce #serverApp-state within ${LIST_DEADLINE_MS}ms (no interstitial was shown, so the page was served too slowly rather than refused)`, { cause: error } ); } @@ -308,7 +344,7 @@ export const handler = async (ctx): Promise => { // eslint-disable-next-line no-await-in-loop -- same shared tab await page.goto(`${HOST}/rent_store/${b.id}/`, { waitUntil: 'domcontentloaded' }); // eslint-disable-next-line no-await-in-loop -- same shared tab - await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: STATE_TIMEOUT }); + await page.waitForSelector('#serverApp-state', { state: 'attached', timeout: DETAIL_STATE_TIMEOUT }); // eslint-disable-next-line no-await-in-loop -- same shared tab detail = detailOf(await page.content()); cache.set(key, JSON.stringify(detail)); @@ -361,7 +397,9 @@ export const route: Route = { }, description: `貸店舗 listings on アットホーム for one 市区町村,newest first. -This route needs a browser, and the browser is not a convenience. The page is Angular Universal SSR behind a JavaScript interstitial, and only the settled page carries the \`#serverApp-state\` payload the route reads. A plain HTTP client is served normally for about four requests and then gets the 「認証中」 interstitial on everything afterwards — pacing the requests 3s apart does not lift it — while a real browser keeps being served throughout. The interstitial reloads itself, which stops a \`domcontentloaded\` navigation from ever settling, so the page is opened with \`commit\` and every wait is bounded here; a blocked client is reported as such rather than as a slow page. **Being blocked is a volume problem, not a retry problem** — poll less often rather than retrying, and keep the cache warm. The route waits for \`#serverApp-state\` on the **list** page and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. +This route needs a browser, and the browser is not a convenience. The page is Angular Universal SSR behind a JavaScript interstitial, and only the settled page carries the \`#serverApp-state\` payload the route reads. A plain HTTP client is served normally for about four requests and then gets the 「認証中」 interstitial on everything afterwards, and pacing the requests 3s apart does not lift it. A browser gets a far larger allowance — dozens of navigations — but **is not exempt**: push hard enough and it is refused too, so this is a matter of cadence rather than of using the right client. The interstitial reloads itself, which stops a \`domcontentloaded\` navigation from ever settling, so the page is opened with \`commit\` and every wait is bounded here, against a deadline measured from the start of the request rather than from the navigation. + +Being refused and being served slowly are told apart rather than guessed at: one check watches for \`#serverApp-state\` and for the 認証中 title at the same time, so a refused client is reported in about a second while a slow one keeps the full window. That window is wide (26s for the list page) because \`#serverApp-state\` sits roughly 62% of the way into a 2.4MB document — a throttled client has to receive about 1.5MB before it can appear — and the width costs a refused client nothing. For the same reason the page's images, fonts, stylesheets and media are not fetched at all, leaving the connection to the one response that matters; scripts are left alone, since the interstitial needs one to clear itself. **Being blocked is a volume problem, not a retry problem** — poll less often rather than retrying, and keep the cache warm. The route waits for \`#serverApp-state\` on the **list** page and **throws if it never appears**, so an unsettled page surfaces as an error rather than as a silently empty feed. 情報公開日,the coordinates and the fee detail exist only on each listing's own page, so the route visits them — reusing one browser tab rather than opening a browser per listing, and caching per listing so a repeated poll only pays for listings it has not seen before. From f99e982c82441a194582533574affdfbde6e32f0 Mon Sep 17 00:00:00 2001 From: Jiamin <16831220+magazian@users.noreply.github.com> Date: Sun, 20 Sep 2026 03:21:19 +0800 Subject: [PATCH 0879/1190] feat(route): add Anhui Museum news and exhibition route (#23112) * feat(route): add Anhui Museum news and exhibition route * fix: skip unmatched exhibition in flatMap * fix: update with ct2-waap --- lib/routes/ahm/abxw.ts | 66 ++++++++++++ lib/routes/ahm/ct2-waap.ts | 194 ++++++++++++++++++++++++++++++++++++ lib/routes/ahm/namespace.ts | 10 ++ lib/routes/ahm/xztj.tsx | 138 +++++++++++++++++++++++++ 4 files changed, 408 insertions(+) create mode 100644 lib/routes/ahm/abxw.ts create mode 100644 lib/routes/ahm/ct2-waap.ts create mode 100644 lib/routes/ahm/namespace.ts create mode 100644 lib/routes/ahm/xztj.tsx diff --git a/lib/routes/ahm/abxw.ts b/lib/routes/ahm/abxw.ts new file mode 100644 index 000000000000..90796df92a86 --- /dev/null +++ b/lib/routes/ahm/abxw.ts @@ -0,0 +1,66 @@ +import { load } from 'cheerio'; + +import type { DataItem, Route } from '@/types'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +import { fetchPage } from './ct2-waap'; +import { namespace } from './namespace'; + +const baseUrl = 'https://www.ahm.cn'; + +export const route: Route = { + path: '/news/abxw', + categories: ['travel'], + example: '/ahm/news/abxw', + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.ahm.cn/News/List/abxw'], + target: '/news/abxw', + }, + ], + name: '安博新闻', + maintainers: ['magazian'], + handler: async () => { + const museumName = namespace.zh?.name || namespace.name; + const listUrl = `${baseUrl}/News/List/abxw`; + + const html = await fetchPage(listUrl); + const $ = load(html); + + const items: DataItem[] = $('ul.img-cont-list li') + .toArray() + .map((el) => { + const $el = $(el); + const a = $el.find('a'); + const href = a.attr('href') ?? ''; + const link = new URL(href, baseUrl).href; + + const dateP = $el.find('.date p'); + const monthDay = dateP.eq(0).text(); + const year = dateP.eq(1).text(); + const dateStr = `${year}/${monthDay}`; + + return { + title: $el.find('.cont h3').text(), + link, + pubDate: timezone(parseDate(dateStr, 'YYYY/MM/DD'), 8), + }; + }); + + return { + title: `${museumName} - 安博新闻`, + link: listUrl, + language: 'zh-CN', + item: items, + }; + }, +}; diff --git a/lib/routes/ahm/ct2-waap.ts b/lib/routes/ahm/ct2-waap.ts new file mode 100644 index 000000000000..436838f66265 --- /dev/null +++ b/lib/routes/ahm/ct2-waap.ts @@ -0,0 +1,194 @@ +import { createCipheriv, createDecipheriv, randomUUID } from 'node:crypto'; + +import { evaluateScriptData } from '@/utils/evaluate-script'; +import md5 from '@/utils/md5'; +import ofetch from '@/utils/ofetch'; + +interface Challenge { + salt: string; + envKey: string; + queryKey: string; + envCookie: string; + queryCookie: string; + bundleMd5: string; +} + +const challengeCache = new Map(); + +const hex = (s: string) => (s.startsWith('-') ? -Number(s.slice(1)) : Number(s)); +const argRe = /^(\w+)(?:-\s?(-?0x[0-9a-f]+))?$/; + +const decodeStrings = async (bundle: string) => { + const dec = bundle.match(/function (\w+)\((\w+),\w+\)\{\2=\2-(0x[0-9a-fA-F]+);var \w+=(\w+)\(\);/); + if (!dec) { + throw new Error('CT2-WAAP: string decoder missing'); + } + + const [, decoderName, , baseHex, tableFn] = dec; + const tableStart = bundle.indexOf(`function ${tableFn}(){var `); + const tableEnd = bundle.indexOf(`];${tableFn}=function`, tableStart); + if (tableStart === -1 || tableEnd === -1) { + throw new Error('CT2-WAAP: string table missing'); + } + + const table = await evaluateScriptData(`${bundle.slice(tableStart + `function ${tableFn}(){`.length, tableEnd + 1)};`, bundle.slice(tableStart, tableEnd).match(/var (\w+)=\[/)![1]); + const base = Number(baseHex); + + const wrappers = new Map }>(); + for (const m of bundle.matchAll(/function (\w+)\(([\w,]*)\)\{return (\w+)\(([^()]*)\);\}/g)) { + const args = m[4].split(',').map((a) => a.match(argRe)); + if (args.every((a) => a !== null)) { + wrappers.set(m[1], { params: m[2].split(','), callee: m[3], args: args.map((a) => ({ p: a[1], off: a[2] ? hex(a[2]) : 0 })) }); + } + } + + const decode = (name: string, vals: number[], depth = 0): string | undefined => { + if (name === decoderName) { + return table[vals[0] - base]; + } + const w = wrappers.get(name); + if (!w || depth > 16) { + return undefined; + } + + const env = Object.fromEntries(w.params.map((p, i) => [p, vals[i]])); + return decode( + w.callee, + w.args.map((a) => env[a.p] - a.off), + depth + 1 + ); + }; + + return bundle.replaceAll(/\b(\w+)\((-?0x[0-9a-f]+(?:,-?0x[0-9a-f]+)*)\)/g, (call, name: string, args: string) => { + const value = + name === decoderName || wrappers.has(name) + ? decode( + name, + args.split(',').map((a) => hex(a)) + ) + : undefined; + return value === undefined ? call : JSON.stringify(value); + }); +}; + +const q = String.raw`["']`; + +const analyzeBundle = async (bundle: string): Promise => { + const plain = await decodeStrings(bundle); + const salt = plain.match(new RegExp(String.raw`\[${q}slice${q}\]\(0x0,0x4\)\+${q}([^"']+)${q}`)); + const keys = plain.match(new RegExp(String.raw`${q}env${q}===\w+\?\w+\[${q}xyjgnaksfLocal${q}\]\[${q}(\w+)${q}\]:\w+\[${q}xyjgnaksfLocal${q}\]\[${q}(\w+)${q}\]`)); + + const cookieVar = (role: string) => { + const call = plain.match(new RegExp(String.raw`\(${q}${role}${q},(\w+),\w+\)`)); + const lit = call && plain.match(new RegExp(String.raw`[,;\s]${call[1]}=${q}(CT_\w+)${q}`)); + return lit?.[1]; + }; + const envCookie = cookieVar('env'); + const queryCookie = cookieVar('query'); + if (!salt || !keys || !envCookie || !queryCookie) { + throw new Error('CT2-WAAP: bundle structure changed'); + } + + return { + salt: salt[1], + envKey: keys[1], + queryKey: keys[2], + envCookie, + queryCookie, + bundleMd5: md5(bundle), + }; +}; + +const aes = (key: string) => ({ + decrypt: (b64: string) => { + const d = createDecipheriv('aes-128-cbc', key, key); + return Buffer.concat([d.update(b64, 'base64'), d.final()]).toString('utf8'); + }, + encrypt: (text: string) => { + const c = createCipheriv('aes-128-cbc', key, key); + return Buffer.concat([c.update(text, 'utf8'), c.final()]).toString('base64'); + }, +}); + +const alnum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; +const randomString = (n: number) => Array.from({ length: n }, () => alnum[Math.floor(Math.random() * alnum.length)]).join(''); + +const visitorId = () => { + const uuid = randomUUID(); + let sum = 0; + for (const ch of uuid.replaceAll('-', '')) { + sum += Number.parseInt(ch, 16) % 10; + } + const head = String(sum).slice(0, 3).padStart(3, '0'); + const parts = uuid.split('-'); + parts.splice(1, 0, head + randomString(8 - head.length)); + return parts.join('-'); +}; + +const buildCookies = (c: Challenge, encStr: string) => { + const cfg = JSON.parse(aes(encStr.slice(0, 4) + c.salt).decrypt(encStr.slice(4))) as Record; + const serverTime = Object.values(cfg).find((v) => typeof v === 'string' && /^\d{10}$/.test(v)) as string | undefined; + const envKey = cfg[c.envKey]; + const queryKey = cfg[c.queryKey]; + if (!serverTime || typeof envKey !== 'string' || typeof queryKey !== 'string') { + throw new Error('CT2-WAAP: challenge config changed'); + } + const last2 = serverTime.slice(-2); + const wrap = (data: string) => `002&&${serverTime}&&${data}&&${last2}`; + return { + [c.envCookie]: aes(envKey.slice(3, 19)).encrypt(wrap(`envCTCT&&${visitorId()}&&0&&allRight`)), + [c.queryCookie]: aes(queryKey.slice(3, 19)).encrypt(wrap(`queryCTCT&&${randomString(2)}&&${c.bundleMd5}`)), + }; +}; + +export const fetchPage = async (pageUrl: string): Promise => { + const first = await ofetch.raw(pageUrl, { + responseType: 'text', + ignoreResponseError: true, + }); + const html = first._data ?? ''; + if (first.status === 200 && !html.includes('ctct_bundle')) { + return html; + } + + const scripts = Array.from(html.matchAll(/]+src="([^"]+)"/g), (m) => m[1]); + const xyPath = scripts.find((s) => !s.includes('ctct_bundle')); + const bundlePath = scripts.find((s) => s.includes('ctct_bundle')); + if (!xyPath || !bundlePath) { + throw new Error(`CT2-WAAP: unexpected status ${first.status} for ${pageUrl}`); + } + + const bundleUrl = new URL(bundlePath, pageUrl).href; + const [xy, bundleText] = await Promise.all([ofetch(new URL(xyPath, pageUrl).href, { responseType: 'text' }), ofetch(bundleUrl, { responseType: 'text' })]); + + const candidates = xy.matchAll(/var (\w+)\s*=\s*"((?:[^"\\]|\\.)*)"/g).toArray(); + candidates.sort((a, b) => b[2].length - a[2].length); + if (!candidates.length) { + throw new Error('CT2-WAAP: challenge config missing'); + } + + let challenge = challengeCache.get(bundleUrl); + if (!challenge) { + if (challengeCache.size > 5) { + challengeCache.clear(); + } + challenge = await analyzeBundle(bundleText); + challengeCache.set(bundleUrl, challenge); + } + + const cookie = Object.entries(buildCookies(challenge, candidates[0][2])) + .map(([k, v]) => `${k}=${v}`) + .join('; '); + + const second = await ofetch.raw(pageUrl, { + headers: { Cookie: cookie }, + responseType: 'text', + ignoreResponseError: true, + }); + if (second.status !== 200) { + challengeCache.delete(bundleUrl); + throw new Error(`CT2-WAAP: challenge failed with ${second.status} for ${pageUrl}`); + } + + return second._data ?? ''; +}; diff --git a/lib/routes/ahm/namespace.ts b/lib/routes/ahm/namespace.ts new file mode 100644 index 000000000000..c2975fdf15a9 --- /dev/null +++ b/lib/routes/ahm/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Anhui Museum', + url: 'www.ahm.cn', + + zh: { + name: '安徽博物院', + }, +}; diff --git a/lib/routes/ahm/xztj.tsx b/lib/routes/ahm/xztj.tsx new file mode 100644 index 000000000000..67911133a2ae --- /dev/null +++ b/lib/routes/ahm/xztj.tsx @@ -0,0 +1,138 @@ +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { renderToString } from 'hono/jsx/dom/server'; + +import type { DataItem, Route } from '@/types'; +import { parseDate } from '@/utils/parse-date'; + +import { fetchPage } from './ct2-waap'; +import { namespace } from './namespace'; + +const baseUrl = 'https://www.ahm.cn'; + +const parseExhibitionDate = (dateStr?: string): string | undefined => (dateStr ? dayjs(dateStr).format('YYYY-MM-DD') : undefined); + +const parseExhibitionDuration = (durationText: string) => { + const [start, end] = durationText.split('-').map((s) => s.trim()); + return { + startDate: parseExhibitionDate(start), + endDate: parseExhibitionDate(end), + }; +}; + +export const route: Route = { + path: '/exhibition/xztj', + categories: ['travel'], + example: '/ahm/exhibition/xztj', + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['www.ahm.cn/Exhibition/TListNow/xztj'], + target: '/exhibition/xztj', + }, + ], + name: 'Special Exhibition', + maintainers: ['magazian'], + handler: async () => { + const museumName = namespace.zh?.name || namespace.name; + const listUrl = `${baseUrl}/Exhibition/TListNow/xztj`; + + const html = await fetchPage(listUrl); + const $ = load(html); + + const items: DataItem[] = $('ul.exhibition-new li') + .toArray() + .flatMap((el) => { + const $el = $(el); + + const onclickAttr = $el.find('a[onclick]').first().attr('onclick') ?? ''; + const onclickMatch = onclickAttr.match(/f_visitCount\('(\d+)','([^']+)','([^']+)'\)/); + if (!onclickMatch) { + return []; + } + + const exhibitionId = onclickMatch[1]; + const link = onclickMatch[2]; + + const title = $el.find('.cont h3').text(); + const imgUrl = $el.find('img').attr('src') ?? ''; + + const durationText = + $el + .find('p') + .toArray() + .map((p) => $(p).text()) + .find((t) => t.startsWith('展出时间:')) ?? ''; + + const locationText = + $el + .find('p') + .toArray() + .map((p) => $(p).text()) + .find((t) => t.startsWith('展出地点:')) ?? ''; + + const location = locationText.replace('展出地点:', ''); + const fullDuration = durationText.replace('展出时间:', ''); + + const { startDate, endDate } = parseExhibitionDuration(fullDuration); + + const pubDate = startDate ? parseDate(startDate) : undefined; + + const description = renderToString( +
    + +
    +

    + 地点: + {location || '参考详情'} +

    +

    + 开展: + {startDate || '未定/常设'} +

    +

    + 闭展: + {endDate || '未定/常设'} +

    + {fullDuration && ( +

    + 原始展期:{fullDuration} +

    + )} +
    + ); + + const guid = `${baseUrl}/Exhibition/Details/xztj?nid=${exhibitionId}`; + + return [ + { + title, + link, + guid, + pubDate, + description, + _extra: { + museumName, + location, + startDate, + endDate, + }, + }, + ]; + }); + + return { + title: `${museumName} - 新展推介`, + link: listUrl, + language: 'zh-CN', + item: items, + }; + }, +}; From 55e2dd8161f421b574b32a87fea7e3986e449792 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 08:12:29 +0000 Subject: [PATCH 0880/1190] chore(deps): bump rate-limiter-flexible from 11.2.0 to 11.2.1 (#23335) Bumps [rate-limiter-flexible](https://github.com/animir/node-rate-limiter-flexible) from 11.2.0 to 11.2.1. - [Release notes](https://github.com/animir/node-rate-limiter-flexible/releases) - [Commits](https://github.com/animir/node-rate-limiter-flexible/compare/v11.2.0...v11.2.1) --- updated-dependencies: - dependency-name: rate-limiter-flexible dependency-version: 11.2.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d9a745b11417..d58d75818fc1 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "patchright": "1.63.0", "postal-mime": "3.0.0", "query-string": "9.5.1", - "rate-limiter-flexible": "11.2.0", + "rate-limiter-flexible": "11.2.1", "re2js": "2.8.6", "rfc4648": "1.5.4", "rss-parser": "3.13.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58b6cacbc9f1..981aeab13b31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,8 +215,8 @@ importers: specifier: 9.5.1 version: 9.5.1 rate-limiter-flexible: - specifier: 11.2.0 - version: 11.2.0 + specifier: 11.2.1 + version: 11.2.1 re2js: specifier: 2.8.6 version: 2.8.6 @@ -3725,7 +3725,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -5392,8 +5392,8 @@ packages: ramda@0.32.0: resolution: {integrity: sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==} - rate-limiter-flexible@11.2.0: - resolution: {integrity: sha512-L0eIK+BmFMi6NcvmtEg7RSswOFKi9MMD5RhBIypFfOve+G6Jl1Xbb8qEHgK3uRzNtkOFYF0L9f7P4rSf2PnUVw==} + rate-limiter-flexible@11.2.1: + resolution: {integrity: sha512-JAaz01HZ893zAw+Hx5MdM1z3lLAkyVvNpqR+GNp0k3kjuQB8gY91fXhf5C0osWs+A7iKgFv585qzwmjS3aBHlA==} re2js@2.8.6: resolution: {integrity: sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==} @@ -10989,7 +10989,7 @@ snapshots: ramda@0.32.0: {} - rate-limiter-flexible@11.2.0: {} + rate-limiter-flexible@11.2.1: {} re2js@2.8.6: {} From e7de373c2b48ce69e07e2c0e9c1ed0a4dbc4a29f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 08:20:36 +0000 Subject: [PATCH 0881/1190] chore(deps): bump codecov/codecov-action from 7.1.0 to 7.1.1 (#23336) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 7.1.0 to 7.1.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/0b35c9ecc4f0529d0eb674914510c22f85b196b4...303a32d7a59b442fa8d48b6a1cc6825c09c847a5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 7.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a797ffb7f6e7..4b7c3dd1ad85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -54,7 +54,7 @@ jobs: run: pnpm run vitest:workerd - name: Upload coverage to Codecov if: ${{ matrix.node-version == 'lts/*' }} - uses: codecov/codecov-action@0b35c9ecc4f0529d0eb674914510c22f85b196b4 # v7.1.0 + uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos as documented, but seems broken From 12bcc384ff6f99515e67680c856c187fdb055cab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 08:23:31 +0000 Subject: [PATCH 0882/1190] chore(deps-dev): bump rolldown from 1.2.8 to 1.2.9 (#23339) Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.2.8 to 1.2.9. - [Release notes](https://github.com/rolldown/rolldown/releases) - [Changelog](https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md) - [Commits](https://github.com/rolldown/rolldown/commits/v1.2.9/packages/rolldown) --- updated-dependencies: - dependency-name: rolldown dependency-version: 1.2.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 147 ++++++++++++++++++++++++------------------------- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index d58d75818fc1..8188ea1a17db 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "rolldown": "1.2.8", + "rolldown": "1.2.9", "tsdown": "0.23.0", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-7": "npm:typescript@7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 981aeab13b31..8addfec8e520 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -447,8 +447,8 @@ importers: specifier: 11.0.0 version: 11.0.0 rolldown: - specifier: 1.2.8 - version: 1.2.8 + specifier: 1.2.9 + version: 1.2.9 tsdown: specifier: 0.23.0 version: 0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1) @@ -1944,9 +1944,6 @@ packages: cpu: [x64] os: [win32] - '@oxc-project/types@0.149.0': - resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} - '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} @@ -2384,98 +2381,98 @@ packages: '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rolldown/binding-android-arm-eabi@1.2.8': - resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} + '@rolldown/binding-android-arm-eabi@1.2.9': + resolution: {integrity: sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rolldown/binding-android-arm64@1.2.8': - resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} + '@rolldown/binding-android-arm64@1.2.9': + resolution: {integrity: sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.2.8': - resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} + '@rolldown/binding-darwin-arm64@1.2.9': + resolution: {integrity: sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.2.8': - resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} + '@rolldown/binding-darwin-x64@1.2.9': + resolution: {integrity: sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.2.8': - resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} + '@rolldown/binding-freebsd-x64@1.2.9': + resolution: {integrity: sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': - resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + resolution: {integrity: sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.2.8': - resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} + '@rolldown/binding-linux-arm64-gnu@1.2.9': + resolution: {integrity: sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.2.8': - resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} + '@rolldown/binding-linux-arm64-musl@1.2.9': + resolution: {integrity: sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.2.8': - resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + resolution: {integrity: sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.2.8': - resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} + '@rolldown/binding-linux-s390x-gnu@1.2.9': + resolution: {integrity: sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.2.8': - resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} + '@rolldown/binding-linux-x64-gnu@1.2.9': + resolution: {integrity: sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.2.8': - resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} + '@rolldown/binding-linux-x64-musl@1.2.9': + resolution: {integrity: sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.2.8': - resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} + '@rolldown/binding-openharmony-arm64@1.2.9': + resolution: {integrity: sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-win32-arm64-msvc@1.2.8': - resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} + '@rolldown/binding-win32-arm64-msvc@1.2.9': + resolution: {integrity: sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.2.8': - resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} + '@rolldown/binding-win32-x64-msvc@1.2.9': + resolution: {integrity: sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -5525,8 +5522,8 @@ packages: vue-tsc: optional: true - rolldown@1.2.8: - resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} + rolldown@1.2.9: + resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7533,8 +7530,6 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.150.0': optional: true - '@oxc-project/types@0.149.0': {} - '@oxc-project/types@0.150.0': {} '@oxc-resolver/binding-android-arm-eabi@11.24.2': @@ -7787,49 +7782,49 @@ snapshots: dependencies: quansync: 1.0.0 - '@rolldown/binding-android-arm-eabi@1.2.8': + '@rolldown/binding-android-arm-eabi@1.2.9': optional: true - '@rolldown/binding-android-arm64@1.2.8': + '@rolldown/binding-android-arm64@1.2.9': optional: true - '@rolldown/binding-darwin-arm64@1.2.8': + '@rolldown/binding-darwin-arm64@1.2.9': optional: true - '@rolldown/binding-darwin-x64@1.2.8': + '@rolldown/binding-darwin-x64@1.2.9': optional: true - '@rolldown/binding-freebsd-x64@1.2.8': + '@rolldown/binding-freebsd-x64@1.2.9': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': optional: true - '@rolldown/binding-linux-arm64-gnu@1.2.8': + '@rolldown/binding-linux-arm64-gnu@1.2.9': optional: true - '@rolldown/binding-linux-arm64-musl@1.2.8': + '@rolldown/binding-linux-arm64-musl@1.2.9': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.2.8': + '@rolldown/binding-linux-ppc64-gnu@1.2.9': optional: true - '@rolldown/binding-linux-s390x-gnu@1.2.8': + '@rolldown/binding-linux-s390x-gnu@1.2.9': optional: true - '@rolldown/binding-linux-x64-gnu@1.2.8': + '@rolldown/binding-linux-x64-gnu@1.2.9': optional: true - '@rolldown/binding-linux-x64-musl@1.2.8': + '@rolldown/binding-linux-x64-musl@1.2.9': optional: true - '@rolldown/binding-openharmony-arm64@1.2.8': + '@rolldown/binding-openharmony-arm64@1.2.9': optional: true - '@rolldown/binding-win32-arm64-msvc@1.2.8': + '@rolldown/binding-win32-arm64-msvc@1.2.9': optional: true - '@rolldown/binding-win32-x64-msvc@1.2.8': + '@rolldown/binding-win32-x64-msvc@1.2.9': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -11128,12 +11123,12 @@ snapshots: rfc4648@1.5.4: {} - rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.8): + rolldown-plugin-dts@0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.9): dependencies: dts-resolver: 3.0.0(oxc-resolver@11.24.2) get-tsconfig: 5.0.0-beta.6 obug: 2.1.4 - rolldown: 1.2.8 + rolldown: 1.2.9 yuku-ast: 0.9.3 yuku-codegen: 0.9.3 yuku-parser: 0.9.3 @@ -11142,26 +11137,26 @@ snapshots: transitivePeerDependencies: - oxc-resolver - rolldown@1.2.8: + rolldown@1.2.9: dependencies: - '@oxc-project/types': 0.149.0 + '@oxc-project/types': 0.150.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.8 - '@rolldown/binding-android-arm64': 1.2.8 - '@rolldown/binding-darwin-arm64': 1.2.8 - '@rolldown/binding-darwin-x64': 1.2.8 - '@rolldown/binding-freebsd-x64': 1.2.8 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 - '@rolldown/binding-linux-arm64-gnu': 1.2.8 - '@rolldown/binding-linux-arm64-musl': 1.2.8 - '@rolldown/binding-linux-ppc64-gnu': 1.2.8 - '@rolldown/binding-linux-s390x-gnu': 1.2.8 - '@rolldown/binding-linux-x64-gnu': 1.2.8 - '@rolldown/binding-linux-x64-musl': 1.2.8 - '@rolldown/binding-openharmony-arm64': 1.2.8 - '@rolldown/binding-win32-arm64-msvc': 1.2.8 - '@rolldown/binding-win32-x64-msvc': 1.2.8 + '@rolldown/binding-android-arm-eabi': 1.2.9 + '@rolldown/binding-android-arm64': 1.2.9 + '@rolldown/binding-darwin-arm64': 1.2.9 + '@rolldown/binding-darwin-x64': 1.2.9 + '@rolldown/binding-freebsd-x64': 1.2.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.9 + '@rolldown/binding-linux-arm64-gnu': 1.2.9 + '@rolldown/binding-linux-arm64-musl': 1.2.9 + '@rolldown/binding-linux-ppc64-gnu': 1.2.9 + '@rolldown/binding-linux-s390x-gnu': 1.2.9 + '@rolldown/binding-linux-x64-gnu': 1.2.9 + '@rolldown/binding-linux-x64-musl': 1.2.9 + '@rolldown/binding-openharmony-arm64': 1.2.9 + '@rolldown/binding-win32-arm64-msvc': 1.2.9 + '@rolldown/binding-win32-x64-msvc': 1.2.9 rollup@4.63.3: dependencies: @@ -11531,8 +11526,8 @@ snapshots: import-without-cache: 0.4.0 obug: 2.1.4 picomatch: 4.0.7 - rolldown: 1.2.8 - rolldown-plugin-dts: 0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.8) + rolldown: 1.2.9 + rolldown-plugin-dts: 0.28.5(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(rolldown@1.2.9) tinyexec: 1.3.1 tinyglobby: 0.2.17 tree-kill: 1.2.2 @@ -11688,7 +11683,7 @@ snapshots: unrun@0.3.1: dependencies: - rolldown: 1.2.8 + rolldown: 1.2.9 until-async@3.0.2: {} From 63ff8ba3603937cfc21e7d8c259820005d731d59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 08:24:32 +0000 Subject: [PATCH 0883/1190] chore(deps): bump imapflow from 2.0.3 to 2.0.5 (#23343) Bumps [imapflow](https://github.com/postalsys/imapflow) from 2.0.3 to 2.0.5. - [Release notes](https://github.com/postalsys/imapflow/releases) - [Changelog](https://github.com/postalsys/imapflow/blob/master/CHANGELOG.md) - [Commits](https://github.com/postalsys/imapflow/compare/v2.0.3...v2.0.5) --- updated-dependencies: - dependency-name: imapflow dependency-version: 2.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 46 +++++++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 8188ea1a17db..a323e4865b59 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.1.0", "iconv-lite": "0.7.3", - "imapflow": "2.0.3", + "imapflow": "2.0.5", "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8addfec8e520..1ab4f11f3317 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,8 +149,8 @@ importers: specifier: 0.7.3 version: 0.7.3 imapflow: - specifier: 2.0.3 - version: 2.0.3 + specifier: 2.0.5 + version: 2.0.5 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -3238,8 +3238,8 @@ packages: '@yuku-toolchain/types@0.9.3': resolution: {integrity: sha512-rFE+5P4g2wxko5C85MugJOlVjBHEQq87dIkhLkniLXLp63PEtgaFjD954i5HXlfnyzLxPcZHsSOVDVgmo1HToA==} - '@zone-eu/mailsplit@5.4.16': - resolution: {integrity: sha512-zQ9iXvlT3Wi/hazeC1MdI4rQc1UJwJ6IQ6QzSZ5KDxLZZWQSazWLOzImLFluXadKShJ9WJvI1xH+AyVS8b9azg==} + '@zone-eu/mailsplit@5.4.17': + resolution: {integrity: sha512-LOMzdrKRVgaghLaezIPdHRhsGpfDiWNQo1RptWc84CRllI4dWLwqX/L4x1iTt1CaeyvyUfMXM/6mjIsEisT3Tg==} abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} @@ -3798,8 +3798,8 @@ packages: enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - encoding-japanese@2.3.0: - resolution: {integrity: sha512-eQyh1vzHz13DUkZcJO+0IOAoKXRQwKV5IBffeuYsWZyRLGiSzfzXObCqWvqFXdX0UU8qOk+lBXbkUhMCpdJe4Q==} + encoding-japanese@2.4.0: + resolution: {integrity: sha512-PE0KjlsDy9JSsoU4C2Q8NXb1kC4c+bZRac9ZvHXUE4q1Ht63gScKMdEy9jst9+Fi3M4eDkqzkEhBZvix4blP2A==} engines: {node: '>=18.0.0'} encoding-sniffer@0.2.1: @@ -4357,8 +4357,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@2.0.3: - resolution: {integrity: sha512-Y5jq5EbG+7hZVZKBRw6gMii/wh+ZdWpRkq9PE5UHVNm29lE89QodiTxcAWxpTFwqroGsb2UXx3z5TO0Z8tirEw==} + imapflow@2.0.5: + resolution: {integrity: sha512-YG666DWbx0iXDpS4UghDmai1I0XF4w0eZYNEX2iMk3L98GGKxjogIe4RrKgqWzR0RNKJtOXwUgTDbPJIBgbReg==} engines: {node: '>=20.0.0'} import-in-the-middle@3.4.0: @@ -4403,8 +4403,8 @@ packages: resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} engines: {node: '>= 12'} - ip-address@10.7.1: - resolution: {integrity: sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA==} + ip-address@10.7.2: + resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} engines: {node: '>= 12'} ip-regex@4.3.0: @@ -4620,8 +4620,8 @@ packages: libbase64@1.3.0: resolution: {integrity: sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==} - libmime@5.4.3: - resolution: {integrity: sha512-di9BoDabBUMqjeD/wGj+hHpSgdqAph5ui7w6OdY6NpzU6O6VFLQsMOg9tqCjm/zf9OHzAM9EZxSOF7uIb8O8Hw==} + libmime@5.4.4: + resolution: {integrity: sha512-d3zZ2k8s6iO+8u9Vfkl6HfhD5lSVocGcmOViTz/i0CJcwhJADyubEJ/+TkBhuLDUSIBOiG+D8G5kJKwXwKbXXA==} libqp@2.1.1: resolution: {integrity: sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==} @@ -8486,10 +8486,10 @@ snapshots: '@yuku-toolchain/types@0.9.3': {} - '@zone-eu/mailsplit@5.4.16': + '@zone-eu/mailsplit@5.4.17': dependencies: libbase64: 1.3.0 - libmime: 5.4.3 + libmime: 5.4.4 libqp: 2.1.1 abbrev@3.0.1: {} @@ -8974,7 +8974,7 @@ snapshots: enabled@2.0.0: {} - encoding-japanese@2.3.0: {} + encoding-japanese@2.4.0: {} encoding-sniffer@0.2.1: dependencies: @@ -9710,13 +9710,13 @@ snapshots: image-size@0.7.5: {} - imapflow@2.0.3: + imapflow@2.0.5: dependencies: - '@zone-eu/mailsplit': 5.4.16 - encoding-japanese: 2.3.0 + '@zone-eu/mailsplit': 5.4.17 + encoding-japanese: 2.4.0 iconv-lite: 0.7.3 libbase64: 1.3.0 - libmime: 5.4.3 + libmime: 5.4.4 libqp: 2.1.1 pino: 10.3.1 socks: 2.8.10 @@ -9781,7 +9781,7 @@ snapshots: ip-address@10.7.0: {} - ip-address@10.7.1: {} + ip-address@10.7.2: {} ip-regex@4.3.0: {} @@ -9983,9 +9983,9 @@ snapshots: libbase64@1.3.0: {} - libmime@5.4.3: + libmime@5.4.4: dependencies: - encoding-japanese: 2.3.0 + encoding-japanese: 2.4.0 iconv-lite: 0.7.3 libbase64: 1.3.0 libqp: 2.1.1 @@ -11309,7 +11309,7 @@ snapshots: socks@2.8.10: dependencies: - ip-address: 10.7.1 + ip-address: 10.7.2 smart-buffer: 4.2.0 socks@2.8.9: From cc11f83d5a564f65e7bb4fd946d8476ef976563f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:26:10 +0800 Subject: [PATCH 0884/1190] chore(deps-dev): bump @types/node from 26.5.1 to 26.6.1 (#23341) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.5.1 to 26.6.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.6.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 156 ++++++++++++++++++++++++------------------------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index a323e4865b59..00333006c644 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", - "@types/node": "26.5.1", + "@types/node": "26.6.1", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.70.0", "@typescript-eslint/parser": "8.70.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ab4f11f3317..9d5ce51119fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -307,7 +307,7 @@ importers: version: 1.3.6 '@cloudflare/vitest-plugin': specifier: 1.1.9 - version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': specifier: 5.20260915.1 version: 5.20260915.1 @@ -351,8 +351,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.5.1 - version: 26.5.1 + specifier: 26.6.1 + version: 26.6.1 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -379,7 +379,7 @@ importers: version: 10.10.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.5.1)(eslint@10.10.0(jiti@2.7.0)) + version: 9.1.1(@types/node@26.6.1)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) @@ -418,7 +418,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) @@ -466,13 +466,13 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: specifier: 4.131.2 - version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -2831,8 +2831,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.5.1': - resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} + '@types/node@26.6.1': + resolution: {integrity: sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -6554,15 +6554,15 @@ snapshots: optionalDependencies: workerd: 1.20260911.1 - '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) - wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -7101,76 +7101,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.5.1)': + '@inquirer/checkbox@4.3.2(@types/node@26.6.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.5.1) + '@inquirer/core': 10.3.2(@types/node@26.6.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.1) + '@inquirer/type': 3.0.10(@types/node@26.6.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/confirm@5.1.21(@types/node@26.5.1)': + '@inquirer/confirm@5.1.21(@types/node@26.6.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.5.1) - '@inquirer/type': 3.0.10(@types/node@26.5.1) + '@inquirer/core': 10.3.2(@types/node@26.6.1) + '@inquirer/type': 3.0.10(@types/node@26.6.1) optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/confirm@6.1.1(@types/node@26.5.1)': + '@inquirer/confirm@6.1.1(@types/node@26.6.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.5.1) - '@inquirer/type': 4.0.7(@types/node@26.5.1) + '@inquirer/core': 11.2.1(@types/node@26.6.1) + '@inquirer/type': 4.0.7(@types/node@26.6.1) optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/core@10.3.2(@types/node@26.5.1)': + '@inquirer/core@10.3.2(@types/node@26.6.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.1) + '@inquirer/type': 3.0.10(@types/node@26.6.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/core@11.2.1(@types/node@26.5.1)': + '@inquirer/core@11.2.1(@types/node@26.6.1)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.5.1) + '@inquirer/type': 4.0.7(@types/node@26.6.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.5.1)': + '@inquirer/select@4.4.2(@types/node@26.6.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.5.1) + '@inquirer/core': 10.3.2(@types/node@26.6.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.5.1) + '@inquirer/type': 3.0.10(@types/node@26.6.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/type@3.0.10(@types/node@26.5.1)': + '@inquirer/type@3.0.10(@types/node@26.6.1)': optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 - '@inquirer/type@4.0.7(@types/node@26.5.1)': + '@inquirer/type@4.0.7(@types/node@26.6.1)': optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@ioredis/commands@2.0.0': {} @@ -8046,7 +8046,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/caseless@0.12.5': {} @@ -8059,7 +8059,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/crypto-js@4.2.2': {} @@ -8080,11 +8080,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/express-serve-static-core@5.1.3': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8098,7 +8098,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/html-to-text@9.0.4': {} @@ -8110,7 +8110,7 @@ snapshots: '@types/jsdom@30.0.0': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/tough-cookie': 4.0.5 parse5: 8.0.1 undici-types: 8.10.2 @@ -8121,7 +8121,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/jsrsasign@10.5.15': {} @@ -8133,7 +8133,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.5.1': + '@types/node@26.6.1': dependencies: undici-types: 8.9.0 @@ -8149,7 +8149,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8159,16 +8159,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@types/statuses@2.0.6': {} @@ -8368,7 +8368,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/expect@4.1.11': dependencies: @@ -8379,14 +8379,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + msw: 2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) '@vitest/pretty-format@4.1.11': dependencies: @@ -9140,12 +9140,12 @@ snapshots: eslint: 10.10.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.5.1)(eslint@10.10.0(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.6.1)(eslint@10.10.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.5.1) - '@inquirer/confirm': 5.1.21(@types/node@26.5.1) - '@inquirer/select': 4.4.2(@types/node@26.5.1) + '@inquirer/checkbox': 4.3.2(@types/node@26.6.1) + '@inquirer/confirm': 5.1.21(@types/node@26.6.1) + '@inquirer/select': 4.4.2(@types/node@26.6.1) eslint: 10.10.0(jiti@2.7.0) eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) optionator: 0.9.4 @@ -9883,7 +9883,7 @@ snapshots: jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 - '@types/node': 26.5.1 + '@types/node': 26.6.1 jsdom@30.0.1(@noble/hashes@2.3.0): dependencies: @@ -10452,10 +10452,10 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.4(@types/node@26.5.1) + sharp: 0.35.4(@types/node@26.6.1) undici: 7.29.0 workerd: 1.20260911.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -10485,9 +10485,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.5.1) + '@inquirer/confirm': 6.1.1(@types/node@26.6.1) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -10934,7 +10934,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.5.1 + '@types/node': 26.6.1 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -11231,7 +11231,7 @@ snapshots: set-cookie-parser@3.1.2: {} - sharp@0.35.4(@types/node@26.5.1): + sharp@0.35.4(@types/node@26.6.1): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -11262,7 +11262,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.4 '@img/sharp-win32-ia32': 0.35.4 '@img/sharp-win32-x64': 0.35.4 - '@types/node': 26.5.1 + '@types/node': 26.6.1 shebang-command@2.0.0: dependencies: @@ -11750,16 +11750,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): + vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11768,17 +11768,17 @@ snapshots: rollup: 4.63.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.5.1 + '@types/node': 26.6.1 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 yaml: 2.9.0 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.5.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11795,11 +11795,11 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.5.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.5.1 + '@types/node': 26.6.1 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.0.1(@noble/hashes@2.3.0) transitivePeerDependencies: @@ -11890,13 +11890,13 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260911.1 '@cloudflare/workerd-windows-64': 1.20260911.1 - wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.5.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 workerd: 1.20260911.1 From 4257c821d0284de1dd8af6c13eea129804e2d18d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:48:04 +0800 Subject: [PATCH 0885/1190] chore(deps): bump the nix group with 2 updates (#23344) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `8ac3f3a` to `ccb4a99` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/8ac3f3a9f47df8c23e4b24c8488d609c7d9bd7a3...ccb4a99d35474afd07634ee194174ae40f7d98af) Updates `nixpkgs` from `ef34387` to `b1b8759` - [Commits](https://github.com/NixOS/nixpkgs/compare/ef34387ddd751e1ab8857adf4676492d32eb24ec...b1b875982b17dabde9b4a37f3e229e74913e6db3) --- updated-dependencies: - dependency-name: devenv dependency-version: ccb4a99d35474afd07634ee194174ae40f7d98af dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: b1b875982b17dabde9b4a37f3e229e74913e6db3 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 128e2449c611..8709e48db0c4 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789395478, - "narHash": "sha256-s3k1Qkp37Jz2oWyjDgLfzpCIrdg/ESYS0G/Y/Cy0k/8=", + "lastModified": 1789691577, + "narHash": "sha256-mS9lQo1gpKzwWMYgzdxc4yv4sIdwqI0GfFMCbV4LZtA=", "owner": "cachix", "repo": "devenv", - "rev": "8ac3f3a9f47df8c23e4b24c8488d609c7d9bd7a3", + "rev": "ccb4a99d35474afd07634ee194174ae40f7d98af", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789286504, - "narHash": "sha256-eiEK7cKZORNEvX0GeF3RtNEF/JXhgf2RqSp3230q13E=", + "lastModified": 1789546076, + "narHash": "sha256-zVxLZiSnmaaPLwnhj7pwmqe3axBg/C6nG5JZsJMh2g4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ef34387ddd751e1ab8857adf4676492d32eb24ec", + "rev": "b1b875982b17dabde9b4a37f3e229e74913e6db3", "type": "github" }, "original": { From d7dea4c853f8803ef29b1aec669da2cf6a7b646b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:57:47 +0800 Subject: [PATCH 0886/1190] chore(deps): bump jsdom from 30.0.1 to 30.1.0 (#23337) Bumps [jsdom](https://github.com/jsdom/jsdom) from 30.0.1 to 30.1.0. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](https://github.com/jsdom/jsdom/compare/v30.0.1...v30.1.0) --- updated-dependencies: - dependency-name: jsdom dependency-version: 30.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 120 ++++++++++++++++++++++++------------------------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index 00333006c644..37031413e9a6 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", - "jsdom": "30.0.1", + "jsdom": "30.1.0", "json-bigint": "1.0.0", "jsrsasign": "11.1.5", "lru-cache": "11.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d5ce51119fe..2489d6409898 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,8 +161,8 @@ importers: specifier: 5.0.0 version: 5.0.0 jsdom: - specifier: 30.0.1 - version: 30.0.1(@noble/hashes@2.3.0) + specifier: 30.1.0 + version: 30.1.0(@noble/hashes@2.3.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -469,7 +469,7 @@ importers: version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: specifier: 4.131.2 version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -497,13 +497,13 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@asamuzakjp/css-color@6.0.7': - resolution: {integrity: sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==} - engines: {node: ^22.13.0 || >=24.0.0} + '@asamuzakjp/css-color@7.0.0': + resolution: {integrity: sha512-IxddtGnnoidVSO6O/aiTueptbTThHvi2qGHu9/AWuD1J7i86Ox+jl+Y5SXcDH1RixzQlP2cjH/PjnlImZiAurQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} - '@asamuzakjp/dom-selector@8.3.2': - resolution: {integrity: sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==} - engines: {node: ^22.13.0 || >=24.0.0} + '@asamuzakjp/dom-selector@9.1.4': + resolution: {integrity: sha512-ynDE7RDZM1z+YuSU+iGhRp8WcSXHqK9+G32ZEzsL35TKSoy72fYR7VfrJCQkWe3ciiIWGSTAIBc9XB5jVpqyTw==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@asteasolutions/zod-to-openapi@9.1.0': resolution: {integrity: sha512-pLMeRgRYS7/vZIgAAkOe2P6+XGTifR1MT9pqDoxZ11EmcJJ+DPmdPqLN8ZZF4U8R9KHCCQCS9c2wtuE8wSrfkw==} @@ -649,15 +649,15 @@ packages: resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.3.0': - resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} + '@csstools/css-calc@3.4.0': + resolution: {integrity: sha512-XQKj5B7QiZcHiegCOCAzcAOJdhGgWOHbbu62h5e5mkHnn8lWcfiJhllkqWmxu5zWR9jucPHuo1iTB56P033hcg==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.2.2': - resolution: {integrity: sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==} + '@csstools/css-color-parser@4.2.3': + resolution: {integrity: sha512-y4LpL+lmpuyKDiEFq2PnZUVFdAjsoB/qQJod79yLNokXyW7jewi+/WJ69EfItj8A2unWtxXnGjw6LYXgXu5ZjA==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -669,16 +669,16 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.12': - resolution: {integrity: sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==} + '@csstools/css-syntax-patches-for-csstree@1.1.14': + resolution: {integrity: sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: css-tree: optional: true - '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + '@csstools/css-tokenizer@4.0.1': + resolution: {integrity: sha512-bPlN9S9O1A0euCpEWE4qnvB5YDuyYVsUTrxSgmAM1Is0j4tICHoVyOVAXfWMP/kS9ZrjvyIXWV2PmomiAXXqOw==} engines: {node: '>=20.19.0'} '@dabh/diagnostics@2.0.8': @@ -3359,8 +3359,8 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + bidi-js@1.1.0: + resolution: {integrity: sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==} big-integer@1.6.52: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} @@ -4541,8 +4541,8 @@ packages: resolution: {integrity: sha512-V4Ww4EHnTcTLSOMoB0FsF72JhQvcAsriCm/LWnxJeGWoxIjEL2l9na11abQok5SYShq8m0Gl02el/xAbTCulvQ==} engines: {node: ^22.22.2 || >=24.15.0} - jsdom@30.0.1: - resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} + jsdom@30.1.0: + resolution: {integrity: sha512-h/Q+H+KmlGf49ITw3TrykSVbg04S75S2O7ZtJc9gMGOWBrQRZmx5AJIKLtJ01e6Kszl+/tpFthCVqxNifxhoqg==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -5729,9 +5729,6 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -5981,6 +5978,10 @@ packages: resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} engines: {node: '>=18.17'} + undici@6.28.1: + resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} + engines: {node: '>=18.17'} + undici@7.29.0: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} @@ -6237,8 +6238,8 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@17.1.0: - resolution: {integrity: sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==} + whatwg-url@17.1.1: + resolution: {integrity: sha512-ohjk1mdUebJVadRt3bAhQhx8lSnISq+GDttK79LFl8EHQkAPvzwctoasC4hs8tBt6kLAncBWWyq1N52qEfKvDw==} engines: {node: ^22.14.0 || >=24.0.0} whatwg-url@5.0.0: @@ -6436,26 +6437,26 @@ snapshots: '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.28.0 + undici: 6.28.1 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.28.0 + undici: 6.28.1 '@actions/io@3.0.2': {} - '@asamuzakjp/css-color@6.0.7': + '@asamuzakjp/css-color@7.0.0': dependencies: - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) + '@csstools/css-color-parser': 4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) + '@csstools/css-tokenizer': 4.0.1 lru-cache: 11.5.2 - '@asamuzakjp/dom-selector@8.3.2': + '@asamuzakjp/dom-selector@9.1.4': dependencies: - bidi-js: 1.0.3 + bidi-js: 1.1.0 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 @@ -6561,7 +6562,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -6595,27 +6596,27 @@ snapshots: '@csstools/color-helpers@6.1.1': {} - '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)': dependencies: - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) + '@csstools/css-tokenizer': 4.0.1 - '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)': dependencies: '@csstools/color-helpers': 6.1.1 - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) + '@csstools/css-tokenizer': 4.0.1 - '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1)': dependencies: - '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-tokenizer': 4.0.1 - '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.14(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 - '@csstools/css-tokenizer@4.0.0': {} + '@csstools/css-tokenizer@4.0.1': {} '@dabh/diagnostics@2.0.8': dependencies: @@ -8368,7 +8369,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) '@vitest/expect@4.1.11': dependencies: @@ -8579,7 +8580,7 @@ snapshots: before-after-hook@4.0.0: {} - bidi-js@1.0.3: + bidi-js@1.1.0: dependencies: require-from-string: 2.0.2 @@ -9885,12 +9886,12 @@ snapshots: '@types/estree': 1.0.9 '@types/node': 26.6.1 - jsdom@30.0.1(@noble/hashes@2.3.0): + jsdom@30.1.0(@noble/hashes@2.3.0): dependencies: - '@asamuzakjp/css-color': 6.0.7 - '@asamuzakjp/dom-selector': 8.3.2 + '@asamuzakjp/css-color': 7.0.0 + '@asamuzakjp/dom-selector': 9.1.4 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.12(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.14(css-tree@3.2.1) '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.3.0) @@ -9900,13 +9901,12 @@ snapshots: lru-cache: 11.5.2 parse5: 8.0.1 saxes: 6.0.0 - symbol-tree: 3.2.4 tough-cookie: 6.0.2 undici: 8.10.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 17.1.0(@noble/hashes@2.3.0) + whatwg-url: 17.1.1(@noble/hashes@2.3.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -11404,8 +11404,6 @@ snapshots: dependencies: has-flag: 4.0.0 - symbol-tree@3.2.4: {} - system-architecture@0.1.0: {} tagged-tag@1.0.0: {} @@ -11625,6 +11623,8 @@ snapshots: undici@6.28.0: {} + undici@6.28.1: {} + undici@7.29.0: {} undici@8.10.2: {} @@ -11775,7 +11775,7 @@ snapshots: tsx: 4.23.13 yaml: 2.9.0 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.11 '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) @@ -11801,7 +11801,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@types/node': 26.6.1 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) - jsdom: 30.0.1(@noble/hashes@2.3.0) + jsdom: 30.1.0(@noble/hashes@2.3.0) transitivePeerDependencies: - msw @@ -11838,7 +11838,7 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - whatwg-url@17.1.0(@noble/hashes@2.3.0): + whatwg-url@17.1.1(@noble/hashes@2.3.0): dependencies: '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) tr46: 6.0.0 From 98cf0dfb052fbdb7061cf0e4abb780bdcf701e5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:07:14 +0800 Subject: [PATCH 0887/1190] chore(deps): bump dotenv from 17.4.2 to 18.0.0 (#23340) Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.4.2 to 18.0.0. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v17.4.2...v18.0.0) --- updated-dependencies: - dependency-name: dotenv dependency-version: 18.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 37031413e9a6..6563fba7ad68 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "currency-symbol-map": "5.1.0", "dayjs": "1.11.23", "destr": "2.0.5", - "dotenv": "17.4.2", + "dotenv": "18.0.0", "entities": "8.1.0", "etag": "1.8.1", "fanfou-sdk": "6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2489d6409898..06c4b08c8a46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,8 +116,8 @@ importers: specifier: 2.0.5 version: 2.0.5 dotenv: - specifier: 17.4.2 - version: 17.4.2 + specifier: 18.0.0 + version: 18.0.0 entities: specifier: 8.1.0 version: 8.1.0 @@ -3761,9 +3761,10 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@17.4.2: - resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + dotenv@18.0.0: + resolution: {integrity: sha512-16YSUvsKtKbwrTbESvtTSZzzJUug6jdFdqiBFg9Q6Bk1JY9r0hAtrtTiWxdI9YrsgOTmkjZRdgU0n3LnwGpECw==} engines: {node: '>=12'} + hasBin: true dts-resolver@3.0.0: resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} @@ -8945,7 +8946,7 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@17.4.2: {} + dotenv@18.0.0: {} dts-resolver@3.0.0(oxc-resolver@11.24.2): optionalDependencies: From 5f591da5b3ff16d8370509056b660d344b541022 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 19:11:35 +0800 Subject: [PATCH 0888/1190] chore(deps): bump @sentry/node from 10.74.0 to 10.75.0 (#23342) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.74.0 to 10.75.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/10.75.0/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.74.0...10.75.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.75.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 69 +++++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 6563fba7ad68..b56a6a0271de 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "@oxc-parser/binding-wasm32-wasip1": "0.0.1", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.12.1", - "@sentry/node": "10.74.0", + "@sentry/node": "10.75.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06c4b08c8a46..27b9831ac35c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,8 +92,8 @@ importers: specifier: 0.12.1 version: 0.12.1(hono@4.13.8) '@sentry/node': - specifier: 10.74.0 - version: 10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) + specifier: 10.75.0 + version: 10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -2677,12 +2677,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.74.0': - resolution: {integrity: sha512-u9rY8vcZfktccwm6LznfCZlqP5C9A+p76r4/pFS1grqpuTO0m21Cl8rosnlESrDGP/Xd9tfr91rWYk0jPH8jeQ==} + '@sentry/core@10.75.0': + resolution: {integrity: sha512-5wDQpQqjJ6RHdPR+z6Q+47XlwoxRKBv0yyB0LKHqL/1azPOQbR+nllyLmmQDcoJpaksuLSmLA1q6hFX7gjDT2w==} engines: {node: '>=18'} - '@sentry/node-core@10.74.0': - resolution: {integrity: sha512-btgZXcGmkOGgojbxHo/gfGiyqYpzC9E8zVR78Q3MtM6Xnemk9gwJiQlhNmEX/FM+C36WBRPZrdcZcnMaJhbfJw==} + '@sentry/node-core@10.75.0': + resolution: {integrity: sha512-+E3KSX1oMqhpWQ23hj6NblIVUzLy3T2oceU7DfMX1s1ar+npaQZxR5K0/cMGcQgS1LLm8Jqwqo/lrZlKTCDm5A==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2702,20 +2702,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.74.0': - resolution: {integrity: sha512-u1wsarTOWHn9CCev81Da5T4IQHZgdcosXRfX2+4DMII/lVJMBYesKixTQuMwXexjVG2+pkf16zTmgjZDa+75jA==} + '@sentry/node@10.75.0': + resolution: {integrity: sha512-XdYW+SEiscQnuugh1hMldfnHurmleKSTSDqgfro6Y1yd7s0r2csnZ5/SEYxIeL4lSl1QXg1lIA1pBmAXjcdnKA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.74.0': - resolution: {integrity: sha512-ua5mt0NDBfye+/ACKjAw9Ad2i+y42lOLvhxXZepZXjszblMz80MEsIZflMB7uLZUCTNH7MbZN8tlzCy8KsJKmQ==} + '@sentry/opentelemetry@10.75.0': + resolution: {integrity: sha512-reJoMtuHMuaiztoDVoaeitc22eHlTAvz2qpFhibWyzukJlF8oXB+o8EvDN9mmDDMedU89c//cXRcnPBlJvUxcw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.74.0': - resolution: {integrity: sha512-AHmPIGE8yVRyywpZRhAkm/H0cHTgsQSPhFZFbaeQLcYHFE/eOP9bAMh8Nj/Apr7WMagFXYHd04tR/63BxtLgKw==} + '@sentry/server-utils@10.75.0': + resolution: {integrity: sha512-7fIa9vFGNzB13hmxl8Sip3xImSkqpc4wETzuQ7CLzVOwPvwI6y/gVEm0pxRhtJSq8SzfOp26eJWl80u8v78Osg==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -3839,6 +3839,9 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-module-lexer@3.0.2: + resolution: {integrity: sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==} + es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} engines: {node: '>=0.10'} @@ -4362,8 +4365,8 @@ packages: resolution: {integrity: sha512-YG666DWbx0iXDpS4UghDmai1I0XF4w0eZYNEX2iMk3L98GGKxjogIe4RrKgqWzR0RNKJtOXwUgTDbPJIBgbReg==} engines: {node: '>=20.0.0'} - import-in-the-middle@3.4.0: - resolution: {integrity: sha512-Xfjwfarhe+LGmoaof+sexeNo3sGRysb5x56WrZLwHtmqT0OilSKtVWkv0lrCcMgSKyKP9oBogBAisHZvtJH0aw==} + import-in-the-middle@3.5.1: + resolution: {integrity: sha512-mPKuL8bPQzecui2KK6Gb+M8JvJoHnhS1FeYGa22QopBmlevF5F0FE6ued/B5EgHDeIoMTONIpDWWFKUPOG0DBQ==} engines: {node: '>=18'} import-without-cache@0.4.0: @@ -7382,7 +7385,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 - import-in-the-middle: 3.4.0 + import-in-the-middle: 3.5.1 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -7968,16 +7971,16 @@ snapshots: '@sentry/conventions@0.16.0': {} - '@sentry/core@10.74.0': + '@sentry/core@10.75.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.74.0 - '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - import-in-the-middle: 3.4.0 + '@sentry/core': 10.75.0 + '@sentry/opentelemetry': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + import-in-the-middle: 3.5.1 optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) @@ -7985,34 +7988,34 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.74.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.74.0 - '@sentry/node-core': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.74.0 - import-in-the-middle: 3.4.0 + '@sentry/core': 10.75.0 + '@sentry/node-core': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.75.0 + import-in-the-middle: 3.5.1 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.74.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.74.0 + '@sentry/core': 10.75.0 - '@sentry/server-utils@10.74.0': + '@sentry/server-utils@10.75.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.74.0 + '@sentry/core': 10.75.0 '@sindresorhus/is@4.6.0': {} @@ -9006,6 +9009,8 @@ snapshots: es-module-lexer@2.3.2: {} + es-module-lexer@3.0.2: {} + es5-ext@0.10.64: dependencies: es6-iterator: 2.0.3 @@ -9723,10 +9728,10 @@ snapshots: pino: 10.3.1 socks: 2.8.10 - import-in-the-middle@3.4.0: + import-in-the-middle@3.5.1: dependencies: cjs-module-lexer: 2.2.1 - es-module-lexer: 2.3.2 + es-module-lexer: 3.0.2 module-details-from-path: 1.0.4 import-without-cache@0.4.0: {} From a84b41faa31581bacac4cea374fcf909f784bab9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 07:08:43 +0800 Subject: [PATCH 0889/1190] chore(deps-dev): bump eslint-plugin-unicorn from 74.0.0 to 75.0.0 (#23338) * chore(deps-dev): bump eslint-plugin-unicorn from 74.0.0 to 75.0.0 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 74.0.0 to 75.0.0. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v74.0.0...v75.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-version: 75.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * style: fix unicorn-js/prefer-combined-guards * style: fix prefer-smaller-scope * style: fix prefer-continue * style: fix prefer-early-return * style: fix prefer-iterator-helpers * style: add v75 recommended preset --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tony --- .oxlintrc.json | 9 +- eslint-plugins/nsfw-flag.js | 114 +++++------ lib/api/radar/rules/utils.ts | 37 ++-- lib/middleware/anti-hotlink.ts | 68 ++++--- lib/middleware/template.tsx | 23 ++- lib/registry-helpers.ts | 30 +-- lib/routes/4khd/article.ts | 8 +- lib/routes/6v123/utils.ts | 8 +- lib/routes/ali213/news.ts | 26 +-- lib/routes/ally/rail.ts | 2 +- lib/routes/bbc/learningenglish.ts | 2 +- lib/routes/bbc/utils.tsx | 7 +- lib/routes/bestofjs/monthly.tsx | 2 +- lib/routes/bilibili/cache.ts | 7 +- lib/routes/bilibili/wasm-exec.ts | 12 +- lib/routes/bloomberg/utils.ts | 24 ++- lib/routes/blowstudio/work.ts | 2 +- lib/routes/buaa/sme.ts | 2 +- lib/routes/bwu/news.ts | 2 +- lib/routes/caixin/utils-fulltext.ts | 5 +- lib/routes/capitalmind/utils.ts | 18 +- lib/routes/cczu/jwc.ts | 2 +- lib/routes/cczu/news.ts | 2 +- lib/routes/cfr/utils.ts | 20 +- lib/routes/cgtn/opinions.ts | 2 +- lib/routes/chinadegrees/province.tsx | 2 +- lib/routes/chinamoney/notice.ts | 8 +- lib/routes/chnmuseum/zl.tsx | 26 +-- lib/routes/cnki/author.ts | 2 +- lib/routes/consumer/shopping-guide.ts | 8 +- lib/routes/coomer/index.tsx | 8 +- lib/routes/cqust/jw.ts | 2 +- lib/routes/cqust/lib.ts | 2 +- lib/routes/daodu/index.ts | 2 +- lib/routes/deadline/posts.tsx | 10 +- lib/routes/dgut/xsc.ts | 2 +- lib/routes/discuz/discuz.ts | 10 +- lib/routes/domp4/utils.ts | 7 +- lib/routes/douban/other/discussion.ts | 2 +- lib/routes/dribbble/utils.tsx | 14 +- lib/routes/dxy/utils.ts | 8 +- lib/routes/ehentai/ehapi.ts | 76 +++---- lib/routes/eprice/rss.tsx | 22 +- lib/routes/espn/news.tsx | 20 +- lib/routes/fx678/kx.ts | 2 +- lib/routes/google/news.tsx | 2 +- lib/routes/gov/beijing/mhc.ts | 2 +- lib/routes/gov/ccdi/utils.ts | 20 +- lib/routes/gov/nsfc/index.ts | 2 +- lib/routes/govopendata/xinwenlianbo.ts | 2 +- lib/routes/huijin-inv/news.ts | 14 +- lib/routes/iapp/news.ts | 10 +- lib/routes/inuki-ichiba/utils.ts | 5 +- lib/routes/ithome/zt.tsx | 8 +- lib/routes/jike/topic-text.ts | 26 +-- lib/routes/jike/topic.ts | 64 +++--- lib/routes/kaoyan/kaoyan.ts | 2 +- lib/routes/kemono/index.tsx | 22 +- lib/routes/kenshin/index.ts | 2 +- lib/routes/keylol/index.ts | 10 +- .../kleinanzeigen/utils/get-product-page.ts | 2 +- lib/routes/latexstudio/home.ts | 2 +- lib/routes/lfsyd/utils.tsx | 20 +- lib/routes/lifetimes/index.ts | 2 +- lib/routes/locals/feed.ts | 9 +- lib/routes/lorientlejour/index.tsx | 8 +- lib/routes/mashiro/index.ts | 2 +- lib/routes/metacritic/release.ts | 2 +- lib/routes/nankai/graduate-notice.ts | 16 +- lib/routes/nature/utils.ts | 44 ++-- lib/routes/naver/search.ts | 12 +- lib/routes/ncu/jwc.ts | 8 +- lib/routes/neu/yz.ts | 28 +-- lib/routes/nextjs/blog.ts | 2 +- lib/routes/notion/release.ts | 2 +- lib/routes/pawchive/index.tsx | 14 +- lib/routes/picnob/user.ts | 4 +- lib/routes/pixiv/novel-series.ts | 5 +- lib/routes/pixiv/pixiv-got.ts | 22 +- lib/routes/pixivision/utils.ts | 40 ++-- lib/routes/plurk/utils.ts | 8 +- lib/routes/proletar/index.ts | 2 +- lib/routes/qnap/release-notes.ts | 2 +- lib/routes/qzcea/index.ts | 2 +- lib/routes/react/blog.ts | 2 +- lib/routes/rsshub/transform/html.ts | 2 +- lib/routes/saraba1st/digest.tsx | 14 +- lib/routes/saraba1st/thread.ts | 2 +- lib/routes/sigsac/ccs.ts | 2 +- lib/routes/simpread/changelog.ts | 16 +- lib/routes/sis001/forum.ts | 2 +- lib/routes/smzdm/utils.ts | 10 +- lib/routes/snnu/ccs.ts | 2 +- lib/routes/snnu/index.ts | 2 +- lib/routes/snnu/yjs.ts | 2 +- lib/routes/socialbeta/home.ts | 2 +- lib/routes/sohu/mp.tsx | 8 +- lib/routes/solidot/_article.ts | 3 +- lib/routes/techpowerup/utils.ts | 8 +- lib/routes/telegram/channel-media.ts | 5 +- lib/routes/telegram/channel.ts | 28 +-- lib/routes/telegram/tglib/channel.ts | 31 +-- lib/routes/temposmart/utils.ts | 5 +- lib/routes/the/index.ts | 8 +- lib/routes/theinitium/utils.ts | 5 +- lib/routes/treasury/press-releases.ts | 2 +- lib/routes/twitter/api/developer-api/api.ts | 5 +- lib/routes/twitter/api/mobile-api/api.ts | 76 +++---- lib/routes/twitter/api/web-api/utils.ts | 76 +++---- lib/routes/twitter/utils.ts | 12 +- lib/routes/weibo/utils.ts | 13 +- lib/routes/wenxuecity/bbs.ts | 2 +- lib/routes/wenxuecity/hot.ts | 2 +- lib/routes/wenxuecity/news.ts | 2 +- lib/routes/whu/cs.ts | 12 +- lib/routes/wikipedia/current-events.ts | 19 +- lib/routes/worldjournal/index.ts | 8 +- lib/routes/xiaohongshu/util.ts | 32 +-- lib/routes/xjtu/zs.ts | 2 +- lib/routes/yahoo/author.ts | 8 +- lib/routes/yahoo/news/utils.tsx | 48 +++-- lib/routes/yamibo/utils.ts | 20 +- lib/routes/youtube/utils.tsx | 13 +- lib/routes/yuque/book.ts | 17 +- lib/routes/zhihu/execlib/x-zse-96-v3.ts | 7 +- lib/routes/zhihu/utils.ts | 18 +- lib/routes/zhishifenzi/innovation.ts | 2 +- lib/routes/zzu/dzb.ts | 2 +- lib/routes/zzu/gs.ts | 2 +- lib/routes/zzu/jwc.ts | 2 +- lib/routes/zzu/kjc.ts | 2 +- lib/routes/zzu/math.ts | 2 +- lib/routes/zzu/news.ts | 2 +- lib/routes/zzu/rsc.ts | 2 +- lib/routes/zzu/ss.ts | 2 +- lib/routes/zzu/student.ts | 2 +- lib/routes/zzu/sxy.ts | 4 +- lib/routes/zzu/tzhb.ts | 2 +- lib/utils/cache/http.test.ts | 5 +- lib/utils/cache/kv.ts | 16 +- lib/utils/cache/redis.ts | 14 +- lib/utils/evaluate-script.ts | 20 +- lib/utils/header-generator.ts | 18 +- lib/utils/parse-date.ts | 11 +- lib/utils/proxy/multi-proxy.ts | 50 +++-- lib/utils/wechat-mp.ts | 44 ++-- package.json | 2 +- pnpm-lock.yaml | 188 ++++++++---------- scripts/workflow/build-routes.ts | 37 ++-- .../workflow/test-issue/call-maintainer.mjs | 23 ++- tests/cache-atomic-backends.test.ts | 8 +- 151 files changed, 1123 insertions(+), 1008 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 08ae09ef4ad7..0c7e2ae92097 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -195,6 +195,7 @@ "unicorn-js/no-array-sort-for-min-max": "error", "unicorn-js/no-array-splice": "error", "unicorn-js/no-async-promise-finally": "error", + "unicorn-js/no-async-iterator-callback": "error", // "unicorn/no-await-expression-member": "error", "unicorn/no-await-in-promise-methods": "error", // "unicorn-js/no-blob-to-file": "error", // unused @@ -292,7 +293,8 @@ "unicorn-js/no-unsafe-property-key": "error", // "unicorn-js/no-unsafe-sqlite-interpolation": "error", // unused "unicorn-js/no-unsafe-string-replacement": "error", - "unicorn-js/no-unused-array-method-return": "error", + "unicorn-js/no-unused-builtin-method-return": "error", + "unicorn-js/no-unused-iterator-helper": "error", "unicorn-js/no-useless-boolean-cast": "error", "unicorn-js/no-useless-coercion": "error", "unicorn/no-useless-collection-argument": "error", @@ -311,9 +313,11 @@ "unicorn-js/no-useless-re-export": "error", // "unicorn-js/no-useless-recursion": "error", unopinionated "unicorn/no-useless-spread": "error", + "unicorn-js/no-useless-set-construction": "error", // "unicorn/no-useless-switch-case": "error", "unicorn-js/no-useless-template-literals": "error", // "unicorn/no-useless-undefined": "error", + "unicorn-js/no-using-resource-escape": "error", "unicorn-js/no-xor-as-exponentiation": "error", "unicorn/no-zero-fractions": "error", // "unicorn/number-literal-case": "error", @@ -342,6 +346,7 @@ "unicorn-js/prefer-block-statement-over-iife": "error", "unicorn-js/prefer-boolean-return": "error", "unicorn/prefer-class-fields": "error", + "unicorn-js/prefer-combined-guards": "error", // "unicorn/prefer-classlist-toggle": "error", // unused // "unicorn/prefer-code-point": "error", "unicorn-js/prefer-continue": "error", @@ -372,6 +377,7 @@ "unicorn-js/prefer-iterable-in-constructor": "error", "unicorn-js/prefer-iterator-helpers": "error", "unicorn-js/prefer-iterator-to-array": "error", + "unicorn-js/prefer-iterator-zip": "error", // "unicorn-js/prefer-iterator-to-array-at-end": "error", // "unicorn/prefer-keyboard-event-key": "error", // unused "unicorn-js/prefer-location-assign": "error", @@ -431,6 +437,7 @@ // "unicorn/prefer-string-starts-ends-with": "error", use typescript/prefer-string-starts-ends-with "unicorn/prefer-string-trim-start-end": "error", "unicorn/prefer-structured-clone": "error", + "unicorn-js/prefer-temporal-conversion": "error", // "unicorn/prefer-switch": "error", "unicorn/prefer-ternary": "error", "unicorn-js/prefer-then-catch": "error", diff --git a/eslint-plugins/nsfw-flag.js b/eslint-plugins/nsfw-flag.js index a4cbc5eb19fa..1a0e7926d99e 100644 --- a/eslint-plugins/nsfw-flag.js +++ b/eslint-plugins/nsfw-flag.js @@ -141,71 +141,73 @@ export default eslintCompatPlugin({ const routeDeclaration = node.declaration.declarations[0]; const routeObject = routeDeclaration.init; - if (routeObject && routeObject.type === 'ObjectExpression') { - let featuresProperty = null; - let nsfwProperty = null; + if (!routeObject || routeObject.type !== 'ObjectExpression') { + return; + } - // 查找 features 属性 - for (const prop of routeObject.properties) { - if (!(prop.type === 'Property' && prop.key && prop.key.name === 'features')) { - continue; - } + let featuresProperty = null; + let nsfwProperty = null; - featuresProperty = prop; + // 查找 features 属性 + for (const prop of routeObject.properties) { + if (!(prop.type === 'Property' && prop.key && prop.key.name === 'features')) { + continue; + } - // 在 features 中查找 nsfw 属性 - if (prop.value && prop.value.type === 'ObjectExpression') { - for (const featureProp of prop.value.properties) { - if (!(featureProp.type === 'Property' && featureProp.key && featureProp.key.name === 'nsfw')) { - continue; - } + featuresProperty = prop; - nsfwProperty = featureProp; - break; + // 在 features 中查找 nsfw 属性 + if (prop.value && prop.value.type === 'ObjectExpression') { + for (const featureProp of prop.value.properties) { + if (!(featureProp.type === 'Property' && featureProp.key && featureProp.key.name === 'nsfw')) { + continue; } + + nsfwProperty = featureProp; + break; } - break; } + break; + } - // 检查是否需要添加或修复 nsfw 标志 - if (!featuresProperty) { - // 没有 features 属性,需要添加整个 features 对象 - context.report({ - node: routeObject, - messageId: 'missingNsfwFlag', - fix(fixer) { - // 在对象的最后添加 features 属性 - const lastProperty = routeObject.properties.at(-1); + // 检查是否需要添加或修复 nsfw 标志 + if (!featuresProperty) { + // 没有 features 属性,需要添加整个 features 对象 + context.report({ + node: routeObject, + messageId: 'missingNsfwFlag', + fix(fixer) { + // 在对象的最后添加 features 属性 + const lastProperty = routeObject.properties.at(-1); - return lastProperty - ? fixer.insertTextAfter(lastProperty, ',\n features: {\n nsfw: true,\n }') - : // 空对象的情况 - fixer.insertTextAfter(routeObject.properties.length > 0 ? routeObject.properties.at(-1) : routeObject, '\n features: {\n nsfw: true,\n }\n'); - }, - }); - } else if (!nsfwProperty) { - // 有 features 属性但没有 nsfw 属性 - context.report({ - node: featuresProperty.value, - messageId: 'missingNsfwFlag', - fix(fixer) { - const featuresObject = featuresProperty.value; - if (featuresObject.properties.length > 0) { - const lastFeatureProp = featuresObject.properties.at(-1); - return fixer.insertTextAfter(lastFeatureProp, ',\n nsfw: true'); - } - // features 是空对象 - return fixer.replaceTextRange([featuresObject.range[0] + 1, featuresObject.range[1] - 1], '\n nsfw: true,\n '); - }, - }); - } else if (nsfwProperty.value && (nsfwProperty.value.type !== 'Literal' || nsfwProperty.value.value !== true)) { - // nsfw 属性存在但不是 true - context.report({ - node: nsfwProperty.value, - messageId: 'missingNsfwFlag', - fix: (fixer) => fixer.replaceText(nsfwProperty.value, 'true'), - }); - } + return lastProperty + ? fixer.insertTextAfter(lastProperty, ',\n features: {\n nsfw: true,\n }') + : // 空对象的情况 + fixer.insertTextAfter(routeObject.properties.length > 0 ? routeObject.properties.at(-1) : routeObject, '\n features: {\n nsfw: true,\n }\n'); + }, + }); + } else if (!nsfwProperty) { + // 有 features 属性但没有 nsfw 属性 + context.report({ + node: featuresProperty.value, + messageId: 'missingNsfwFlag', + fix(fixer) { + const featuresObject = featuresProperty.value; + if (featuresObject.properties.length > 0) { + const lastFeatureProp = featuresObject.properties.at(-1); + return fixer.insertTextAfter(lastFeatureProp, ',\n nsfw: true'); + } + // features 是空对象 + return fixer.replaceTextRange([featuresObject.range[0] + 1, featuresObject.range[1] - 1], '\n nsfw: true,\n '); + }, + }); + } else if (nsfwProperty.value && (nsfwProperty.value.type !== 'Literal' || nsfwProperty.value.value !== true)) { + // nsfw 属性存在但不是 true + context.report({ + node: nsfwProperty.value, + messageId: 'missingNsfwFlag', + fix: (fixer) => fixer.replaceText(nsfwProperty.value, 'true'), + }); } }, }), diff --git a/lib/api/radar/rules/utils.ts b/lib/api/radar/rules/utils.ts index 7291c281686c..0c8b92161600 100644 --- a/lib/api/radar/rules/utils.ts +++ b/lib/api/radar/rules/utils.ts @@ -21,25 +21,26 @@ export const getRadarRules = async (): Promise> => { const parsedDomain = parse(new URL('https://' + radarItem.source[0]).hostname); const subdomain = parsedDomain.subdomain || '.'; const domain = parsedDomain.domain; - if (domain) { - if (!Object.hasOwn(rules, domain)) { - rules[domain] = { - _name: namespaces[namespace].name, - } as RadarDomain; - } - if (!Object.hasOwn(rules[domain], subdomain)) { - rules[domain][subdomain] = []; - } - rules[domain][subdomain].push({ - title: radarItem.title || data.name, - docs: `https://docs.rsshub.app/routes/${data.categories?.[0] || 'other'}`, - source: radarItem.source.map((source) => { - const sourceURL = new URL('https://' + source); - return sourceURL.pathname + sourceURL.search + sourceURL.hash; - }), - target: radarItem.target ? `/${namespace}${radarItem.target}` : realPath, - }); + if (!domain) { + continue; } + if (!Object.hasOwn(rules, domain)) { + rules[domain] = { + _name: namespaces[namespace].name, + } as RadarDomain; + } + if (!Object.hasOwn(rules[domain], subdomain)) { + rules[domain][subdomain] = []; + } + rules[domain][subdomain].push({ + title: radarItem.title || data.name, + docs: `https://docs.rsshub.app/routes/${data.categories?.[0] || 'other'}`, + source: radarItem.source.map((source) => { + const sourceURL = new URL('https://' + source); + return sourceURL.pathname + sourceURL.search + sourceURL.hash; + }), + target: radarItem.target ? `/${namespace}${radarItem.target}` : realPath, + }); } } } diff --git a/lib/middleware/anti-hotlink.ts b/lib/middleware/anti-hotlink.ts index c028f701599a..ab307d1d6335 100644 --- a/lib/middleware/anti-hotlink.ts +++ b/lib/middleware/anti-hotlink.ts @@ -64,12 +64,14 @@ const replaceUrl = (template?: string, url?: string) => { const replaceUrls = ($: CheerioAPI, selector: string, template: string, attribute = 'src') => { $(selector).each((_, el) => { const oldSrc = $(el).attr(attribute); - if (oldSrc) { - const url = parseUrl(oldSrc); - if (url && url.protocol !== 'data:') { - // Cheerio will do the right thing to prohibit XSS. - $(el).attr(attribute, interpolate(template, url)); - } + if (!oldSrc) { + return; + } + + const url = parseUrl(oldSrc); + if (url && url.protocol !== 'data:') { + // Cheerio will do the right thing to prohibit XSS. + $(el).attr(attribute, interpolate(template, url)); } }); }; @@ -137,37 +139,39 @@ const middleware: MiddlewareHandler = async (ctx, next) => { // Use Cheerio to load the description as html and filter all // image link const data: Data = ctx.get('data'); - if (data) { - if (data.image) { - data.image = replaceUrl(imageHotlinkTemplate, data.image); - } - if (data.description) { - data.description = process(data.description, imageHotlinkTemplate, multimediaHotlinkTemplate); - } + if (!data) { + return; + } - if (data.item) { - for (const item of data.item) { - if (item.description) { - item.description = process(item.description, imageHotlinkTemplate, multimediaHotlinkTemplate); - } - if (item.enclosure_url && item.enclosure_type) { - if (item.enclosure_type.startsWith('image/')) { - item.enclosure_url = replaceUrl(imageHotlinkTemplate, item.enclosure_url); - } else if (/^(?:video|audio)\//.test(item.enclosure_type)) { - item.enclosure_url = replaceUrl(multimediaHotlinkTemplate, item.enclosure_url); - } - } - if (item.image) { - item.image = replaceUrl(imageHotlinkTemplate, item.image); - } - if (item.itunes_item_image) { - item.itunes_item_image = replaceUrl(imageHotlinkTemplate, item.itunes_item_image); + if (data.image) { + data.image = replaceUrl(imageHotlinkTemplate, data.image); + } + if (data.description) { + data.description = process(data.description, imageHotlinkTemplate, multimediaHotlinkTemplate); + } + + if (data.item) { + for (const item of data.item) { + if (item.description) { + item.description = process(item.description, imageHotlinkTemplate, multimediaHotlinkTemplate); + } + if (item.enclosure_url && item.enclosure_type) { + if (item.enclosure_type.startsWith('image/')) { + item.enclosure_url = replaceUrl(imageHotlinkTemplate, item.enclosure_url); + } else if (/^(?:video|audio)\//.test(item.enclosure_type)) { + item.enclosure_url = replaceUrl(multimediaHotlinkTemplate, item.enclosure_url); } } + if (item.image) { + item.image = replaceUrl(imageHotlinkTemplate, item.image); + } + if (item.itunes_item_image) { + item.itunes_item_image = replaceUrl(imageHotlinkTemplate, item.itunes_item_image); + } } - - ctx.set('data', data); } + + ctx.set('data', data); }; export default middleware; diff --git a/lib/middleware/template.tsx b/lib/middleware/template.tsx index cf2648524806..a97726b7ab21 100644 --- a/lib/middleware/template.tsx +++ b/lib/middleware/template.tsx @@ -79,17 +79,18 @@ const middleware: MiddlewareHandler = async (ctx, next) => { Math.floor(item.itunes_duration / 3600) + ':' + (Math.floor((item.itunes_duration % 3600) / 60) / 100).toFixed(2).slice(-2) + ':' + (((item.itunes_duration % 3600) % 60) / 100).toFixed(2).slice(-2); } - if (outputType !== 'rss') { - try { - item.pubDate &&= convertDateToISO8601(item.pubDate) || ''; - } catch { - item.pubDate = ''; - } - try { - item.updated &&= convertDateToISO8601(item.updated) || ''; - } catch { - item.updated = ''; - } + if (outputType === 'rss') { + continue; + } + try { + item.pubDate &&= convertDateToISO8601(item.pubDate) || ''; + } catch { + item.pubDate = ''; + } + try { + item.updated &&= convertDateToISO8601(item.updated) || ''; + } catch { + item.updated = ''; } } } diff --git a/lib/registry-helpers.ts b/lib/registry-helpers.ts index 5532f0057b08..23899d3fb788 100644 --- a/lib/registry-helpers.ts +++ b/lib/registry-helpers.ts @@ -174,22 +174,24 @@ export function registerRssRoutes(app: Hono, namespaces: NamespacesType): void { for (const [path, routeData] of sortedRoutes) { const wrappedHandler: Handler = async (ctx) => { logger.debug(`Matched route: ${routePath(ctx)}`); - if (!ctx.get('data')) { - if (!routeData.handler) { - if (process.env.NODE_ENV === 'test') { - const { route } = await import(`./routes/${namespace}/${routeData.location}`); - routeData.handler = route.handler; - } else if (routeData.module) { - const { route } = await routeData.module(); - routeData.handler = route.handler; - } - } - const response = await routeData.handler(ctx); - if (response instanceof Response) { - return response; + if (ctx.get('data')) { + return; + } + + if (!routeData.handler) { + if (process.env.NODE_ENV === 'test') { + const { route } = await import(`./routes/${namespace}/${routeData.location}`); + routeData.handler = route.handler; + } else if (routeData.module) { + const { route } = await routeData.module(); + routeData.handler = route.handler; } - ctx.set('data', response); } + const response = await routeData.handler(ctx); + if (response instanceof Response) { + return response; + } + ctx.set('data', response); }; subApp.get(path, wrappedHandler); } diff --git a/lib/routes/4khd/article.ts b/lib/routes/4khd/article.ts index 5f8ddf8ea0cb..575700ea78a3 100644 --- a/lib/routes/4khd/article.ts +++ b/lib/routes/4khd/article.ts @@ -8,10 +8,12 @@ const processImages = ($) => { $('a').each((_, elem) => { const $elem = $(elem); const largePhotoUrl = $elem.attr('href')?.replace('i0.wp.com', '').replace('pic.4khd.com', 'yt4.googleusercontent.com').replace('AsHYQ', 'AsYHQ').replace('l/AAA', 'I/AAA'); - if (largePhotoUrl) { - $elem.attr('href', largePhotoUrl); - $elem.find('img').attr('src', largePhotoUrl); + if (!largePhotoUrl) { + return; } + + $elem.attr('href', largePhotoUrl); + $elem.find('img').attr('src', largePhotoUrl); }); }; diff --git a/lib/routes/6v123/utils.ts b/lib/routes/6v123/utils.ts index 7ea686a78d3d..c9af0f9a43ef 100644 --- a/lib/routes/6v123/utils.ts +++ b/lib/routes/6v123/utils.ts @@ -45,12 +45,8 @@ export async function processItems(ctx, baseURL, exclude) { const pubDate = timezone(parseDate($(item).find('span').text().replaceAll(/[[\]]/g, ''), 'MM-DD'), 8); const text = link.text(); - if (href === undefined) { - return; - } - - if (exclude && exclude.some((e) => e.test(text))) { - // 过滤掉满足正则的标题条目 + // 过滤掉满足正则的标题条目 + if (href === undefined || (exclude && exclude.some((e) => e.test(text)))) { return; } diff --git a/lib/routes/ali213/news.ts b/lib/routes/ali213/news.ts index b33089a797a0..8dc4ced12f02 100644 --- a/lib/routes/ali213/news.ts +++ b/lib/routes/ali213/news.ts @@ -97,19 +97,21 @@ export const handler = async (ctx: Context): Promise => { const mediaUrl: string | undefined = $$el.prop('src'); const mediaType: string | undefined = mediaUrl?.split(/\./).pop(); - if (mediaType && mediaUrl) { - media[mediaType] = { url: mediaUrl }; - - pEl.replaceWith( - renderDescription({ - images: [ - { - src: mediaUrl, - }, - ], - }) - ); + if (!(mediaType && mediaUrl)) { + return; } + + media[mediaType] = { url: mediaUrl }; + + pEl.replaceWith( + renderDescription({ + images: [ + { + src: mediaUrl, + }, + ], + }) + ); }); } diff --git a/lib/routes/ally/rail.ts b/lib/routes/ally/rail.ts index 8c47779818ae..315808c3d126 100644 --- a/lib/routes/ally/rail.ts +++ b/lib/routes/ally/rail.ts @@ -42,7 +42,7 @@ async function handler(ctx) { const response = await got.get(pageUrl); const $ = load(response.data); let title = ''; - const titleLinks = $('.container .regsiter a').toArray().slice(1); // what a typo... drop "首页" + const titleLinks = $('.container .regsiter a').slice(1).toArray(); // what a typo... drop "首页" for (const link of titleLinks) { const linkText = $(link).text(); title = title ? `${title} - ${linkText}` : linkText; diff --git a/lib/routes/bbc/learningenglish.ts b/lib/routes/bbc/learningenglish.ts index 048959b7caff..1cdfb9e88561 100644 --- a/lib/routes/bbc/learningenglish.ts +++ b/lib/routes/bbc/learningenglish.ts @@ -54,8 +54,8 @@ async function handler(ctx: Context) { // get rest ul article links const restItems: DataItem[] = $('.threecol li') - .toArray() .slice(0, 10) + .toArray() .map((article) => { const $article = load(article); diff --git a/lib/routes/bbc/utils.tsx b/lib/routes/bbc/utils.tsx index 065e103f59c3..1a993778fee9 100644 --- a/lib/routes/bbc/utils.tsx +++ b/lib/routes/bbc/utils.tsx @@ -295,10 +295,11 @@ const renderEmbedImages = (block: Block, index: number): JSX.Element | null => { const rawImageBlock = findBlocksByType(imageBlock.model?.blocks ?? imageBlock.blocks ?? imageBlock.items, 'rawImage')[0]; const width = rawImageBlock?.model?.width; - if (width && width > maxWidth) { - maxWidth = width; - largestImage = imageBlock; + if (!(width && width > maxWidth)) { + continue; } + maxWidth = width; + largestImage = imageBlock; } if (!largestImage) { diff --git a/lib/routes/bestofjs/monthly.tsx b/lib/routes/bestofjs/monthly.tsx index 7b247b44c199..db3969867c8a 100644 --- a/lib/routes/bestofjs/monthly.tsx +++ b/lib/routes/bestofjs/monthly.tsx @@ -170,8 +170,8 @@ const getMonthlyRankings = (year: string, month: string): Promise // Additional info (contributors, created date) const additionalInfo = $tr .find('td:nth-child(3) > div') - .toArray() .slice(1) + .toArray() .map((el) => $(el).text().trim()) .join('; '); return { diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index 9100d703a666..8166cb3507f6 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -28,10 +28,11 @@ const getConfiguredCookie = () => { // Update b_lsid in cookies for (const key of Object.keys(config.bilibili.cookies)) { const cookie = config.bilibili.cookies[key]; - if (cookie) { - const updatedCookie = cookie.replace(/b_lsid=[0-9A-F]+_[0-9A-F]+/, () => `b_lsid=${utils.lsid()}`); - config.bilibili.cookies[key] = updatedCookie; + if (!cookie) { + continue; } + const updatedCookie = cookie.replace(/b_lsid=[0-9A-F]+_[0-9A-F]+/, () => `b_lsid=${utils.lsid()}`); + config.bilibili.cookies[key] = updatedCookie; } return config.bilibili.cookies[Object.keys(config.bilibili.cookies)[Math.floor(Math.random() * Object.keys(config.bilibili.cookies).length)]] || ''; diff --git a/lib/routes/bilibili/wasm-exec.ts b/lib/routes/bilibili/wasm-exec.ts index 9a142d8cfb90..a14b739bbafd 100644 --- a/lib/routes/bilibili/wasm-exec.ts +++ b/lib/routes/bilibili/wasm-exec.ts @@ -392,12 +392,14 @@ sp >>>= 0; const id = this.mem.getUint32(sp + 8, true); this._goRefCounts[id]--; - if (this._goRefCounts[id] === 0) { - const v = this._values[id]; - this._values[id] = null; - this._ids.delete(v); - this._idPool.push(id); + if (this._goRefCounts[id] !== 0) { + return; } + + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); }, // func stringVal(value string) ref diff --git a/lib/routes/bloomberg/utils.ts b/lib/routes/bloomberg/utils.ts index ee1c4459a987..136fce9c9cd6 100644 --- a/lib/routes/bloomberg/utils.ts +++ b/lib/routes/bloomberg/utils.ts @@ -287,18 +287,20 @@ const processLedeMedia = async (story_json) => { } return ''; } - if (story_json.type === 'Lede') { - const props = story_json.props; - - const media = { - kind: props.media, - caption: props.caption?.replaceAll(capRegex, '') ?? '', - description: props.dek?.replaceAll(capRegex, '') ?? '', - credit: props.credit?.replaceAll(capRegex, '') ?? '', - src: props.url, - }; - return renderLedeMedia(media); + if (story_json.type !== 'Lede') { + return; } + + const props = story_json.props; + + const media = { + kind: props.media, + caption: props.caption?.replaceAll(capRegex, '') ?? '', + description: props.dek?.replaceAll(capRegex, '') ?? '', + credit: props.credit?.replaceAll(capRegex, '') ?? '', + src: props.url, + }; + return renderLedeMedia(media); }; const processBody = async (body_html, story_json) => { diff --git a/lib/routes/blowstudio/work.ts b/lib/routes/blowstudio/work.ts index 09f8fe09aeef..2ce08a68483a 100644 --- a/lib/routes/blowstudio/work.ts +++ b/lib/routes/blowstudio/work.ts @@ -17,7 +17,7 @@ async function handler() { const rootUrl = 'https://www.blowstudio.es'; const response = await ofetch(`${rootUrl}/work/`); const $ = load(response); - const list = $('.portfolios.normal > ul > li').toArray().slice(0, 10); + const list = $('.portfolios.normal > ul > li').slice(0, 10).toArray(); const videostyle = 'width="640" height="360"'; const imgstyle = 'style="max-width: 650px; height: auto; object-fit: contain; flex: 0 0 auto;"'; diff --git a/lib/routes/buaa/sme.ts b/lib/routes/buaa/sme.ts index ac30ae1c0d16..d61899352be3 100755 --- a/lib/routes/buaa/sme.ts +++ b/lib/routes/buaa/sme.ts @@ -66,8 +66,8 @@ async function getList(url) { const { data } = await got(url); const $ = load(data); const title = $('.nytit .fr a') - .toArray() .slice(1) + .toArray() .map((item) => $(item).text().trim()) .join(' - '); const list = $("div[class='Newslist'] > ul > li") diff --git a/lib/routes/bwu/news.ts b/lib/routes/bwu/news.ts index 351d7583f199..b4c6e8c0eecf 100644 --- a/lib/routes/bwu/news.ts +++ b/lib/routes/bwu/news.ts @@ -27,8 +27,8 @@ async function handler() { const $ = load(response); const list = $('div[class=subPage] div ul li') - .toArray() .slice(0, 25) + .toArray() .map((item) => { const $item = $(item); const aTag = $item.find('a'); diff --git a/lib/routes/caixin/utils-fulltext.ts b/lib/routes/caixin/utils-fulltext.ts index 1e80d7101430..1f905d5b423c 100644 --- a/lib/routes/caixin/utils-fulltext.ts +++ b/lib/routes/caixin/utils-fulltext.ts @@ -11,10 +11,7 @@ const rsaPrivateKey = '-----BEGIN PRIVATE KEY-----MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLci8q2u3NGFyFlMUwjCP91PsvGjHdRAq9fmqZLxvue+n+RhzNxnKKYOv35pLgFKWXsGq2TV+5Xrv6xZgNx36IUkqbmrO+eCa8NFmti04wvMfG3DCNdKA7Lue880daNiK3BOhlQlZPykUXt1NftMNS/z+e70W+Vpv1ZxCx5BipqZkdoceM3uin0vUQmqmHqjxi5qKUuov90dXLaMxypCA0TDsIDnX8RPvPtqKff1p2TMW2a0XYe7CPYhRggaQMpmo0TcFutgrM1Vywyr2TPxYR+H/tpuuWRET7tUIQykBYoO1WKfL2dX6cxarjAJfnYnod3sMzppHouyp8Pt7gHVG7AgMBAAECggEAEFshSy6IrADKgWSUyH/3jMNZfwnchW6Ar/9O847CAPQJ2yhQIpa/Qpnhs58Y5S2myqcHrUBgFPcWp3BbyGn43naAh8XahWHEcVjWl/N6BV9vM1UKYN0oGikDR3dljCBDbCIoPBBO3WcFOaXoIpaqPmbwCG1aSdwQyPUA0UzG08eDbuHK6L5jvbe3xv5kLpWTVddrocW+SakbZRAX1Ykp7IujOce235nM7GOfoq4b8jmK5CLg6VIZGQV20wnn9YxuFOndRSjneFberzfzBMhVLpPsQ16M2xDLpZaDTggZnq2L6nZygds8Hda++ga3WbD3TcgjJNYuENu1S88IowYhSQKBgQDFqRA+38mo6KsxVDCNWcuEk2hSq8NEUzRHJpS7/QjZmEIYpFzDXgSGwhZJ0WNsQtaxJeBbc7B/OOqh8TL1reLl5AdTimS1OLHWVf/MUsLVS7Y82hx/hpYWxZnRSq41oI3P8FO/53FiQMYo2wbwqF6uQjB1y8h58aqL3OYpTH/5xQKBgQC0mobALJ+bU4nCPzkVDZuD6RyNWPwS1aE3+925wDSN2rJ0iLIb4N5czWZmHb66VlAtfGbp2q+amsCV4r6UR19A/y8k9SFB0mdtxix6mjEfaGhVJm4B1mkvsn0OHMAanKkohUvCjROQc3sziyp2gqSEQ98G7//VMPx/3dhgyQpVfwKBgQCycsqu6N0n+D6t/0MCKiJaI7bYhCd7JN8aqVM4UN5PjG2Hz8PLwbK2cr0qkbaAA+vN7NMb3Vtn0FvMLnUCZqVlRTP0EQqQrYmoZuXUcpdhd8QkNgnqe/g+wND4qcKTucquA1uo8mtj9/Su5+bhGDC6hBk6D+uDZFHDiX/loyIavQKBgQCXF6AcLjjpDZ52b8Yloti0JtXIOuXILAlQeNoqiG5vLsOVUrcPM7VUFlLQo5no8kTpiOXgRyAaS9VKkAO4sW0zR0n9tUY5dvkokV6sw0rNZ9/BPQFTcDlXug99OvhMSzwJtlqHTNdNRg+QM6E2vF0+ejmf6DEz/mN/5e0cK5UFqQKBgCR2hVfbRtDz9Cm/P8chPqaWFkH5ulUxBpc704Igc6bVH5DrEoWo6akbeJixV2obAZO3sFyeJqBUqaCvqG17Xei6jn3Hc3WMz9nLrAJEI9BTCfwvuxCOyY0IxqAAYT28xYv42I4+ADT/PpCq2Dj5u43X0dapAjZBZDfVVis7q1Bw-----END PRIVATE KEY-----'; export async function getFulltext(url: string) { - if (!config.caixin.cookie) { - return; - } - if (!/\d+\.html/.test(url)) { + if (!config.caixin.cookie || !/\d+\.html/.test(url)) { return; } const articleID = url.match(/(\d+)\.html/)![1]; diff --git a/lib/routes/capitalmind/utils.ts b/lib/routes/capitalmind/utils.ts index 3a09455ef58c..156ddd7e72b9 100644 --- a/lib/routes/capitalmind/utils.ts +++ b/lib/routes/capitalmind/utils.ts @@ -98,14 +98,18 @@ export async function fetchArticles(path) { // Remove srcset attribute $img.removeAttr('srcset'); - if (src && src.startsWith('/_next/image')) { - // Extract the original URL from the Next.js image URL - const urlMatch = src.match(/url=([^&]+)/); - if (urlMatch && urlMatch[1]) { - const originalUrl = decodeURIComponent(urlMatch[1]); - $img.attr('src', originalUrl); - } + if (!src?.startsWith('/_next/image')) { + return; + } + + // Extract the original URL from the Next.js image URL + const urlMatch = src.match(/url=([^&]+)/); + if (!urlMatch?.[1]) { + return; } + + const originalUrl = decodeURIComponent(urlMatch[1]); + $img.attr('src', originalUrl); }); return { title, diff --git a/lib/routes/cczu/jwc.ts b/lib/routes/cczu/jwc.ts index 3762efc98a60..e1e260b2fd3f 100644 --- a/lib/routes/cczu/jwc.ts +++ b/lib/routes/cczu/jwc.ts @@ -38,8 +38,8 @@ async function handler(ctx: Context) { link: pageUrl, title: category === 'all' ? baseTitle : `${baseTitle} ${$('title').text()}`, item: $('div[id^="wp_news_w"] a') - .toArray() .slice(0, 10) + .toArray() .filter((elem) => entryUrlRegex.test(elem.attribs.href)) .map((elem) => ({ link: new URL(elem.attribs.href, pageUrl).href, diff --git a/lib/routes/cczu/news.ts b/lib/routes/cczu/news.ts index 052cfe3d2395..81539a76addf 100644 --- a/lib/routes/cczu/news.ts +++ b/lib/routes/cczu/news.ts @@ -35,8 +35,8 @@ async function handler(ctx: Context) { link: pageUrl, title: category === 'all' ? baseTitle : `${baseTitle} ${$('title').text()}`, item: $('.news_title a') - .toArray() .slice(0, 10) + .toArray() .filter((elem) => entryUrlRegex.test(elem.attribs.href)) .map((elem) => ({ link: new URL(elem.attribs.href, pageUrl).href, diff --git a/lib/routes/cfr/utils.ts b/lib/routes/cfr/utils.ts index 0d688cce246d..53e6ed635904 100644 --- a/lib/routes/cfr/utils.ts +++ b/lib/routes/cfr/utils.ts @@ -245,14 +245,18 @@ function parseLinkData($: CheerioAPI) { function getVideoIframe($ele: Cheerio) { const setup = $ele.find('video').data('setup') as VideoSetup; - if (setup) { - const youtubeSource = setup.sources.find((i) => i.type === 'video/youtube'); - if (youtubeSource) { - const videoId = youtubeSource.src.match(/\?v=([^&]+)/)?.[1]; - if (videoId) { - return ``; - } - } + if (!setup) { + return; + } + + const youtubeSource = setup.sources.find((i) => i.type === 'video/youtube'); + if (!youtubeSource) { + return; + } + + const videoId = youtubeSource.src.match(/\?v=([^&]+)/)?.[1]; + if (videoId) { + return ``; } } diff --git a/lib/routes/cgtn/opinions.ts b/lib/routes/cgtn/opinions.ts index 4232d8fd8244..3af753162c92 100644 --- a/lib/routes/cgtn/opinions.ts +++ b/lib/routes/cgtn/opinions.ts @@ -23,8 +23,8 @@ async function handler() { $('.cg-pic').parent().remove(); const list = $('.cg-title h3') - .toArray() .slice(0, 15) + .toArray() .map((item): DataItem => { const a = $(item).find('a'); return { diff --git a/lib/routes/chinadegrees/province.tsx b/lib/routes/chinadegrees/province.tsx index 84bd0c2c64ab..8b3ff7eab5d9 100644 --- a/lib/routes/chinadegrees/province.tsx +++ b/lib/routes/chinadegrees/province.tsx @@ -100,8 +100,8 @@ async function handler(ctx) { return { title: $('caption').text().trim(), items: $('.datalist tr') - .toArray() .slice(1) + .toArray() .map((item): DataItem => { const $item = $(item); const title = $item.find('td').eq(1).text(); diff --git a/lib/routes/chinamoney/notice.ts b/lib/routes/chinamoney/notice.ts index 5c8ce37deb45..aecedb23b0a3 100644 --- a/lib/routes/chinamoney/notice.ts +++ b/lib/routes/chinamoney/notice.ts @@ -94,10 +94,12 @@ async function handler(ctx) { }); article.find('.article-a-attach-body a').each((i, ele) => { const $ele = $(ele); - if ($ele.attr('onclick')?.startsWith("location.href=encodeURI($('#fileDownUrl').val()+'fileDownLoad.do")) { - $ele.attr('href', `${baseUrl}/dqs/cm-s-notice-query/fileDownLoad.do?mode=open&contentId=${item.contentId}&priority=${i}`); - $ele.removeAttr('onclick'); + if (!$ele.attr('onclick')?.startsWith("location.href=encodeURI($('#fileDownUrl').val()+'fileDownLoad.do")) { + return; } + + $ele.attr('href', `${baseUrl}/dqs/cm-s-notice-query/fileDownLoad.do?mode=open&contentId=${item.contentId}&priority=${i}`); + $ele.removeAttr('onclick'); }); item.description = article.html(); diff --git a/lib/routes/chnmuseum/zl.tsx b/lib/routes/chnmuseum/zl.tsx index 6c93e1a1cff8..b2b5d8c50dae 100644 --- a/lib/routes/chnmuseum/zl.tsx +++ b/lib/routes/chnmuseum/zl.tsx @@ -111,19 +111,21 @@ const fetchTargetElements = async (cleanType: string, subtype: string | undefine for (const el of pageElements) { const $item = $(el).closest('li'); - if ($item.length > 0) { - const rawLink = $(el).attr('href') || ''; - const rawZtzl = $(el).attr('ztzlurl') || ''; // some exhibition links have a separate detailed page, use ztzlurl to get the detailed exhibition link if available - - // Use exhibitionLink to remove the repeat ones - const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); - const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); - - if (exhibitionLink && !seenLinks.has(exhibitionLink)) { - seenLinks.add(exhibitionLink); - items.push({ $item, contextUrl, itemLink, exhibitionLink, rawZtzl }); - } + if ($item.length === 0) { + continue; } + const rawLink = $(el).attr('href') || ''; + const rawZtzl = $(el).attr('ztzlurl') || ''; // some exhibition links have a separate detailed page, use ztzlurl to get the detailed exhibition link if available + + // Use exhibitionLink to remove the repeat ones + const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); + const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); + + if (!exhibitionLink || seenLinks.has(exhibitionLink)) { + continue; + } + seenLinks.add(exhibitionLink); + items.push({ $item, contextUrl, itemLink, exhibitionLink, rawZtzl }); } }; diff --git a/lib/routes/cnki/author.ts b/lib/routes/cnki/author.ts index 1a9f0b43ee97..8fcdbdbd10fa 100644 --- a/lib/routes/cnki/author.ts +++ b/lib/routes/cnki/author.ts @@ -122,8 +122,8 @@ async function handler(ctx) { }); const $ = load(response); const list = $('tr') - .toArray() .slice(1) + .toArray() .map((item) => { const title = $(item).find('a.fz14').text(); const filename = $(item).find('a.icon-collect').attr('data-filename'); diff --git a/lib/routes/consumer/shopping-guide.ts b/lib/routes/consumer/shopping-guide.ts index b95dffc36111..518858c59bd8 100644 --- a/lib/routes/consumer/shopping-guide.ts +++ b/lib/routes/consumer/shopping-guide.ts @@ -66,10 +66,12 @@ async function handler(ctx) { const header = $('.article-img-blk'); header.find('img').each((_, ele) => { const $ele = $(ele); - if ($ele.attr('src') && $ele.attr('srcset')) { - $ele.removeAttr('srcset'); - $ele.attr('src', $ele.attr('src')!.replace(/\/\d+c\d+\//, '/0p0/')); + if (!($ele.attr('src') && $ele.attr('srcset'))) { + return; } + + $ele.removeAttr('srcset'); + $ele.attr('src', $ele.attr('src')!.replace(/\/\d+c\d+\//, '/0p0/')); }); item.description = header.html()! + $('article .ckec').html()!; diff --git a/lib/routes/coomer/index.tsx b/lib/routes/coomer/index.tsx index 81fc21ac9e9e..6116d96d1247 100644 --- a/lib/routes/coomer/index.tsx +++ b/lib/routes/coomer/index.tsx @@ -97,10 +97,12 @@ async function handler(ctx) { const regex = /downloads.fanbox.cc/; $('a').each((_, el) => { const link = $(el).attr('href'); - if (regex.test(link!)) { - count++; - $(el).replaceWith(coomerFiles[count]); + if (!regex.test(link!)) { + return; } + + count++; + $(el).replaceWith(coomerFiles[count]); }); desc = (coomerFiles.length > 0 ? coomerFiles[0] : '') + $.html(); const remainingCoomerFiles = coomerFiles.slice(count + 1); diff --git a/lib/routes/cqust/jw.ts b/lib/routes/cqust/jw.ts index 3153036ae4a3..ad5bac7f8632 100644 --- a/lib/routes/cqust/jw.ts +++ b/lib/routes/cqust/jw.ts @@ -36,8 +36,8 @@ async function handler(ctx: Context) { link, description: $('meta[name="description"]').attr('content') || '重科教务处', item: $('li[id^="lineu"]') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/cqust/lib.ts b/lib/routes/cqust/lib.ts index f938edcce55c..86cba0ef8bf9 100644 --- a/lib/routes/cqust/lib.ts +++ b/lib/routes/cqust/lib.ts @@ -39,8 +39,8 @@ async function handler(ctx: Context) { link, description: $('meta[name="description"]').attr('content') || '重科图书馆公告', item: $('div[class="newsList"] li') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/daodu/index.ts b/lib/routes/daodu/index.ts index 90bc432e3613..5c255076bdce 100644 --- a/lib/routes/daodu/index.ts +++ b/lib/routes/daodu/index.ts @@ -29,8 +29,8 @@ async function handler(ctx: Context) { const $ = load(response); const list: DataItem[] = $('h2[itemprop="headline"] a, .post-header .archive__article__title a') - .toArray() .slice(0, 15) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/deadline/posts.tsx b/lib/routes/deadline/posts.tsx index 0df0900673ea..b7bb851a14ab 100644 --- a/lib/routes/deadline/posts.tsx +++ b/lib/routes/deadline/posts.tsx @@ -55,11 +55,13 @@ async function handler(ctx): Promise { $('.c-lazy-image__img').each((_, img) => { const $img = $(img); - if ($img.attr('data-lazy-src')) { - $img.attr('src', $img.attr('data-lazy-src')!.split('?', 1)[0]); - $img.removeAttr('data-lazy-src'); - $img.removeAttr('data-lazy-srcset'); + if (!$img.attr('data-lazy-src')) { + return; } + + $img.attr('src', $img.attr('data-lazy-src')!.split('?', 1)[0]); + $img.removeAttr('data-lazy-src'); + $img.removeAttr('data-lazy-srcset'); }); $('[class^="lrv-a-crop-"]').contents().unwrap(); diff --git a/lib/routes/dgut/xsc.ts b/lib/routes/dgut/xsc.ts index d11df6af2fb6..c3a75853c360 100644 --- a/lib/routes/dgut/xsc.ts +++ b/lib/routes/dgut/xsc.ts @@ -40,8 +40,8 @@ async function handler(ctx: Context) { link: `${host}${urlType}/index.shtml`, description: `东莞理工学院 ${name} ${info}`, item: $('#paging > ul li') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/discuz/discuz.ts b/lib/routes/discuz/discuz.ts index 800d43513ad0..744068a200b2 100644 --- a/lib/routes/discuz/discuz.ts +++ b/lib/routes/discuz/discuz.ts @@ -64,11 +64,13 @@ async function loadContent(itemLink, charset, header) { // fix lazyload image post.find('img').each((_, img) => { const $img = $(img); - if ($img.attr('src')?.endsWith('none.gif') && $img.attr('file')) { - $img.attr('src', $img.attr('file') || $img.attr('zoomfile')); - $img.removeAttr('file'); - $img.removeAttr('zoomfile'); + if (!($img.attr('src')?.endsWith('none.gif') && $img.attr('file'))) { + return; } + + $img.attr('src', $img.attr('file') || $img.attr('zoomfile')); + $img.removeAttr('file'); + $img.removeAttr('zoomfile'); }); // 只抓取论坛1楼消息 diff --git a/lib/routes/domp4/utils.ts b/lib/routes/domp4/utils.ts index 04ec09a53e61..ed0f26f6758c 100644 --- a/lib/routes/domp4/utils.ts +++ b/lib/routes/domp4/utils.ts @@ -80,10 +80,11 @@ function decodeCipherText(p, a, c, k, e, d) { } while (c--) { const replacement = k[c]; - if (replacement) { - const token = e(c.toString()); - p = p.replaceAll(new RegExp(String.raw`\b` + token + String.raw`\b`, 'g'), () => replacement); + if (!replacement) { + continue; } + const token = e(c.toString()); + p = p.replaceAll(new RegExp(String.raw`\b` + token + String.raw`\b`, 'g'), () => replacement); } return p; } diff --git a/lib/routes/douban/other/discussion.ts b/lib/routes/douban/other/discussion.ts index 437339143bcb..764de59d7eec 100644 --- a/lib/routes/douban/other/discussion.ts +++ b/lib/routes/douban/other/discussion.ts @@ -41,8 +41,8 @@ async function handler(ctx) { const $ = load(response); // 列表 const list = $('#posts-table>tbody>tr') - .toArray() .slice(1) + .toArray() .map((item): DataItem => { const $item = $(item); const a = $item.find('a').first(); diff --git a/lib/routes/dribbble/utils.tsx b/lib/routes/dribbble/utils.tsx index 97d78b72ab77..74f24ca168c8 100644 --- a/lib/routes/dribbble/utils.tsx +++ b/lib/routes/dribbble/utils.tsx @@ -34,13 +34,15 @@ async function loadContent(link) { const $video = $(video); const dataSrc = $video.attr('data-src'); - if (!$video.attr('src') && dataSrc) { - $video.attr('src', dataSrc); - $video.removeAttr('data-src'); - $video.removeAttr('data-video-small'); - $video.removeAttr('data-video-medium'); - $video.removeAttr('data-video-large'); + if ($video.attr('src') || !dataSrc) { + return; } + + $video.attr('src', dataSrc); + $video.removeAttr('data-src'); + $video.removeAttr('data-video-small'); + $video.removeAttr('data-video-medium'); + $video.removeAttr('data-video-large'); }); object.find('img').each((_, img) => { const $img = $(img); diff --git a/lib/routes/dxy/utils.ts b/lib/routes/dxy/utils.ts index 1da76f9884f9..4c01f8733381 100644 --- a/lib/routes/dxy/utils.ts +++ b/lib/routes/dxy/utils.ts @@ -63,10 +63,12 @@ const getPost = (item) => $img.attr('src', hsrc); $img.removeAttr('data-hsrc'); } - if (osrc) { - $img.attr('src', osrc); - $img.removeAttr('data-osrc'); + if (!osrc) { + return; } + + $img.attr('src', osrc); + $img.removeAttr('data-osrc'); }); item.description = $.html(); diff --git a/lib/routes/ehentai/ehapi.ts b/lib/routes/ehentai/ehapi.ts index 02609f607364..924c85bd5399 100644 --- a/lib/routes/ehentai/ehapi.ts +++ b/lib/routes/ehentai/ehapi.ts @@ -108,36 +108,38 @@ async function parsePage(cache, data, get_bittorrent: string | boolean = false, thumbnail = await ehgot_thumb(cache, thumbnail); } const description = `thumbnail`; - if (title && link) { - const item: DataItem & { bittorrent_page_url?: string } = { - title, - description, - pubDate, - link, - }; - if (get_bittorrent) { - const el_down = el.find('div.gldown'); - const bittorrent_page_url = el_down.find('a').attr('href'); - if (bittorrent_page_url) { - const bittorrent_url = await getBittorrent(cache, bittorrent_page_url); - if (bittorrent_url) { - item.enclosure_url = bittorrent_url; - item.enclosure_type = 'application/x-bittorrent'; - item.bittorrent_page_url = bittorrent_page_url; - } + if (!title || !link) { + return; + } + + const item: DataItem & { bittorrent_page_url?: string } = { + title, + description, + pubDate, + link, + }; + if (get_bittorrent) { + const el_down = el.find('div.gldown'); + const bittorrent_page_url = el_down.find('a').attr('href'); + if (bittorrent_page_url) { + const bittorrent_url = await getBittorrent(cache, bittorrent_page_url); + if (bittorrent_url) { + item.enclosure_url = bittorrent_url; + item.enclosure_type = 'application/x-bittorrent'; + item.bittorrent_page_url = bittorrent_page_url; } } - if ('le'.includes(layout)) { - // artist tags will only show in Compact or Extended layout - // get artist names as author - item.author = $(el) - .find('div.gt[title^="artist:"]') - .toArray() - .map((tag) => $(tag).text()) - .join(' / '); - } - return item; } + if ('le'.includes(layout)) { + // artist tags will only show in Compact or Extended layout + // get artist names as author + item.author = $(el) + .find('div.gt[title^="artist:"]') + .toArray() + .map((tag) => $(tag).text()) + .join(' / '); + } + return item; } const item_Promises: Array> = []; @@ -167,15 +169,17 @@ function getBittorrent(cache, bittorrent_page_url) { for (const el_form of el_forms) { const el_a = $(el_form).find('a'); const onclick = el_a.attr('onclick'); - if (onclick) { - const match = onclick.match(/'(.*?)'/); - if (match) { - bittorrent_url = match[1]; - const match_p = bittorrent_url.match(/torrent\?p=(.*)$/); - if (match_p) { - p = match_p[1]; - } - } + if (!onclick) { + continue; + } + const match = onclick.match(/'(.*?)'/); + if (!match) { + continue; + } + bittorrent_url = match[1]; + const match_p = bittorrent_url.match(/torrent\?p=(.*)$/); + if (match_p) { + p = match_p[1]; } } return bittorrent_url; diff --git a/lib/routes/eprice/rss.tsx b/lib/routes/eprice/rss.tsx index e9da68d686a3..96e5617e737a 100644 --- a/lib/routes/eprice/rss.tsx +++ b/lib/routes/eprice/rss.tsx @@ -68,17 +68,19 @@ async function handler(ctx) { // fix lazyload image $('a').each((_, e) => { const $e = $(e); - if ($e.attr('href') && $e.attr('href')!.endsWith('.jpg')) { - $e.after( - renderToString( -
    - {$e.attr('title') -
    {$e.attr('title') ?? ''}
    -
    - ) - ); - $e.remove(); + if (!$e.attr('href')?.endsWith('.jpg')) { + return; } + + $e.after( + renderToString( +
    + {$e.attr('title') +
    {$e.attr('title') ?? ''}
    +
    + ) + ); + $e.remove(); }); $('img').each((_, e) => { const $e = $(e); diff --git a/lib/routes/espn/news.tsx b/lib/routes/espn/news.tsx index c6a3a9cc9912..aa938d90da4a 100644 --- a/lib/routes/espn/news.tsx +++ b/lib/routes/espn/news.tsx @@ -119,15 +119,17 @@ export const route: Route = { if (junkPattern.test(name)) { $(ele).remove(); } - if (mediaPattern.test(name)) { - const mediaType = name.match(mediaPattern)![1] === 'photo' ? 'images' : 'video'; - const mediaIndex = Number.parseInt(name.match(mediaPattern)![2]) - 1; - const media = article.content[mediaType][mediaIndex]; - if (media) { - $(ele).replaceWith(renderMedia(media)); - } else { - $(ele).remove(); - } + if (!mediaPattern.test(name)) { + return; + } + + const mediaType = name.match(mediaPattern)![1] === 'photo' ? 'images' : 'video'; + const mediaIndex = Number.parseInt(name.match(mediaPattern)![2]) - 1; + const media = article.content[mediaType][mediaIndex]; + if (media) { + $(ele).replaceWith(renderMedia(media)); + } else { + $(ele).remove(); } }); diff --git a/lib/routes/fx678/kx.ts b/lib/routes/fx678/kx.ts index 57c272ad560a..a2d069bd85ac 100644 --- a/lib/routes/fx678/kx.ts +++ b/lib/routes/fx678/kx.ts @@ -39,8 +39,8 @@ async function handler() { // 页面新闻消息列表 const list = $('.body_zb ul .body_zb_li .zb_word') .find('.list_font_pic > a:first-child') - .toArray() .slice(0, 30) + .toArray() .map((e) => $(e).attr('href')); const out = await Promise.all( diff --git a/lib/routes/google/news.tsx b/lib/routes/google/news.tsx index 09acbec3246c..af29d833090d 100644 --- a/lib/routes/google/news.tsx +++ b/lib/routes/google/news.tsx @@ -36,8 +36,8 @@ async function handler(ctx) { const $ = load(front_data); return [ ...$('a.brSCsc') - .toArray() .slice(3) // skip Home, For you and Following + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/gov/beijing/mhc.ts b/lib/routes/gov/beijing/mhc.ts index 8f58c779d75c..12245b6abaa5 100644 --- a/lib/routes/gov/beijing/mhc.ts +++ b/lib/routes/gov/beijing/mhc.ts @@ -53,8 +53,8 @@ async function handler(ctx: Context) { const $ = load(response); const list = $('div.weinei_left_con div.weinei_left_con_line') - .toArray() .slice(0, 10) + .toArray() .map((item): DataItem => { const $item = $(item); const a = $item.find('a[title]'); diff --git a/lib/routes/gov/ccdi/utils.ts b/lib/routes/gov/ccdi/utils.ts index 66e1d9d17d60..193882051f8c 100644 --- a/lib/routes/gov/ccdi/utils.ts +++ b/lib/routes/gov/ccdi/utils.ts @@ -58,16 +58,18 @@ export const parseNewsList = async (url, selector, ctx) => { const changeTrCookie = async () => { const cookies = await cookieJar.getCookies(rootUrl); const c = cookies.find((c) => c.key === 'HOY_TR'); - if (c) { - const value = c.value; - const tr_array = value.split(','); - const csr = tr_array[0]; - const cnv = [...tr_array[1]]; - const otr = [...tr_array[2]]; - otr[0] = csr.charAt(Number.parseInt(cnv[0], 16)); - const nc = new Cookie({ key: 'HOY_TR', value: csr + ',' + cnv.join('') + ',' + otr.join('') + ',0' }); - await cookieJar.setCookie(nc, rootUrl); + if (!c) { + return; } + + const value = c.value; + const tr_array = value.split(','); + const csr = tr_array[0]; + const cnv = [...tr_array[1]]; + const otr = [...tr_array[2]]; + otr[0] = csr.charAt(Number.parseInt(cnv[0], 16)); + const nc = new Cookie({ key: 'HOY_TR', value: csr + ',' + cnv.join('') + ',' + otr.join('') + ',0' }); + await cookieJar.setCookie(nc, rootUrl); }; export const parseArticle = async (item) => { diff --git a/lib/routes/gov/nsfc/index.ts b/lib/routes/gov/nsfc/index.ts index 482585c40e43..02d8076aa811 100644 --- a/lib/routes/gov/nsfc/index.ts +++ b/lib/routes/gov/nsfc/index.ts @@ -112,8 +112,8 @@ async function handler(ctx) { return { item: items, title: `国家自然科学基金委员会 - ${$('#ess_essBREADCRUMB_lblBreadCrumb a.break') - .toArray() .slice(1) + .toArray() .map((a) => $(a).text()) .join(' - ')}`, link: currentUrl, diff --git a/lib/routes/govopendata/xinwenlianbo.ts b/lib/routes/govopendata/xinwenlianbo.ts index b2d1a5394213..ef99de9bda76 100644 --- a/lib/routes/govopendata/xinwenlianbo.ts +++ b/lib/routes/govopendata/xinwenlianbo.ts @@ -23,8 +23,8 @@ async function handler() { const $ = load(response); const list = $('article.news-card') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); const a = $item.find('h2.news-title a'); diff --git a/lib/routes/huijin-inv/news.ts b/lib/routes/huijin-inv/news.ts index 6ba1d575cb94..6e25c65ff643 100644 --- a/lib/routes/huijin-inv/news.ts +++ b/lib/routes/huijin-inv/news.ts @@ -21,12 +21,14 @@ async function handler(): Promise { let redirectPath = DEFAULT_REDIRECT_PATH; $scripts.each((_, el) => { const redirectScript = $entry(el).text(); - if (redirectScript !== null) { - // Get the real index page by JS redirection href. The path may change. - const match = redirectScript.match(/window\.location\.href\s*=\s*["']([^"']+)["']/); - if (match) { - redirectPath = match[1]; - } + if (redirectScript === null) { + return; + } + + // Get the real index page by JS redirection href. The path may change. + const match = redirectScript.match(/window\.location\.href\s*=\s*["']([^"']+)["']/); + if (match) { + redirectPath = match[1]; } }); const redirectURL = `${BASE_URL}${redirectPath}`; diff --git a/lib/routes/iapp/news.ts b/lib/routes/iapp/news.ts index 03b9f52b441b..ec8b96527dfb 100644 --- a/lib/routes/iapp/news.ts +++ b/lib/routes/iapp/news.ts @@ -36,11 +36,13 @@ async function handler(ctx: Context) { $('script').each((_, el) => { const text = $(el).text(); const match = text.match(/\\"appID\\":\\"(\w+)\\",\\"apiKey\\":\\"(\w+)\\"/); - if (match) { - appId = match[1]; - apiKey = match[2]; - return false; + if (!match) { + return; } + + appId = match[1]; + apiKey = match[2]; + return false; }); if (!appId || !apiKey) { throw new Error('Failed to extract Algolia credentials from iapp.org'); diff --git a/lib/routes/inuki-ichiba/utils.ts b/lib/routes/inuki-ichiba/utils.ts index 8be3570da9f9..4329e8ac0ffd 100644 --- a/lib/routes/inuki-ichiba/utils.ts +++ b/lib/routes/inuki-ichiba/utils.ts @@ -50,11 +50,8 @@ export const parseJpy = (text: string | null): number | null => { if (/無償|なし|無し/.test(s)) { return 0; } - if (isUnknown(s)) { - return null; - } // Without 円 / 万 only a bare number is accepted ('3ヶ月' is not an amount). - if (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s)) { + if (isUnknown(s) || (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s))) { return null; } const oku = s.match(/^(\d+(?:\.\d+)?)億(?:(\d+(?:\.\d+)?)万)?/); diff --git a/lib/routes/ithome/zt.tsx b/lib/routes/ithome/zt.tsx index 8ca1ce8b94f4..f081e1afa8a8 100644 --- a/lib/routes/ithome/zt.tsx +++ b/lib/routes/ithome/zt.tsx @@ -63,10 +63,12 @@ export const handler = async (ctx) => { const src = $el.prop('data-original'); - if (src) { - const alt = $el.prop('alt'); - $el.replaceWith(renderToString(
    {alt ? {alt} : }
    )); + if (!src) { + return; } + + const alt = $el.prop('alt'); + $el.replaceWith(renderToString(
    {alt ? {alt} : }
    )); }); const title = $$('h1').text(); diff --git a/lib/routes/jike/topic-text.ts b/lib/routes/jike/topic-text.ts index cff991490517..5b903fc01a29 100644 --- a/lib/routes/jike/topic-text.ts +++ b/lib/routes/jike/topic-text.ts @@ -38,17 +38,19 @@ async function handler(ctx) { const data = await constructTopicEntry(ctx, topicUrl); - if (data) { - const result = data.result; - result.item = data.posts.map((item) => { - const date = dayjs(item.createdAt); - return { - title: `${data.topic.content} ${date.format('MM月DD日')}`, - description: item.content.replaceAll('\n', '
    '), - pubDate: date.toDate(), - link: `https://m.okjike.com/originalPosts/${item.id}`, - }; - }); - return result; + if (!data) { + return; } + + const result = data.result; + result.item = data.posts.map((item) => { + const date = dayjs(item.createdAt); + return { + title: `${data.topic.content} ${date.format('MM月DD日')}`, + description: item.content.replaceAll('\n', '
    '), + pubDate: date.toDate(), + link: `https://m.okjike.com/originalPosts/${item.id}`, + }; + }); + return result; } diff --git a/lib/routes/jike/topic.ts b/lib/routes/jike/topic.ts index 463f922320a5..b2d9ebd6953e 100644 --- a/lib/routes/jike/topic.ts +++ b/lib/routes/jike/topic.ts @@ -51,36 +51,38 @@ async function handler(ctx) { const data = await constructTopicEntry(ctx, topicUrl); - if (data) { - const result = data.result; - result.item = topicDataHanding(data, ctx); - if (id === '553870e8e4b0cafb0a1bef68' || id === '55963702e4b0d84d2c30ce6f') { - result.item = await Promise.all( - result.item.map(async (one) => { - const item = { ...one }; - const regResult = /https:\/\/www\.okjike\.com\/medium\/[\dA-Za-z]*/.exec(item.description); - if (regResult) { - const newsUrl = regResult[0]; - item.description = await cache.tryGet(newsUrl, async () => { - const { data } = await got(newsUrl); - const $ = load(data); - const upper = $('ul.main > li.item'); - const links = upper.find('a').map((_, ele) => $(ele).attr('href')); - const texts = upper.find('span.text').map((_, ele) => $(ele).text()); - let description = ''; - for (let i = 0; i < links.length; i++) { - description += `${i + 1}、${texts[i]}
    `; - } - description = description.replace(/
    $/, ''); - return description; - }); - } - item.description = item.description.replaceAll(urlRegex, (url) => `${url}`); - item.title = `${data.topic.content} ${dayjs(one.pubDate).format('MM月DD日')}`; - return item; - }) - ); - } - return result; + if (!data) { + return; } + + const result = data.result; + result.item = topicDataHanding(data, ctx); + if (id === '553870e8e4b0cafb0a1bef68' || id === '55963702e4b0d84d2c30ce6f') { + result.item = await Promise.all( + result.item.map(async (one) => { + const item = { ...one }; + const regResult = /https:\/\/www\.okjike\.com\/medium\/[\dA-Za-z]*/.exec(item.description); + if (regResult) { + const newsUrl = regResult[0]; + item.description = await cache.tryGet(newsUrl, async () => { + const { data } = await got(newsUrl); + const $ = load(data); + const upper = $('ul.main > li.item'); + const links = upper.find('a').map((_, ele) => $(ele).attr('href')); + const texts = upper.find('span.text').map((_, ele) => $(ele).text()); + let description = ''; + for (let i = 0; i < links.length; i++) { + description += `${i + 1}、${texts[i]}
    `; + } + description = description.replace(/
    $/, ''); + return description; + }); + } + item.description = item.description.replaceAll(urlRegex, (url) => `${url}`); + item.title = `${data.topic.content} ${dayjs(one.pubDate).format('MM月DD日')}`; + return item; + }) + ); + } + return result; } diff --git a/lib/routes/kaoyan/kaoyan.ts b/lib/routes/kaoyan/kaoyan.ts index 397d36fbe01d..335ba849bd44 100644 --- a/lib/routes/kaoyan/kaoyan.ts +++ b/lib/routes/kaoyan/kaoyan.ts @@ -20,8 +20,8 @@ async function handler() { const $ = load(response); const items = $('.areaZslist li') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/kemono/index.tsx b/lib/routes/kemono/index.tsx index 61205672543d..cf2e908e8f64 100644 --- a/lib/routes/kemono/index.tsx +++ b/lib/routes/kemono/index.tsx @@ -164,13 +164,15 @@ function generateEnclosureInfo(htmlContent: string): { enclosure_url?: string; e const extension = extractFileExtension(src); const mimeType = MIME_TYPE_MAP[extension as keyof typeof MIME_TYPE_MAP]; - if (mimeType) { - enclosureInfo = { - enclosure_url: new URL(src, KEMONO_ROOT_URL).href, - enclosure_type: mimeType, - }; - return false; + if (!mimeType) { + return; } + + enclosureInfo = { + enclosure_url: new URL(src, KEMONO_ROOT_URL).href, + enclosure_type: mimeType, + }; + return false; }); return enclosureInfo; @@ -332,10 +334,12 @@ function processPosts(posts: KemonoPost[], authorName: string, limit: number) { const fanboxRegex = /downloads\.fanbox\.cc/; $('a').each((_, el) => { const link = $(el).attr('href'); - if (link && fanboxRegex.test(link)) { - $(el).replaceWith(kemonoFileElements[replacementCount] || ''); - replacementCount++; + if (!(link && fanboxRegex.test(link))) { + return; } + + $(el).replaceWith(kemonoFileElements[replacementCount] || ''); + replacementCount++; }); description = (kemonoFileElements[0] || '') + $.html(); diff --git a/lib/routes/kenshin/index.ts b/lib/routes/kenshin/index.ts index 8e087c26f681..e8fb37154b83 100644 --- a/lib/routes/kenshin/index.ts +++ b/lib/routes/kenshin/index.ts @@ -63,8 +63,8 @@ async function handler(ctx: Context) { const $ = load(response); const list = $('.entry-title a') - .toArray() .slice(0, 10) + .toArray() .map((item): DataItem & { link: string } => { const $item = $(item); return { diff --git a/lib/routes/keylol/index.ts b/lib/routes/keylol/index.ts index d8971c8fa5a4..8dc86375cef6 100644 --- a/lib/routes/keylol/index.ts +++ b/lib/routes/keylol/index.ts @@ -192,11 +192,13 @@ function getDescription($) { // handle lazyload image descriptionEl.find('img').each((_, img) => { const $img = $(img); - if ($img.attr('src')?.endsWith('none.gif') && $img.attr('file')) { - $img.attr('src', $img.attr('file')); - $img.removeAttr('file'); - $img.removeAttr('zoomfile'); + if (!($img.attr('src')?.endsWith('none.gif') && $img.attr('file'))) { + return; } + + $img.attr('src', $img.attr('file')); + $img.removeAttr('file'); + $img.removeAttr('zoomfile'); }); return descriptionEl.length > 0 ? descriptionEl.html() : $('div.alert_info').html(); diff --git a/lib/routes/kleinanzeigen/utils/get-product-page.ts b/lib/routes/kleinanzeigen/utils/get-product-page.ts index 0f4562c06f9f..3fb123e915d1 100644 --- a/lib/routes/kleinanzeigen/utils/get-product-page.ts +++ b/lib/routes/kleinanzeigen/utils/get-product-page.ts @@ -58,8 +58,8 @@ export const getProductPage = (url: string): Promise => const category = [ ...$('.breadcrump .breadcrump-link') - .toArray() .slice(1) + .toArray() .map((x) => $(x).text().trim()), product .find('.addetailslist--detail') diff --git a/lib/routes/latexstudio/home.ts b/lib/routes/latexstudio/home.ts index 27e4d985e607..a26eb763e0d8 100644 --- a/lib/routes/latexstudio/home.ts +++ b/lib/routes/latexstudio/home.ts @@ -23,8 +23,8 @@ async function handler() { const list = $('div.article-list') .find('h3.article-title') - .toArray() .slice(0, 20) + .toArray() .map((item): DataItem & { link: string } => { const a = $(item).find('a'); return { diff --git a/lib/routes/lfsyd/utils.tsx b/lib/routes/lfsyd/utils.tsx index 1ef1f2fc577c..dfc45c074d58 100644 --- a/lib/routes/lfsyd/utils.tsx +++ b/lib/routes/lfsyd/utils.tsx @@ -78,16 +78,18 @@ const cleanHtml = (htmlString) => { const bvid = $(e) .attr('src')! .match(/bvid=(.*?)&/)![1]; - if (bvid) { - const url = `https://www.bilibili.com/video/${bvid}`; - $(e).after( - renderToString( -

    - {url} -

    - ) - ); + if (!bvid) { + return; } + + const url = `https://www.bilibili.com/video/${bvid}`; + $(e).after( + renderToString( +

    + {url} +

    + ) + ); }); // 用户头像 diff --git a/lib/routes/lifetimes/index.ts b/lib/routes/lifetimes/index.ts index 4f64b0f03134..bd80ee3d1cca 100644 --- a/lib/routes/lifetimes/index.ts +++ b/lib/routes/lifetimes/index.ts @@ -53,8 +53,8 @@ async function handler(ctx: Context) { list = $('.list-block ul li, .firstCon-r ul li') .find('a') - .toArray() .slice(0, 10) + .toArray() .map((item) => ({ link: `https:${$(item).attr('href')}`, })); diff --git a/lib/routes/locals/feed.ts b/lib/routes/locals/feed.ts index 548586f013ca..7038546baea6 100644 --- a/lib/routes/locals/feed.ts +++ b/lib/routes/locals/feed.ts @@ -407,11 +407,12 @@ async function fetchFeedData(communityId: number, community: string, session: st } const existing = items.get(item.link); - if (existing) { - existing.category = [...new Set([...(existing.category ?? []), ...(item.category ?? [])])]; - existing.description ||= item.description; - existing.itunes_item_image ||= item.itunes_item_image; + if (!existing) { + continue; } + existing.category = [...new Set([...(existing.category ?? []), ...(item.category ?? [])])]; + existing.description ||= item.description; + existing.itunes_item_image ||= item.itunes_item_image; } } diff --git a/lib/routes/lorientlejour/index.tsx b/lib/routes/lorientlejour/index.tsx index ae0bb8d972bf..7c63fb78456a 100644 --- a/lib/routes/lorientlejour/index.tsx +++ b/lib/routes/lorientlejour/index.tsx @@ -161,10 +161,12 @@ async function handler(ctx) { article.find('.inlineImage').each((_, el) => { const inlineImageSrc = $(el).attr('src'); const inlineAttachment = item.inline_attachments.find((inlineAttachment) => inlineAttachment.url === inlineImageSrc); - if (inlineAttachment && inlineAttachment.description) { - $(el).wrap('
    '); - $(el).after(`
    ${inlineAttachment.description}
    `); + if (!(inlineAttachment && inlineAttachment.description)) { + return; } + + $(el).wrap('
    '); + $(el).after(`
    ${inlineAttachment.description}
    `); }); } item.description = renderToString( diff --git a/lib/routes/mashiro/index.ts b/lib/routes/mashiro/index.ts index b2a0a24afaf7..a05957cff4e3 100644 --- a/lib/routes/mashiro/index.ts +++ b/lib/routes/mashiro/index.ts @@ -27,8 +27,8 @@ export const route: Route = { const response = await ofetch(targetLink); const $ = load(response); const links = $('.archives-group article') - .toArray() .slice(0, 10) + .toArray() .map((item): DataItem => { const $item = $(item); const a = $item.find('a').first(); diff --git a/lib/routes/metacritic/release.ts b/lib/routes/metacritic/release.ts index 4153e8736833..74dc012647d3 100644 --- a/lib/routes/metacritic/release.ts +++ b/lib/routes/metacritic/release.ts @@ -32,7 +32,7 @@ const handler = async (ctx) => { const data = response.body; const $ = load(data); - const list = $('.list_products > li').toArray().slice(0, 10); + const list = $('.list_products > li').slice(0, 10).toArray(); const result = list.map((item) => { const $ = load(item); diff --git a/lib/routes/nankai/graduate-notice.ts b/lib/routes/nankai/graduate-notice.ts index 90a3377fa03b..e66b29e16b81 100644 --- a/lib/routes/nankai/graduate-notice.ts +++ b/lib/routes/nankai/graduate-notice.ts @@ -99,14 +99,16 @@ export const route: Route = { const sudyfileAttr = ($el.attr('sudyfile-attr') || '{}').replaceAll("'", '"'); const sudyfileAttrJson = JSON.parse(sudyfileAttr); const fileName = sudyfileAttrJson.title || '未命名文件.pdf'; - if (pdfSrc) { - let pdfUrl = pdfSrc; - if (!pdfUrl.startsWith('http')) { - pdfUrl = `${baseUrl}${pdfUrl}`; - } - // 替换PDF播放器为下载链接 - $el.replaceWith(`

    📄 ${fileName}

    `); + if (!pdfSrc) { + return; } + + let pdfUrl = pdfSrc; + if (!pdfUrl.startsWith('http')) { + pdfUrl = `${baseUrl}${pdfUrl}`; + } + // 替换PDF播放器为下载链接 + $el.replaceWith(`

    📄 ${fileName}

    `); }); } diff --git a/lib/routes/nature/utils.ts b/lib/routes/nature/utils.ts index 40d9dfa50a27..85a69e691ef4 100644 --- a/lib/routes/nature/utils.ts +++ b/lib/routes/nature/utils.ts @@ -11,32 +11,40 @@ const fixFigure = (html) => { html('picture source').each((_, i) => { const $i = html(i); if ( - $i.attr('srcset') && - ($i.attr('srcset').startsWith('//media.springernature.com/lw685/') || - $i.attr('srcset').startsWith('//media.springernature.com/m312/') || - $i.attr('srcset').startsWith('//media.springernature.com/relative-r300-703_m1050/') || - $i.attr('srcset').startsWith('//media.springernature.com/w300/')) + !( + $i.attr('srcset') && + ($i.attr('srcset').startsWith('//media.springernature.com/lw685/') || + $i.attr('srcset').startsWith('//media.springernature.com/m312/') || + $i.attr('srcset').startsWith('//media.springernature.com/relative-r300-703_m1050/') || + $i.attr('srcset').startsWith('//media.springernature.com/w300/')) + ) ) { - $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/lw685/', '//media.springernature.com/full/')); - $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/m312/', '//media.springernature.com/full/')); - $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/relative-r300-703_m1050/', '//media.springernature.com/full/')); - $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/w300/', '//media.springernature.com/full/')); + return; } + + $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/lw685/', '//media.springernature.com/full/')); + $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/m312/', '//media.springernature.com/full/')); + $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/relative-r300-703_m1050/', '//media.springernature.com/full/')); + $i.attr('srcset', $i.attr('srcset').replace('//media.springernature.com/w300/', '//media.springernature.com/full/')); }); html('img').each((_, i) => { const $i = html(i); if ( - $i.attr('src') && - ($i.attr('src').startsWith('//media.springernature.com/lw685/') || - $i.attr('src').startsWith('//media.springernature.com/m312/') || - $i.attr('src').startsWith('//media.springernature.com/relative-r300-703_m1050/') || - $i.attr('src').startsWith('//media.springernature.com/w300/')) + !( + $i.attr('src') && + ($i.attr('src').startsWith('//media.springernature.com/lw685/') || + $i.attr('src').startsWith('//media.springernature.com/m312/') || + $i.attr('src').startsWith('//media.springernature.com/relative-r300-703_m1050/') || + $i.attr('src').startsWith('//media.springernature.com/w300/')) + ) ) { - $i.attr('src', $i.attr('src').replace('//media.springernature.com/lw685/', '//media.springernature.com/full/')); - $i.attr('src', $i.attr('src').replace('//media.springernature.com/m312/', '//media.springernature.com/full/')); - $i.attr('src', $i.attr('src').replace('//media.springernature.com/relative-r300-703_m1050/', '//media.springernature.com/full/')); - $i.attr('src', $i.attr('src').replace('//media.springernature.com/w300/', '//media.springernature.com/full/')); + return; } + + $i.attr('src', $i.attr('src').replace('//media.springernature.com/lw685/', '//media.springernature.com/full/')); + $i.attr('src', $i.attr('src').replace('//media.springernature.com/m312/', '//media.springernature.com/full/')); + $i.attr('src', $i.attr('src').replace('//media.springernature.com/relative-r300-703_m1050/', '//media.springernature.com/full/')); + $i.attr('src', $i.attr('src').replace('//media.springernature.com/w300/', '//media.springernature.com/full/')); }); }; diff --git a/lib/routes/naver/search.ts b/lib/routes/naver/search.ts index aeeb9fe00b07..a65a694c0542 100644 --- a/lib/routes/naver/search.ts +++ b/lib/routes/naver/search.ts @@ -170,11 +170,7 @@ function extractGenericItem(segment: string, templateId: string) { bodyText = cleanText(bodyMatch[1]); } - if (!title || !link) { - return null; - } - - if (['더보기', '관련도순', '최신순'].includes(title)) { + if (!title || !link || ['더보기', '관련도순', '최신순'].includes(title)) { return null; } @@ -252,11 +248,7 @@ function parseKoreanRelativeTime(timeText: string): Date | undefined { } const match = timeText.match(/(\d+)\s*(시간|[분일주]) 전|(\d+)분 이내|(\d+)시간 이내|방금/); - if (!match) { - return; - } - - if (match[0] === '방금') { + if (!match || match[0] === '방금') { return; } diff --git a/lib/routes/ncu/jwc.ts b/lib/routes/ncu/jwc.ts index 3efff1afed3c..43ab5d9f646e 100644 --- a/lib/routes/ncu/jwc.ts +++ b/lib/routes/ncu/jwc.ts @@ -74,10 +74,12 @@ async function handler(): Promise { attachments.each((_, el) => { const href = $detail(el).attr('href'); const text = $detail(el).text().trim(); - if (href && text) { - const absoluteHref = href.startsWith('http') ? href : new URL(href, baseUrl).href; - description += `
  • ${text}
  • `; + if (!href || !text) { + return; } + + const absoluteHref = href.startsWith('http') ? href : new URL(href, baseUrl).href; + description += `
  • ${text}
  • `; }); description += ''; } diff --git a/lib/routes/neu/yz.ts b/lib/routes/neu/yz.ts index 4f93ad332d3d..0892ae3ee66f 100644 --- a/lib/routes/neu/yz.ts +++ b/lib/routes/neu/yz.ts @@ -120,20 +120,22 @@ const cleanEntryContent = ($) => { entry.find('.wp_video_player').each((_, el) => { const div = $(el); const src = div.attr('sudy-wp-src'); - if (src) { - const videoUrl = BASE_URL + src; - const widthMatch = div.attr('style')?.match(/width:\s*(\d+)px/); - const width = widthMatch && widthMatch[1] ? widthMatch[1] : '600'; - const heightMatch = div.attr('style')?.match(/height:\s*(\d+)px/); - const height = heightMatch && heightMatch[1] ? heightMatch[1] : '400'; - const videoTag = ` - - `; - div.replaceWith(videoTag); + if (!src) { + return; } + + const videoUrl = BASE_URL + src; + const widthMatch = div.attr('style')?.match(/width:\s*(\d+)px/); + const width = widthMatch && widthMatch[1] ? widthMatch[1] : '600'; + const heightMatch = div.attr('style')?.match(/height:\s*(\d+)px/); + const height = heightMatch && heightMatch[1] ? heightMatch[1] : '400'; + const videoTag = ` + + `; + div.replaceWith(videoTag); }); return entry.html(); }; diff --git a/lib/routes/nextjs/blog.ts b/lib/routes/nextjs/blog.ts index 532033d09c3d..70479beab81e 100644 --- a/lib/routes/nextjs/blog.ts +++ b/lib/routes/nextjs/blog.ts @@ -12,8 +12,8 @@ const handler: Route['handler'] = async () => { const item = await Promise.all( $('article') - .toArray() .slice(0, 20) + .toArray() .map((item) => { const $ = load(item); const link = `https://nextjs.org${$('a[href^="/blog"]').attr('href')}`; diff --git a/lib/routes/notion/release.ts b/lib/routes/notion/release.ts index e39136725ed7..8e4e74bd8eab 100644 --- a/lib/routes/notion/release.ts +++ b/lib/routes/notion/release.ts @@ -24,8 +24,8 @@ const handler: Route['handler'] = async () => { // archive const item = await Promise.all( $('div[class^="releasePreviewsSection"] h3 a[href^="/releases/"]') - .toArray() .slice(0, 5) + .toArray() .map((item) => { const link = `https://notion.so${item.attribs.href}`; diff --git a/lib/routes/pawchive/index.tsx b/lib/routes/pawchive/index.tsx index 2b9e25d92b9e..0a23fed0ecbb 100644 --- a/lib/routes/pawchive/index.tsx +++ b/lib/routes/pawchive/index.tsx @@ -24,13 +24,15 @@ function generateEnclosureInfo(htmlContent: string): { enclosure_url?: string; e const extension = src.replace(/.*\./, '').toLowerCase(); const mimeType = MIME_TYPE_MAP[extension as keyof typeof MIME_TYPE_MAP]; - if (mimeType) { - enclosureInfo = { - enclosure_url: src, - enclosure_type: mimeType, - }; - return false; + if (!mimeType) { + return; } + + enclosureInfo = { + enclosure_url: src, + enclosure_type: mimeType, + }; + return false; }); return enclosureInfo; diff --git a/lib/routes/picnob/user.ts b/lib/routes/picnob/user.ts index 80525a864709..3a841c50accf 100644 --- a/lib/routes/picnob/user.ts +++ b/lib/routes/picnob/user.ts @@ -89,8 +89,6 @@ async function handler(ctx) { const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - let media = ''; - if (item.slideOrVideo) { const page = await context.newPage(); await page.route('**/*', (route) => { @@ -105,7 +103,7 @@ async function handler(ctx) { const html = await page.content(); const $ = load(html); - media = $('.slide-item').length + const media = $('.slide-item').length ? $('.slide-item div:first-of-type') .toArray() .map((item) => { diff --git a/lib/routes/pixiv/novel-series.ts b/lib/routes/pixiv/novel-series.ts index 35e687f15256..d36911e02d9d 100644 --- a/lib/routes/pixiv/novel-series.ts +++ b/lib/routes/pixiv/novel-series.ts @@ -55,11 +55,8 @@ async function handler(ctx): Promise { const seriesInfo: SeriesDetail = seriesInfoResponse.data; // xRestrict: 0=All ages, 1=R18, 2=R18G - if (seriesInfo.body.xRestrict > 0) { - return await getNSFWSeriesNovels(id, limit); - } // All-ages: prefer NSFW handler if authenticated - if (hasPixivAuth()) { + if (seriesInfo.body.xRestrict > 0 || hasPixivAuth()) { return await getNSFWSeriesNovels(id, limit); } return await getSFWSeriesNovels(id, limit); diff --git a/lib/routes/pixiv/pixiv-got.ts b/lib/routes/pixiv/pixiv-got.ts index a6cfda3a33c2..568a02bd1b2d 100644 --- a/lib/routes/pixiv/pixiv-got.ts +++ b/lib/routes/pixiv/pixiv-got.ts @@ -47,17 +47,19 @@ const pixivGot = got.extend({ logger.warn(`No IPv4 address resolved from ${config.pixiv.bypassCdnHostname}`); } } - if (hostname) { - const actualHost = options.url.host; - options.headers = { - ...options.headers, - host: actualHost, - }; - options.url.hostname = hostname; - options.checkServerIdentity = function (host, certificate) { - return tls.checkServerIdentity(actualHost, certificate); - }; + if (!hostname) { + return; } + + const actualHost = options.url.host; + options.headers = { + ...options.headers, + host: actualHost, + }; + options.url.hostname = hostname; + options.checkServerIdentity = function (host, certificate) { + return tls.checkServerIdentity(actualHost, certificate); + }; }, ], }, diff --git a/lib/routes/pixivision/utils.ts b/lib/routes/pixivision/utils.ts index cd0f4c277062..e467e0e541a9 100644 --- a/lib/routes/pixivision/utils.ts +++ b/lib/routes/pixivision/utils.ts @@ -58,25 +58,29 @@ export function processContent($: CheerioAPI, lang: string): string { const $link = $elem.find('blockquote > a'); const href = $link.attr('href'); - if (href) { - const match = href.match(/\/status\/(\d+)/); - if (match) { - const tweetId = match[1]; - $elem.html(` - - `); - $elem.find('blockquote').remove(); - } + if (!href) { + return; } + + const match = href.match(/\/status\/(\d+)/); + if (!match) { + return; + } + + const tweetId = match[1]; + $elem.html(` + + `); + $elem.find('blockquote').remove(); }); return ( diff --git a/lib/routes/plurk/utils.ts b/lib/routes/plurk/utils.ts index 895f7f4f3707..0a8bb20ac780 100644 --- a/lib/routes/plurk/utils.ts +++ b/lib/routes/plurk/utils.ts @@ -32,10 +32,12 @@ const getPlurk = (plurkGuid, item, author) => { $('img').each((_, e) => { const $e = $(e); $e.removeAttr('height').removeAttr('width'); - if ($e.attr('alt') && $e.attr('alt')!.startsWith('http')) { - $e.attr('src', $e.attr('alt')); - $e.removeAttr('alt'); + if (!($e.attr('alt') && $e.attr('alt')!.startsWith('http'))) { + return; } + + $e.attr('src', $e.attr('alt')); + $e.removeAttr('alt'); }); return { diff --git a/lib/routes/proletar/index.ts b/lib/routes/proletar/index.ts index e230b0565fc8..15c444e8c45c 100644 --- a/lib/routes/proletar/index.ts +++ b/lib/routes/proletar/index.ts @@ -29,8 +29,8 @@ async function handler(ctx: Context) { const $ = load(response); const list: DataItem[] = $('.archive-item-link') - .toArray() .slice(0, 15) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/qnap/release-notes.ts b/lib/routes/qnap/release-notes.ts index 979f0189ed98..51e981d916b3 100644 --- a/lib/routes/qnap/release-notes.ts +++ b/lib/routes/qnap/release-notes.ts @@ -28,8 +28,8 @@ async function handler(ctx: Context) { const $ = load(response); const list: DataItem[] = $('.release-notes-content__versions li') - .toArray() .slice(0, 20) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/qzcea/index.ts b/lib/routes/qzcea/index.ts index fd5fe1b6a01a..2c297bfe55e3 100644 --- a/lib/routes/qzcea/index.ts +++ b/lib/routes/qzcea/index.ts @@ -32,8 +32,8 @@ async function handler(ctx: Context) { $('a.news-more').remove(); const list = $('li.clearfix div.news-right a') - .toArray() .slice(0, 10) + .toArray() .map((item): DataItem => { const $item = $(item); return { diff --git a/lib/routes/react/blog.ts b/lib/routes/react/blog.ts index c24a04741a19..7d07ec856ec1 100644 --- a/lib/routes/react/blog.ts +++ b/lib/routes/react/blog.ts @@ -12,8 +12,8 @@ const handler: Route['handler'] = async () => { const item = await Promise.all( $('a[href^="/blog/"]') - .toArray() .slice(0, 20) + .toArray() .map((item) => { const link = `https://react.dev${item.attribs.href}`; diff --git a/lib/routes/rsshub/transform/html.ts b/lib/routes/rsshub/transform/html.ts index e6740324935d..1d21823c4ec9 100644 --- a/lib/routes/rsshub/transform/html.ts +++ b/lib/routes/rsshub/transform/html.ts @@ -77,8 +77,8 @@ Parsing of \`routeParams\` parameter: const rssTitle = routeParams.get('title') || $('title').text(); const item = routeParams.get('item') || 'html'; let items: DataItem[] = $(item) - .toArray() .slice(0, 20) + .toArray() .map((item) => { try { const $item = $(item); diff --git a/lib/routes/saraba1st/digest.tsx b/lib/routes/saraba1st/digest.tsx index 1efbcd6d8ca8..73191e5ec182 100644 --- a/lib/routes/saraba1st/digest.tsx +++ b/lib/routes/saraba1st/digest.tsx @@ -105,13 +105,15 @@ async function fetchContent(url) { stubS.find('img').each((_, el) => { const img = subind(el); const file = img.attr('file'); - if (file) { - img.attr('src', file); - img.removeAttr('zoomfile'); - img.removeAttr('file'); - img.removeAttr('onmouseover'); - img.removeAttr('onclick'); + if (!file) { + return; } + + img.attr('src', file); + img.removeAttr('zoomfile'); + img.removeAttr('file'); + img.removeAttr('onmouseover'); + img.removeAttr('onclick'); }); return stubS.html(); diff --git a/lib/routes/saraba1st/thread.ts b/lib/routes/saraba1st/thread.ts index a60befc90ed6..e5a87d871377 100644 --- a/lib/routes/saraba1st/thread.ts +++ b/lib/routes/saraba1st/thread.ts @@ -50,8 +50,8 @@ async function handler(ctx) { const staticUrl = new URL('/image/common/none.gif', host); staticUrl.hostname = `static.${staticUrl.hostname.split('.').slice(-2).join('.')}`; const resultItems = list - .toArray() .slice(0, 20) + .toArray() .map((post) => { const each = $(post); const floor = each.find('td.plc .pi a > em').text(); diff --git a/lib/routes/sigsac/ccs.ts b/lib/routes/sigsac/ccs.ts index 93ce105de2b3..5445ad32ec86 100644 --- a/lib/routes/sigsac/ccs.ts +++ b/lib/routes/sigsac/ccs.ts @@ -60,8 +60,8 @@ async function handler() { }; }); const paperTable = $('tbody tr') - .toArray() .slice(1) // skip table header + .toArray() .map((item) => { const $item = $(item); const title = $item.find('td').eq(0).text().trim(); diff --git a/lib/routes/simpread/changelog.ts b/lib/routes/simpread/changelog.ts index f91f4e4628f2..97eb60f02311 100644 --- a/lib/routes/simpread/changelog.ts +++ b/lib/routes/simpread/changelog.ts @@ -68,13 +68,15 @@ async function handler() { const text = span.html(); span.remove(); - if (spanClass !== undefined) { - const type = spanClass.split(' ', 2)[1]; - if (type === 'empty') { - $li.wrap('
      '); - } else { - $li.prepend(`${text}: `); - } + if (spanClass === undefined) { + return; + } + + const type = spanClass.split(' ', 2)[1]; + if (type === 'empty') { + $li.wrap('
        '); + } else { + $li.prepend(`${text}: `); } }); diff --git a/lib/routes/sis001/forum.ts b/lib/routes/sis001/forum.ts index f08f7f0daea1..1718615d533b 100644 --- a/lib/routes/sis001/forum.ts +++ b/lib/routes/sis001/forum.ts @@ -38,8 +38,8 @@ async function handler(ctx: Context) { const threads = $('form table') .last() .find('tbody') - .toArray() .slice(1) // skip first empty row + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/smzdm/utils.ts b/lib/routes/smzdm/utils.ts index 3df7a5b6b77e..d01ff54ebd55 100644 --- a/lib/routes/smzdm/utils.ts +++ b/lib/routes/smzdm/utils.ts @@ -6,11 +6,13 @@ export const parseSearchDate = (value: string, now = new Date()): Date | undefin if (/^\d{2}:\d{2}$/.test(value)) { return parseDateInTimezone(`${today}T${value}`, 8); } - if (/^\d{2}-\d{2} \d{2}:\d{2}$/.test(value)) { - // Search results omit the year; a later month/day belongs to last year. - const year = Number(today.slice(0, 4)) - Number(value.slice(0, 5) > today.slice(5)); - return parseDateInTimezone(`${year}-${value}`, 8); + if (!/^\d{2}-\d{2} \d{2}:\d{2}$/.test(value)) { + return; } + + // Search results omit the year; a later month/day belongs to last year. + const year = Number(today.slice(0, 4)) - Number(value.slice(0, 5) > today.slice(5)); + return parseDateInTimezone(`${year}-${value}`, 8); }; export const getHeaders = () => ({ diff --git a/lib/routes/snnu/ccs.ts b/lib/routes/snnu/ccs.ts index cf9cfcdf306b..612d05270f47 100644 --- a/lib/routes/snnu/ccs.ts +++ b/lib/routes/snnu/ccs.ts @@ -58,7 +58,7 @@ export const route: Route = { const configTarget = urlMap[type] || urlMap.tzgg; const response = await ofetch(configTarget.url); const $ = load(response); - const list = $('.lunwen dl dd').toArray().slice(0, 10); + const list = $('.lunwen dl dd').slice(0, 10).toArray(); const items = await Promise.all( list.map((item) => { diff --git a/lib/routes/snnu/index.ts b/lib/routes/snnu/index.ts index 5ce63aef0dbc..b887a8a8902b 100644 --- a/lib/routes/snnu/index.ts +++ b/lib/routes/snnu/index.ts @@ -31,7 +31,7 @@ export const route: Route = { const url = 'https://www.snnu.edu.cn/tzgg.htm'; const response = await ofetch(url); const $ = load(response); - const list = $('.ul-txtq3 li').toArray().slice(0, 10); + const list = $('.ul-txtq3 li').slice(0, 10).toArray(); const items = await Promise.all( list.map((item) => { diff --git a/lib/routes/snnu/yjs.ts b/lib/routes/snnu/yjs.ts index 76313269a52c..dd5f030a34cd 100644 --- a/lib/routes/snnu/yjs.ts +++ b/lib/routes/snnu/yjs.ts @@ -31,7 +31,7 @@ export const route: Route = { const url = 'https://newyjs.snnu.edu.cn/tzgg1.htm'; const response = await ofetch(url); const $ = load(response); - const list = $('.n_bt li').toArray().slice(0, 10); + const list = $('.n_bt li').slice(0, 10).toArray(); const items = await Promise.all( list.map((item) => { diff --git a/lib/routes/socialbeta/home.ts b/lib/routes/socialbeta/home.ts index 0b39d721d8c1..9b9134aeed5d 100644 --- a/lib/routes/socialbeta/home.ts +++ b/lib/routes/socialbeta/home.ts @@ -29,7 +29,7 @@ async function handler() { link: a.attr('href')!, }; }); - const uniqueList = new Map(list.map((item) => [item.link, item])).values().toArray().slice(0, 15); + const uniqueList = new Map(list.map((item) => [item.link, item])).values().take(15).toArray(); const items = await Promise.all( uniqueList.map((item) => diff --git a/lib/routes/sohu/mp.tsx b/lib/routes/sohu/mp.tsx index a0400cd6ed4c..7418ed765730 100644 --- a/lib/routes/sohu/mp.tsx +++ b/lib/routes/sohu/mp.tsx @@ -89,10 +89,12 @@ function fetchArticle(item) { }); article.find('img').each((_, e) => { const $e = $(e); - if ($e.attr('data-src') && !$e.attr('src')) { - $e.attr('src', decryptImageUrl($e.attr('data-src'))); - $e.removeAttr('data-src'); + if (!$e.attr('data-src') || $e.attr('src')) { + return; } + + $e.attr('src', decryptImageUrl($e.attr('data-src'))); + $e.removeAttr('data-src'); }); item.description = article.html(); diff --git a/lib/routes/solidot/_article.ts b/lib/routes/solidot/_article.ts index 8f93f7f670f7..9354b6d5d761 100644 --- a/lib/routes/solidot/_article.ts +++ b/lib/routes/solidot/_article.ts @@ -5,9 +5,8 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; export default async function get_article(url) { - const domain = 'https://www.solidot.org'; - if (/^\/.*$/.test(url)) { + const domain = 'https://www.solidot.org'; url = domain + url; } const response = await got({ diff --git a/lib/routes/techpowerup/utils.ts b/lib/routes/techpowerup/utils.ts index 249f44b72f57..0b64b05c0371 100644 --- a/lib/routes/techpowerup/utils.ts +++ b/lib/routes/techpowerup/utils.ts @@ -24,10 +24,12 @@ const fixImages = ($) => { const hdImage = (img) => { img.attribs.src = img.attribs.src.replace('_thm', '').replace('_small', ''); - if (img.parentNode.name === 'a' && img.parentNode.attribs['data-width'] && img.parentNode.attribs['data-height']) { - img.attribs.width = img.parentNode.attribs['data-width']; - img.attribs.height = img.parentNode.attribs['data-height']; + if (!(img.parentNode.name === 'a' && img.parentNode.attribs['data-width'] && img.parentNode.attribs['data-height'])) { + return; } + + img.attribs.width = img.parentNode.attribs['data-width']; + img.attribs.height = img.parentNode.attribs['data-height']; }; const removeFigureStyle = (f) => { delete f.attribs.style; diff --git a/lib/routes/telegram/channel-media.ts b/lib/routes/telegram/channel-media.ts index 91f028aaaa8e..469a475935ef 100644 --- a/lib/routes/telegram/channel-media.ts +++ b/lib/routes/telegram/channel-media.ts @@ -50,10 +50,7 @@ function ExpandInlineBytes(bytes: Buffer) { } function sortThumb(thumb: Api.TypePhotoSize) { - if (thumb instanceof Api.PhotoStrippedSize) { - return thumb.bytes.length; - } - if (thumb instanceof Api.PhotoCachedSize) { + if (thumb instanceof Api.PhotoStrippedSize || thumb instanceof Api.PhotoCachedSize) { return thumb.bytes.length; } if (thumb instanceof Api.PhotoSize) { diff --git a/lib/routes/telegram/channel.ts b/lib/routes/telegram/channel.ts index 555b083829fe..b86d4991730d 100644 --- a/lib/routes/telegram/channel.ts +++ b/lib/routes/telegram/channel.ts @@ -507,10 +507,12 @@ async function handler(ctx) { } tag_media += backgroundUrlSrc ? `` : ''; } - if (tag_media) { - tag_media_all += tag_media; - $node.find('.message_media_not_supported').remove(); + if (!tag_media) { + return; } + + tag_media_all += tag_media; + $node.find('.message_media_not_supported').remove(); }); return tag_media_all; }; @@ -622,16 +624,18 @@ async function handler(ctx) { attachmentTitle += _attachmentTitle; attachmentHtml += _attachmentHtml ? `
        ${_attachmentHtml}
        ` : ''; const wrapNext = $wrap.next('.tgme_widget_message_text'); - if (wrapNext.length) { - const captionHtml = wrapNext.html(); - if (captionHtml!.length) { - attachmentHtml += `

        ${captionHtml}

        `; - } - // remove them, avoid being duplicated - wrapNext.each((_, caption) => { - $(caption).remove(); - }); + if (!wrapNext.length) { + return; } + + const captionHtml = wrapNext.html(); + if (captionHtml!.length) { + attachmentHtml += `

        ${captionHtml}

        `; + } + // remove them, avoid being duplicated + wrapNext.each((_, caption) => { + $(caption).remove(); + }); }); } diff --git a/lib/routes/telegram/tglib/channel.ts b/lib/routes/telegram/tglib/channel.ts index 72b832a3cc18..e2efab6c7b1e 100644 --- a/lib/routes/telegram/tglib/channel.ts +++ b/lib/routes/telegram/tglib/channel.ts @@ -188,23 +188,24 @@ export default async function handler(ctx: Context) { } } } - if (text !== '' || ++i === messages.length - 1) { - let description = attachments.join('
        \n'); - attachments = []; // emitting these, buffer other ones - - if (text) { - description += `

        ${HTMLParser.unparse(message.message, message.entities).replaceAll('\n', '
        ')}

        `; - } + if (text === '' && ++i !== messages.length - 1) { + continue; + } + let description = attachments.join('
        \n'); + attachments = []; // emitting these, buffer other ones - const title = message.text ? message.text.slice(0, 80) + (message.text.length > 80 ? '...' : '') : new Date(message.date * 1000).toUTCString(); - item.push({ - title, - description, - pubDate: new Date(message.date * 1000).toUTCString(), - link: `https://t.me/s/${username}/${message.id}`, - author: getDisplayName(message.sender ?? entity), - }); + if (text) { + description += `

        ${HTMLParser.unparse(message.message, message.entities).replaceAll('\n', '
        ')}

        `; } + + const title = message.text ? message.text.slice(0, 80) + (message.text.length > 80 ? '...' : '') : new Date(message.date * 1000).toUTCString(); + item.push({ + title, + description, + pubDate: new Date(message.date * 1000).toUTCString(), + link: `https://t.me/s/${username}/${message.id}`, + author: getDisplayName(message.sender ?? entity), + }); } return { diff --git a/lib/routes/temposmart/utils.ts b/lib/routes/temposmart/utils.ts index 3dda604c4699..8324f29a154e 100644 --- a/lib/routes/temposmart/utils.ts +++ b/lib/routes/temposmart/utils.ts @@ -72,11 +72,8 @@ export const parseJpy = (text: string | null): number | null => { if (/無償|なし|無し/.test(s)) { return 0; } - if (isUnknown(s)) { - return null; - } // Without 円 / 万 only a bare number is accepted ('3ヶ月' is not an amount). - if (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s)) { + if (isUnknown(s) || (!/[円万億]/.test(s) && !/^\d+(?:\.\d+)?$/.test(s))) { return null; } const oku = s.match(/^(\d+(?:\.\d+)?)億(?:(\d+(?:\.\d+)?)万)?/); diff --git a/lib/routes/the/index.ts b/lib/routes/the/index.ts index 5b06a0fe332c..9a0afe84f17a 100644 --- a/lib/routes/the/index.ts +++ b/lib/routes/the/index.ts @@ -82,10 +82,12 @@ function findArticleLink($: CheerioAPI, block: Cheerio): string | undef const hasTitle = $el.find('h3').length > 0; const isMainImage = $el.hasClass('u-block') && $el.find('.graf-image').length > 0; - if (hasTitle || isMainImage) { - link = href; - return false; + if (!(hasTitle || isMainImage)) { + return; } + + link = href; + return false; }); return link; diff --git a/lib/routes/theinitium/utils.ts b/lib/routes/theinitium/utils.ts index bda270564e74..55be81238c8d 100644 --- a/lib/routes/theinitium/utils.ts +++ b/lib/routes/theinitium/utils.ts @@ -96,11 +96,8 @@ async function scrapeFullArticle(url: string, cookie: string): Promise 0) { + if (article.length === 0 || article.find('.gh-post-upgrade-cta').length > 0) { return null; } return article.html(); diff --git a/lib/routes/treasury/press-releases.ts b/lib/routes/treasury/press-releases.ts index 8743227d6860..a7aba423acae 100644 --- a/lib/routes/treasury/press-releases.ts +++ b/lib/routes/treasury/press-releases.ts @@ -31,8 +31,8 @@ async function handler(ctx: Context) { const $ = load(response); const list = $('.featured-stories__headline a') - .toArray() .slice(0, 10) + .toArray() .map((item): DataItem => { const $item = $(item); return { diff --git a/lib/routes/twitter/api/developer-api/api.ts b/lib/routes/twitter/api/developer-api/api.ts index 61d49020f102..93c1a7c78802 100644 --- a/lib/routes/twitter/api/developer-api/api.ts +++ b/lib/routes/twitter/api/developer-api/api.ts @@ -81,10 +81,7 @@ const appClients: ClientWrapper[] = []; let index = -1; const init = () => { - if (appClients.length) { - return; - } - if (!config.twitter.consumerKey || !config.twitter.consumerSecret) { + if (appClients.length || !config.twitter.consumerKey || !config.twitter.consumerSecret) { return; } diff --git a/lib/routes/twitter/api/mobile-api/api.ts b/lib/routes/twitter/api/mobile-api/api.ts index 1e569d175ffc..6e60ab4a7f4e 100644 --- a/lib/routes/twitter/api/mobile-api/api.ts +++ b/lib/routes/twitter/api/mobile-api/api.ts @@ -134,13 +134,14 @@ function gatherLegacyFromData(entries, filterNested?, userId?) { const filteredEntries: any[] = []; for (const entry of entries) { const entryId = entry.entryId; - if (entryId) { - if (entryId.startsWith('tweet-')) { - filteredEntries.push(entry); - } - if (filterNested && filterNested.some((f) => entryId.startsWith(f))) { - filteredEntries.push(...entry.content.items); - } + if (!entryId) { + continue; + } + if (entryId.startsWith('tweet-')) { + filteredEntries.push(entry); + } + if (filterNested && filterNested.some((f) => entryId.startsWith(f))) { + filteredEntries.push(...entry.content.items); } } for (const entry of filteredEntries) { @@ -153,37 +154,40 @@ function gatherLegacyFromData(entries, filterNested?, userId?) { if (tweet && tweet.tweet) { tweet = tweet.tweet; } - if (tweet) { - const retweet = tweet.legacy?.retweeted_status_result?.result; - for (const t of [tweet, retweet]) { - if (!t?.legacy) { - continue; - } - t.legacy.user = t.core?.user_result?.result?.legacy || t.core?.user_results?.result?.legacy; - t.legacy.id_str = t.rest_id; // avoid falling back to conversation_id_str elsewhere - const quote = t.quoted_status_result?.result; - if (quote) { - t.legacy.quoted_status = quote.legacy; - t.legacy.quoted_status.user = quote.core.user_result?.result?.legacy || quote.core.user_results?.result?.legacy; - } - if (t.note_tweet) { - const tmp = t.note_tweet.note_tweet_results.result; - t.legacy.entities.hashtags = tmp.entity_set.hashtags; - t.legacy.entities.symbols = tmp.entity_set.symbols; - t.legacy.entities.urls = tmp.entity_set.urls; - t.legacy.entities.user_mentions = tmp.entity_set.user_mentions; - t.legacy.full_text = tmp.text; - } + if (!tweet) { + continue; + } + const retweet = tweet.legacy?.retweeted_status_result?.result; + for (const t of [tweet, retweet]) { + if (!t?.legacy) { + continue; + } + t.legacy.user = t.core?.user_result?.result?.legacy || t.core?.user_results?.result?.legacy; + t.legacy.id_str = t.rest_id; // avoid falling back to conversation_id_str elsewhere + const quote = t.quoted_status_result?.result; + if (quote) { + t.legacy.quoted_status = quote.legacy; + t.legacy.quoted_status.user = quote.core.user_result?.result?.legacy || quote.core.user_results?.result?.legacy; } - const legacy = tweet.legacy; - if (legacy) { - if (retweet) { - legacy.retweeted_status = retweet.legacy; - } - if (userId === undefined || legacy.user_id_str === userId + '') { - tweets.push(legacy); - } + if (!t.note_tweet) { + continue; } + const tmp = t.note_tweet.note_tweet_results.result; + t.legacy.entities.hashtags = tmp.entity_set.hashtags; + t.legacy.entities.symbols = tmp.entity_set.symbols; + t.legacy.entities.urls = tmp.entity_set.urls; + t.legacy.entities.user_mentions = tmp.entity_set.user_mentions; + t.legacy.full_text = tmp.text; + } + const legacy = tweet.legacy; + if (!legacy) { + continue; + } + if (retweet) { + legacy.retweeted_status = retweet.legacy; + } + if (userId === undefined || legacy.user_id_str === userId + '') { + tweets.push(legacy); } } return tweets; diff --git a/lib/routes/twitter/api/web-api/utils.ts b/lib/routes/twitter/api/web-api/utils.ts index bb519417885d..da484cdf4cf7 100644 --- a/lib/routes/twitter/api/web-api/utils.ts +++ b/lib/routes/twitter/api/web-api/utils.ts @@ -313,13 +313,14 @@ export function gatherLegacyFromData(entries: any[], filterNested?: string[], us const filteredEntries: any[] = []; for (const entry of entries) { const entryId = entry.entryId; - if (entryId) { - if (entryId.startsWith('tweet-') || entryId.startsWith('profile-grid-0-tweet-')) { - filteredEntries.push(entry); - } - if (filterNested && filterNested.some((f) => entryId.startsWith(f))) { - filteredEntries.push(...entry.content.items); - } + if (!entryId) { + continue; + } + if (entryId.startsWith('tweet-') || entryId.startsWith('profile-grid-0-tweet-')) { + filteredEntries.push(entry); + } + if (filterNested && filterNested.some((f) => entryId.startsWith(f))) { + filteredEntries.push(...entry.content.items); } } for (const entry of filteredEntries) { @@ -354,37 +355,40 @@ export function gatherLegacyFromData(entries: any[], filterNested?: string[], us if (tweet && tweet.tweet) { tweet = tweet.tweet; } - if (tweet) { - const retweet = tweet.legacy?.retweeted_status_result?.result; - for (const t of [tweet, retweet]) { - if (!t?.legacy) { - continue; - } - hydrateLegacyUser(t.legacy, t); - t.legacy.id_str = t.rest_id; // avoid falling back to conversation_id_str elsewhere - const quote = t.quoted_status_result?.result?.tweet || t.quoted_status_result?.result; - if (quote?.legacy) { - t.legacy.quoted_status = quote.legacy; - hydrateLegacyUser(t.legacy.quoted_status, quote); - } - if (t.note_tweet) { - const tmp = t.note_tweet.note_tweet_results.result; - t.legacy.entities.hashtags = tmp.entity_set.hashtags; - t.legacy.entities.symbols = tmp.entity_set.symbols; - t.legacy.entities.urls = tmp.entity_set.urls; - t.legacy.entities.user_mentions = tmp.entity_set.user_mentions; - t.legacy.full_text = tmp.text; - } + if (!tweet) { + continue; + } + const retweet = tweet.legacy?.retweeted_status_result?.result; + for (const t of [tweet, retweet]) { + if (!t?.legacy) { + continue; } - const legacy = tweet.legacy; - if (legacy) { - if (retweet) { - legacy.retweeted_status = retweet.legacy; - } - if (userId === undefined || legacy.user_id_str === userId + '') { - tweets.push(legacy); - } + hydrateLegacyUser(t.legacy, t); + t.legacy.id_str = t.rest_id; // avoid falling back to conversation_id_str elsewhere + const quote = t.quoted_status_result?.result?.tweet || t.quoted_status_result?.result; + if (quote?.legacy) { + t.legacy.quoted_status = quote.legacy; + hydrateLegacyUser(t.legacy.quoted_status, quote); + } + if (!t.note_tweet) { + continue; } + const tmp = t.note_tweet.note_tweet_results.result; + t.legacy.entities.hashtags = tmp.entity_set.hashtags; + t.legacy.entities.symbols = tmp.entity_set.symbols; + t.legacy.entities.urls = tmp.entity_set.urls; + t.legacy.entities.user_mentions = tmp.entity_set.user_mentions; + t.legacy.full_text = tmp.text; + } + const legacy = tweet.legacy; + if (!legacy) { + continue; + } + if (retweet) { + legacy.retweeted_status = retweet.legacy; + } + if (userId === undefined || legacy.user_id_str === userId + '') { + tweets.push(legacy); } } diff --git a/lib/routes/twitter/utils.ts b/lib/routes/twitter/utils.ts index e593176d9293..a660d7ae4c00 100644 --- a/lib/routes/twitter/utils.ts +++ b/lib/routes/twitter/utils.ts @@ -14,10 +14,7 @@ const getOriginalImg = (url) => { } if ((m = url.match(/^(https?:\/\/\w+\.twimg\.com\/[^?]+)(\?.+)$/i))) { const pars = getQueryParams(url); - if (!pars.format || !pars.name) { - return url; - } - if (pars.name === 'orig') { + if (!pars.format || !pars.name || pars.name === 'orig') { return url; } return m[1] + '?format=' + pars.format + '&name=orig'; @@ -186,10 +183,11 @@ const ProcessFeed = (ctx, { data = [] }: { data?: any[] }, params: ProcessFeedPa img += content; - if (mediaNumber) { - img += `

        ${index}/${mediaCount}

        `; - index++; + if (!mediaNumber) { + continue; } + img += `

        ${index}/${mediaCount}

        `; + index++; } } diff --git a/lib/routes/weibo/utils.ts b/lib/routes/weibo/utils.ts index 67ee0bdc8ba1..438e230e42cb 100644 --- a/lib/routes/weibo/utils.ts +++ b/lib/routes/weibo/utils.ts @@ -612,13 +612,14 @@ const weiboUtils = { if (matches) { for (const match of matches) { const hrefMatch = match.match(/href="https:\/\/weibo\.cn\/sinaurl\?u=([^"]+)"/); - if (hrefMatch) { - // 获取并解码 href 中的图片 URL - const imgSrc = decodeURIComponent(hrefMatch[1]); - const imgTag = ``; - // 用替换后的 img 标签替换原来的 标签部分 - replyText = replyText.replaceAll(match, () => imgTag); + if (!hrefMatch) { + continue; } + // 获取并解码 href 中的图片 URL + const imgSrc = decodeURIComponent(hrefMatch[1]); + const imgTag = ``; + // 用替换后的 img 标签替换原来的 标签部分 + replyText = replyText.replaceAll(match, () => imgTag); } } replyText = formatDescriptionText(replyText, descriptionRenderParams); diff --git a/lib/routes/wenxuecity/bbs.ts b/lib/routes/wenxuecity/bbs.ts index c8164781acfc..7f2b7409811b 100644 --- a/lib/routes/wenxuecity/bbs.ts +++ b/lib/routes/wenxuecity/bbs.ts @@ -33,8 +33,8 @@ async function handler(ctx: Context) { const response = await ofetch(baseUrl); const $ = load(response); const list = $('div.odd,div.even') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/wenxuecity/hot.ts b/lib/routes/wenxuecity/hot.ts index d239bf114d8f..291e4281cad7 100644 --- a/lib/routes/wenxuecity/hot.ts +++ b/lib/routes/wenxuecity/hot.ts @@ -23,8 +23,8 @@ async function handler(ctx: Context) { const $ = load(response); const list = $('div.item') - .toArray() .slice(0, 10) + .toArray() .map((item) => { const $item = $(item); return { diff --git a/lib/routes/wenxuecity/news.ts b/lib/routes/wenxuecity/news.ts index e9f77f228d73..aa578f7b5e03 100644 --- a/lib/routes/wenxuecity/news.ts +++ b/lib/routes/wenxuecity/news.ts @@ -22,8 +22,8 @@ async function handler() { const $ = load(response); const list = $('div.mainwrap div.block div.wrapper ul li a') - .toArray() .slice(0, 15) + .toArray() .map((item): DataItem => { const $item = $(item); return { diff --git a/lib/routes/whu/cs.ts b/lib/routes/whu/cs.ts index 228f474c283b..c592e9ad0848 100644 --- a/lib/routes/whu/cs.ts +++ b/lib/routes/whu/cs.ts @@ -93,12 +93,14 @@ async function handler(ctx) { content.find('img').each((_, e) => { const $e = $(e); - if ($e.attr('orisrc')) { - const newUrl = new URL($e.attr('orisrc')!, 'https://cs.whu.edu.cn'); - $e.attr('src', newUrl.href); - $e.removeAttr('orisrc'); - $e.removeAttr('vurl'); + if (!$e.attr('orisrc')) { + return; } + + const newUrl = new URL($e.attr('orisrc')!, 'https://cs.whu.edu.cn'); + $e.attr('src', newUrl.href); + $e.removeAttr('orisrc'); + $e.removeAttr('vurl'); }); item.description = content.html(); diff --git a/lib/routes/wikipedia/current-events.ts b/lib/routes/wikipedia/current-events.ts index 2a88ecf1ef9c..511f4b61d718 100644 --- a/lib/routes/wikipedia/current-events.ts +++ b/lib/routes/wikipedia/current-events.ts @@ -297,16 +297,17 @@ async function fetchMultipleWikiContent(pageNames: string[]): Promise 0) { - const stream = streams[0]; - if (stream.masterUrl) { - videoUrls.push(stream.masterUrl); - } - if (stream.backupUrls?.length) { - videoUrls.push(...stream.backupUrls); - } + if (!streams?.length) { + continue; + } + const stream = streams[0]; + if (stream.masterUrl) { + videoUrls.push(stream.masterUrl); + } + if (stream.backupUrls?.length) { + videoUrls.push(...stream.backupUrls); } } @@ -264,13 +265,14 @@ async function getFullNote(link, displayLivePhoto) { const streamTypes = ['av1', 'h264', 'h265', 'h266']; for (const type of streamTypes) { const streams = image.stream?.[type]; - if (streams?.length > 0) { - if (streams[0].masterUrl) { - videoUrls.push(streams[0].masterUrl); - } - if (streams[0].backupUrls?.length) { - videoUrls.push(...streams[0].backupUrls); - } + if (!streams?.length) { + continue; + } + if (streams[0].masterUrl) { + videoUrls.push(streams[0].masterUrl); + } + if (streams[0].backupUrls?.length) { + videoUrls.push(...streams[0].backupUrls); } } diff --git a/lib/routes/xjtu/zs.ts b/lib/routes/xjtu/zs.ts index f352bda59d14..550f2cf76d2d 100644 --- a/lib/routes/xjtu/zs.ts +++ b/lib/routes/xjtu/zs.ts @@ -63,7 +63,7 @@ export const handler = async (ctx: Context): Promise => { const pubDateStr: string | undefined = $$('div.show01 i') .text() ?.match(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})/)?.[1]; - const categoryEls: Element[] = $$('div.mianbao a').toArray().slice(1); + const categoryEls: Element[] = $$('div.mianbao a').slice(1).toArray(); const categories: string[] = [...new Set(categoryEls.map((el) => $$(el).text()).filter(Boolean))]; const upDatedStr: string | undefined = pubDateStr; diff --git a/lib/routes/yahoo/author.ts b/lib/routes/yahoo/author.ts index 2810d26915d7..bae80f31cb4b 100644 --- a/lib/routes/yahoo/author.ts +++ b/lib/routes/yahoo/author.ts @@ -52,10 +52,12 @@ async function handler(ctx: Context) { content('div.story-cover img, div.content-body img').each((_, img) => { const src = content(img).attr('src'); - if (src?.startsWith('https://s.yimg.com/lo/mysterio/')) { - content(img).attr('src', decodeURIComponent(src.split('/').at(-1)!)); - content(img).siblings('source').remove(); + if (!src?.startsWith('https://s.yimg.com/lo/mysterio/')) { + return; } + + content(img).attr('src', decodeURIComponent(src.split('/').at(-1)!)); + content(img).siblings('source').remove(); }); return { diff --git a/lib/routes/yahoo/news/utils.tsx b/lib/routes/yahoo/news/utils.tsx index ac5ab2270975..5c2de378c78e 100644 --- a/lib/routes/yahoo/news/utils.tsx +++ b/lib/routes/yahoo/news/utils.tsx @@ -164,34 +164,38 @@ const parseItem = (item) => const $ele = $(ele); let dataSrc = $ele.attr('data-src'); - if (dataSrc) { - const match = dataSrc.match(/.*--\/.*--\/(.*)/); - if (match?.[1]) { - dataSrc = match?.[1]; - } - $ele.attr('src', dataSrc); - $ele.removeAttr('data-src'); + if (!dataSrc) { + return; } + + const match = dataSrc.match(/.*--\/.*--\/(.*)/); + if (match?.[1]) { + dataSrc = match?.[1]; + } + $ele.attr('src', dataSrc); + $ele.removeAttr('data-src'); }); // fix blockquote iframe body.find('.caas-iframe').each((_, ele) => { const $ele = $(ele); - if ($ele.data('type') === 'youtube') { - const blockquoteSrc = $ele.find('blockquote').attr('data-src')!; - $ele.replaceWith( - renderToString( - - ) - ); + if ($ele.data('type') !== 'youtube') { + return; } + + const blockquoteSrc = $ele.find('blockquote').attr('data-src')!; + $ele.replaceWith( + renderToString( + + ) + ); }); item.description = body diff --git a/lib/routes/yamibo/utils.ts b/lib/routes/yamibo/utils.ts index ba54ceacc961..31362018a4e9 100644 --- a/lib/routes/yamibo/utils.ts +++ b/lib/routes/yamibo/utils.ts @@ -95,16 +95,18 @@ export class ThreadFetcher { } catch { // Article failures have already been reported to their callers. } - if (this.browser) { - let browser: BrowserPage; - try { - browser = await this.browser; - } catch { - // openBrowser cleans up failed initialization before rejecting. - return; - } - await browser.destroy(); + if (!this.browser) { + return; + } + + let browser: BrowserPage; + try { + browser = await this.browser; + } catch { + // openBrowser cleans up failed initialization before rejecting. + return; } + await browser.destroy(); } } diff --git a/lib/routes/youtube/utils.tsx b/lib/routes/youtube/utils.tsx index 6b026a553293..1547d209f28d 100644 --- a/lib/routes/youtube/utils.tsx +++ b/lib/routes/youtube/utils.tsx @@ -138,16 +138,9 @@ export const getVideoUrl = (id: string) => `https://www.youtube-nocookie.com/emb // Get the appropriate playlist ID with or without shorts export const getPlaylistWithShortsFilter = (id: string, filterShorts = true): string => { - // If filtering shorts is enabled - if (filterShorts) { - if (id.startsWith('UC')) { - // For channel IDs (UC...), convert to playlist format without shorts (UULF...) - return 'UULF' + id.slice(2); - } - if (id.startsWith('UU')) { - // For playlist IDs (UU...), convert to playlist format without shorts (UULF...) - return 'UULF' + id.slice(2); - } + // If filtering shorts is enabled, convert channel IDs (UC...) and playlist IDs (UU...) to playlist format without shorts (UULF...) + if (filterShorts && (id.startsWith('UC') || id.startsWith('UU'))) { + return 'UULF' + id.slice(2); } // If filterShorts is false or the ID format doesn't match known patterns, return original ID return id; diff --git a/lib/routes/yuque/book.ts b/lib/routes/yuque/book.ts index 49db19a621b2..8708460dd62b 100644 --- a/lib/routes/yuque/book.ts +++ b/lib/routes/yuque/book.ts @@ -99,15 +99,16 @@ async function handler(ctx) { for await (const v of $('video').toArray()) { const $v = $(v); const src = $v.attr('src'); - if (src!.startsWith('inputs')) { - const { data } = await got(`${baseUrl}/api/video`, { - searchParams: { - video_id: src, - }, - }); - const { info } = data.data; - $v.replaceWith(``); + if (!src!.startsWith('inputs')) { + continue; } + const { data } = await got(`${baseUrl}/api/video`, { + searchParams: { + video_id: src, + }, + }); + const { info } = data.data; + $v.replaceWith(``); } item.description = $.html(); diff --git a/lib/routes/zhihu/execlib/x-zse-96-v3.ts b/lib/routes/zhihu/execlib/x-zse-96-v3.ts index fe2c48e5597a..359845d8378b 100644 --- a/lib/routes/zhihu/execlib/x-zse-96-v3.ts +++ b/lib/routes/zhihu/execlib/x-zse-96-v3.ts @@ -115,10 +115,11 @@ export const encrypt = (md5Str) => { current |= e; - if (i_mod_3 === 2) { - resultStr += encode(current); - current = 0; + if (i_mod_3 !== 2) { + continue; } + resultStr += encode(current); + current = 0; } return resultStr; }; diff --git a/lib/routes/zhihu/utils.ts b/lib/routes/zhihu/utils.ts index 7110741c254f..7088bdcdd462 100644 --- a/lib/routes/zhihu/utils.ts +++ b/lib/routes/zhihu/utils.ts @@ -25,14 +25,16 @@ export const processImage = (content: string) => { $('a').each((_, elem) => { const href = $(elem).attr('href'); - if (href?.startsWith('http://link.zhihu.com/?target=') || href?.startsWith('https://link.zhihu.com/?target=')) { - const url = new URL(href); - const target = url.searchParams.get('target') || ''; - try { - $(elem).attr('href', decodeURIComponent(target)); - } catch { - // sometimes the target is not a valid url - } + if (!href?.startsWith('http://link.zhihu.com/?target=') && !href?.startsWith('https://link.zhihu.com/?target=')) { + return; + } + + const url = new URL(href); + const target = url.searchParams.get('target') || ''; + try { + $(elem).attr('href', decodeURIComponent(target)); + } catch { + // sometimes the target is not a valid url } }); diff --git a/lib/routes/zhishifenzi/innovation.ts b/lib/routes/zhishifenzi/innovation.ts index 698dc4d37fb9..8b514fabf0e2 100644 --- a/lib/routes/zhishifenzi/innovation.ts +++ b/lib/routes/zhishifenzi/innovation.ts @@ -39,8 +39,8 @@ async function handler(ctx: Context) { const title = navTitle === '' ? '全部創新' : `創新「${navTitle}」`; const list: DataItem[] = $('div.inner_news_list > ul > li.clearfix') - .toArray() .slice(0, 4) + .toArray() .map((elem) => { const $elem = $(elem); return { diff --git a/lib/routes/zzu/dzb.ts b/lib/routes/zzu/dzb.ts index 34ea91a0b456..5fd251bafa67 100644 --- a/lib/routes/zzu/dzb.ts +++ b/lib/routes/zzu/dzb.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.new_list3 dd') - .toArray() .slice(0, 20) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); diff --git a/lib/routes/zzu/gs.ts b/lib/routes/zzu/gs.ts index 6f4ecaa495cf..10d6b8bf1a31 100644 --- a/lib/routes/zzu/gs.ts +++ b/lib/routes/zzu/gs.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.r_list ul li') - .toArray() .slice(0, 20) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a').first(); diff --git a/lib/routes/zzu/jwc.ts b/lib/routes/zzu/jwc.ts index 99e74a9ab128..2d696e7d0af5 100644 --- a/lib/routes/zzu/jwc.ts +++ b/lib/routes/zzu/jwc.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('ul.list li') - .toArray() .slice(0, 15) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a.tit').first(); diff --git a/lib/routes/zzu/kjc.ts b/lib/routes/zzu/kjc.ts index 4f51a193b2b3..f38c3b6a773a 100644 --- a/lib/routes/zzu/kjc.ts +++ b/lib/routes/zzu/kjc.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.list_guild') - .toArray() .slice(0, 14) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); diff --git a/lib/routes/zzu/math.ts b/lib/routes/zzu/math.ts index c5eb55e0ded8..242ce2a57eef 100644 --- a/lib/routes/zzu/math.ts +++ b/lib/routes/zzu/math.ts @@ -44,8 +44,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.text-list ul li') - .toArray() .slice(0, 16) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); diff --git a/lib/routes/zzu/news.ts b/lib/routes/zzu/news.ts index 1d6ba0284bf4..84ba8fe310aa 100644 --- a/lib/routes/zzu/news.ts +++ b/lib/routes/zzu/news.ts @@ -48,8 +48,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.new-item') - .toArray() .slice(0, 15) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('h3 a'); diff --git a/lib/routes/zzu/rsc.ts b/lib/routes/zzu/rsc.ts index c55804f0953d..611bb735ad2e 100644 --- a/lib/routes/zzu/rsc.ts +++ b/lib/routes/zzu/rsc.ts @@ -46,8 +46,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.list_guild') - .toArray() .slice(0, 14) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); diff --git a/lib/routes/zzu/ss.ts b/lib/routes/zzu/ss.ts index 6eaa7fd500b4..fa5b62a0529a 100644 --- a/lib/routes/zzu/ss.ts +++ b/lib/routes/zzu/ss.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.list_notice > a') - .toArray() .slice(0, 10) + .toArray() .map((element) => { const $element = $(element); const link = new URL($element.attr('href')!, typeDict[type][1]).href; diff --git a/lib/routes/zzu/student.ts b/lib/routes/zzu/student.ts index f79dbf283c09..27df99625659 100644 --- a/lib/routes/zzu/student.ts +++ b/lib/routes/zzu/student.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.part-list ul li') - .toArray() .slice(0, 10) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a').first(); diff --git a/lib/routes/zzu/sxy.ts b/lib/routes/zzu/sxy.ts index e8c5886a9f43..df5f65d74ceb 100644 --- a/lib/routes/zzu/sxy.ts +++ b/lib/routes/zzu/sxy.ts @@ -55,8 +55,8 @@ async function handler(ctx) { function parseXyxwList($, typeDict, type) { return $('section.n_titu ul li') - .toArray() .slice(0, 6) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); @@ -79,8 +79,8 @@ function parseXyxwList($, typeDict, type) { function parseOtherList($, typeDict, type) { return $('.n_notice ul.ul li') - .toArray() .slice(0, 16) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a'); diff --git a/lib/routes/zzu/tzhb.ts b/lib/routes/zzu/tzhb.ts index 66a923720139..1668877759dc 100644 --- a/lib/routes/zzu/tzhb.ts +++ b/lib/routes/zzu/tzhb.ts @@ -42,8 +42,8 @@ async function handler(ctx) { // 解析页面内容并提取文章信息 const list = $('.main_conR li') - .toArray() .slice(0, 15) + .toArray() .map((element) => { const $element = $(element); const $link = $element.find('a').first(); diff --git a/lib/utils/cache/http.test.ts b/lib/utils/cache/http.test.ts index d96d01df51bf..b1bfd37dac67 100644 --- a/lib/utils/cache/http.test.ts +++ b/lib/utils/cache/http.test.ts @@ -48,10 +48,7 @@ describe('http cache module', () => { url: input.toString(), }); - if (init?.method === 'PUT') { - return new Response(null, { status: 204 }); - } - if (init?.method === 'HEAD') { + if (init?.method === 'PUT' || init?.method === 'HEAD') { return new Response(null, { status: 204 }); } diff --git a/lib/utils/cache/kv.ts b/lib/utils/cache/kv.ts index 3e7768517361..8b0bd801e3c0 100644 --- a/lib/utils/cache/kv.ts +++ b/lib/utils/cache/kv.ts @@ -57,16 +57,18 @@ export default { return; } const stored = stringify(value); - if (key) { - const promises: Array> = [kvNamespace.put(key, stored, { expirationTtl: maxAge })]; + if (!key) { + return; + } - if (maxAge !== config.cache.contentExpire) { - // Store the cache ttl if it is not the default value - promises.push(kvNamespace.put(getCacheTtlKey(key), String(maxAge), { expirationTtl: maxAge })); - } + const promises: Array> = [kvNamespace.put(key, stored, { expirationTtl: maxAge })]; - await Promise.all(promises); + if (maxAge !== config.cache.contentExpire) { + // Store the cache ttl if it is not the default value + promises.push(kvNamespace.put(getCacheTtlKey(key), String(maxAge), { expirationTtl: maxAge })); } + + await Promise.all(promises); }, clients: {}, status, diff --git a/lib/utils/cache/redis.ts b/lib/utils/cache/redis.ts index 9ae79c6077e0..2aec26661cb0 100644 --- a/lib/utils/cache/redis.ts +++ b/lib/utils/cache/redis.ts @@ -64,13 +64,15 @@ export default { return; } const stored = stringify(value); - if (key) { - if (maxAge !== config.cache.contentExpire) { - // intentionally store the cache ttl if it is not the default value - clients.redisClient.set(getCacheTtlKey(key), maxAge, 'EX', maxAge); - } - return clients.redisClient.set(key, stored, 'EX', maxAge); // setMode: https://redis.io/commands/set + if (!key) { + return; + } + + if (maxAge !== config.cache.contentExpire) { + // intentionally store the cache ttl if it is not the default value + clients.redisClient.set(getCacheTtlKey(key), maxAge, 'EX', maxAge); } + return clients.redisClient.set(key, stored, 'EX', maxAge); // setMode: https://redis.io/commands/set }, clients, status, diff --git a/lib/utils/evaluate-script.ts b/lib/utils/evaluate-script.ts index fa950efcc9ed..0b187503ba2f 100644 --- a/lib/utils/evaluate-script.ts +++ b/lib/utils/evaluate-script.ts @@ -353,16 +353,18 @@ const readScriptData = async (source: string, target: string[], argumentIndex if (value instanceof ScriptDataError) { throw value; } - if (isObject(value)) { - if (ancestors.has(value)) { - throw new ScriptDataError('Cyclic script data is not supported'); - } - ancestors.add(value); - for (const child of Object.values(value)) { - validate(child, ancestors, depth + 1); - } - ancestors.delete(value); + if (!isObject(value)) { + return; + } + + if (ancestors.has(value)) { + throw new ScriptDataError('Cyclic script data is not supported'); + } + ancestors.add(value); + for (const child of Object.values(value)) { + validate(child, ancestors, depth + 1); } + ancestors.delete(value); }; if (source.length > maxScriptLength) { diff --git a/lib/utils/header-generator.ts b/lib/utils/header-generator.ts index 9a974ede6940..b6e30c15f776 100644 --- a/lib/utils/header-generator.ts +++ b/lib/utils/header-generator.ts @@ -14,13 +14,17 @@ const isValidHeader = (headers: Record, browser: string): boolea browser = browser.toLowerCase(); const userAgent = headers['user-agent']; - if (browser === 'chrome') { - if (userAgent.includes('Chrome-Lighthouse') || userAgent.includes('Gener8') || userAgent.includes('HeadlessChrome') || userAgent.includes('SMTBot') || userAgent.includes('Electron') || userAgent.includes('Code')) { - return false; - } - if (!(headers['sec-ch-ua'] && headers['sec-ch-ua-mobile'] && headers['sec-ch-ua-platform'])) { - return false; - } + if ( + browser === 'chrome' && + (userAgent.includes('Chrome-Lighthouse') || + userAgent.includes('Gener8') || + userAgent.includes('HeadlessChrome') || + userAgent.includes('SMTBot') || + userAgent.includes('Electron') || + userAgent.includes('Code') || + !(headers['sec-ch-ua'] && headers['sec-ch-ua-mobile'] && headers['sec-ch-ua-platform'])) + ) { + return false; } return !(browser === 'safari' && userAgent.includes('Applebot')); diff --git a/lib/utils/parse-date.ts b/lib/utils/parse-date.ts index c77f93192801..8d7981a38121 100644 --- a/lib/utils/parse-date.ts +++ b/lib/utils/parse-date.ts @@ -181,11 +181,12 @@ const parseDuration = (str: string): plugin.Duration => { for (const { unit, regExp } of UNIT_PATTERNS) { const match = regExp.exec(cleanStr); - if (match) { - const val = Number(match[1]); - if (!Number.isNaN(val)) { - totalDuration = totalDuration.add(val, unit); - } + if (!match) { + continue; + } + const val = Number(match[1]); + if (!Number.isNaN(val)) { + totalDuration = totalDuration.add(val, unit); } } return totalDuration; diff --git a/lib/utils/proxy/multi-proxy.ts b/lib/utils/proxy/multi-proxy.ts index 35042abe7705..48eb7f737a95 100644 --- a/lib/utils/proxy/multi-proxy.ts +++ b/lib/utils/proxy/multi-proxy.ts @@ -93,35 +93,41 @@ const createMultiProxy = (proxyUris: string[], proxyObj: Config['proxy']): Multi const markProxyFailed = (proxyUri: string) => { const proxy = proxies.find((p) => p.uri === proxyUri); - if (proxy) { - proxy.failureCount++; - proxy.lastFailureTime = Date.now(); - if (proxy.failureCount >= maxFailures) { - proxy.isActive = false; - logger.warn(`Proxy ${proxyUri} marked as inactive after ${maxFailures} failures`); - } else { - logger.warn(`Proxy ${proxyUri} failed (${proxy.failureCount}/${maxFailures})`); - } + if (!proxy) { + return; + } - const activeProxies = proxies.filter((p) => p.isActive); - if (activeProxies.length > 0) { - currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length; - const nextProxy = getNextProxy(); - if (nextProxy) { - logger.info(`Switching to proxy: ${nextProxy.uri}`); - } - } + proxy.failureCount++; + proxy.lastFailureTime = Date.now(); + if (proxy.failureCount >= maxFailures) { + proxy.isActive = false; + logger.warn(`Proxy ${proxyUri} marked as inactive after ${maxFailures} failures`); + } else { + logger.warn(`Proxy ${proxyUri} failed (${proxy.failureCount}/${maxFailures})`); + } + + const activeProxies = proxies.filter((p) => p.isActive); + if (activeProxies.length === 0) { + return; + } + + currentProxyIndex = (currentProxyIndex + 1) % activeProxies.length; + const nextProxy = getNextProxy(); + if (nextProxy) { + logger.info(`Switching to proxy: ${nextProxy.uri}`); } }; const resetProxy = (proxyUri: string) => { const proxy = proxies.find((p) => p.uri === proxyUri); - if (proxy) { - proxy.isActive = true; - proxy.failureCount = 0; - delete proxy.lastFailureTime; - logger.info(`Proxy ${proxyUri} manually reset`); + if (!proxy) { + return; } + + proxy.isActive = true; + proxy.failureCount = 0; + delete proxy.lastFailureTime; + logger.info(`Proxy ${proxyUri} manually reset`); }; const currentProxy = getNextProxy(); diff --git a/lib/utils/wechat-mp.ts b/lib/utils/wechat-mp.ts index e26ad11ef7df..7f0fe04feeb7 100644 --- a/lib/utils/wechat-mp.ts +++ b/lib/utils/wechat-mp.ts @@ -356,20 +356,24 @@ const fixArticleContent = (html?: string | Cheerio, skipImg = false): s $('img[data-src]').each((_, img) => { const $img = $(img); const realSrc = $img.attr('data-src'); - if (realSrc) { - $img.attr('src', realSrc); - $img.removeAttr('data-src'); + if (!realSrc) { + return; } + + $img.attr('src', realSrc); + $img.removeAttr('data-src'); }); } // fix audio: https://mp.weixin.qq.com/s/FnjcMXZ1xdS-d6n-pUUyyw $('mpvoice[voice_encode_fileid]').each((_, voice) => { const $voice = $(voice); const voiceId = $voice.attr('voice_encode_fileid'); - if (voiceId) { - const title = $voice.attr('name') || 'Audio'; - $voice.replaceWith(genAudioTag(genAudioSrc(voiceId), title)); + if (!voiceId) { + return; } + + const title = $voice.attr('name') || 'Audio'; + $voice.replaceWith(genAudioTag(genAudioSrc(voiceId), title)); }); // fix iframe: https://mp.weixin.qq.com/s/FnjcMXZ1xdS-d6n-pUUyyw $('iframe.video_iframe[data-src]').each((_, iframe) => { @@ -380,18 +384,22 @@ const fixArticleContent = (html?: string | Cheerio, skipImg = false): s } const srcUrlObj = new URL(dataSrc); const vid = srcUrlObj.searchParams.get('vid'); - if (srcUrlObj.host === 'v.qq.com' && vid !== null) { - const newSrc = genVideoSrc(vid); - $iframe.attr('src', newSrc); - $iframe.removeAttr('data-src'); - const width = $iframe.attr('data-w'); - const ratio = $iframe.attr('data-ratio'); - if (width && ratio) { - const width_ = Math.min(Number.parseInt(width), 677); - $iframe.attr('width', width_.toString()); - $iframe.attr('height', (width_ / Number(ratio)).toString()); - } - } // else {} FIXME: https://mp.weixin.qq.com/s?__biz=Mzg5Mjk3MzE4OQ==&mid=2247549515&idx=2&sn=a608fca597f0589c1aebd6d0b82ff6e9 + if (srcUrlObj.host !== 'v.qq.com' || vid === null) { + // FIXME: https://mp.weixin.qq.com/s?__biz=Mzg5Mjk3MzE4OQ==&mid=2247549515&idx=2&sn=a608fca597f0589c1aebd6d0b82ff6e9 + return; + } + const newSrc = genVideoSrc(vid); + $iframe.attr('src', newSrc); + $iframe.removeAttr('data-src'); + const width = $iframe.attr('data-w'); + const ratio = $iframe.attr('data-ratio'); + if (!width || !ratio) { + return; + } + + const width_ = Math.min(Number.parseInt(width), 677); + $iframe.attr('width', width_.toString()); + $iframe.attr('height', (width_ / Number(ratio)).toString()); }); // fix section $('section').each((_, section) => { diff --git a/package.json b/package.json index b56a6a0271de..17644074a050 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "eslint-plugin-n": "18.3.0", "eslint-plugin-regexp": "3.3.0", "eslint-plugin-simple-import-sort": "14.0.0", - "eslint-plugin-unicorn": "74.0.0", + "eslint-plugin-unicorn": "75.0.0", "eslint-plugin-yml": "3.8.1", "fast-string-width": "3.0.2", "fs-extra": "11.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27b9831ac35c..a950f57f6ee0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,8 +390,8 @@ importers: specifier: 14.0.0 version: 14.0.0(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-unicorn: - specifier: 74.0.0 - version: 74.0.0(eslint@10.10.0(jiti@2.7.0)) + specifier: 75.0.0 + version: 75.0.0(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-yml: specifier: 3.8.1 version: 3.8.1(eslint@10.10.0(jiti@2.7.0)) @@ -466,10 +466,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) wrangler: specifier: 4.131.2 version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -1198,8 +1198,8 @@ packages: resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/css-tree@4.0.5': - resolution: {integrity: sha512-iPmijIAq4hlIJB86PYmY/fcZORHtjphSqICDbwuw32A/JmkhZQ/K/6TjHE03zqf3n5yABpVcbRAMG8Mi9ojy8g==} + '@eslint/css-tree@4.1.1': + resolution: {integrity: sha512-A+s89eP+yDAPkKjnIOfjlkGifg5J7GPn01nWx14UjBVWktfkxOU4975F9ZZ13xNxkGiblNcBaAqgKhnQrW6SWA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/js@10.0.1': @@ -3349,6 +3349,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + baseline-browser-mapping@2.11.25: + resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==} + engines: {node: '>=6.0.0'} + hasBin: true + basic-ftp@5.3.1: resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} engines: {node: '>=10.0.0'} @@ -3396,6 +3401,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -3406,8 +3416,8 @@ packages: resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} - builtin-modules@5.3.0: - resolution: {integrity: sha512-hMQUl2bUFG339QygPM97E+mc8OY1IAchORZxm4a/frcYwKzozMzRVDBwHW0NjOqGElLm2O37AVQE8ikxlZHrMQ==} + builtin-modules@5.4.0: + resolution: {integrity: sha512-JCWSCdun+4Ovd9BhM/Vtlmwb7oD6Wl4YiPRXXwhAuwlPhW1un/MKgXn7GZoFm53ginnoNzus69V8JWx0K393jg==} engines: {node: '>=18.20'} byte-counter@0.1.0: @@ -3556,10 +3566,6 @@ packages: resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} engines: {node: '>=18'} - convert-hrtime@5.0.0: - resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} - engines: {node: '>=12'} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3789,6 +3795,9 @@ packages: electron-to-chromium@1.5.416: resolution: {integrity: sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==} + electron-to-chromium@1.5.431: + resolution: {integrity: sha512-AAVihz2YwJeOdAynX8MUtqpvjY0gaiARp/7+r4kwqgzqXMrfG1qDN7vwT80uxAoOQRPcZH8t73EI+CA/1OJN8A==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3937,8 +3946,8 @@ packages: peerDependencies: eslint: '>=5.0.0' - eslint-plugin-unicorn@74.0.0: - resolution: {integrity: sha512-AGnsGi2SxHg1HEAXxn9nSnZfyjvTWkxm8E8hpd/9tD6dLjBUdcD7+D6ZN64HmmCXTSXlrwVyUqe20Uyb2CaurA==} + eslint-plugin-unicorn@75.0.0: + resolution: {integrity: sha512-72I7eSn+/z5CpgValFBl9mp2JE5UPD+nUUyB/VIGUMF1rCP1i37mA05zfIAVIU312A+qCZ38T8tSZr3oaMv4Mw==} engines: {node: '>=22'} peerDependencies: eslint: '>=10.4' @@ -4135,10 +4144,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-timeout@1.0.2: - resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} - engines: {node: '>=18'} - gaxios@7.3.1: resolution: {integrity: sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==} engines: {node: '>=18'} @@ -4445,10 +4450,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-identifier@1.1.0: - resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} - engines: {node: '>=18'} - is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -4757,10 +4758,6 @@ packages: magicast@0.5.4: resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} - make-asynchronous@1.1.0: - resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} - engines: {node: '>=18'} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -4819,8 +4816,8 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - mdn-data@2.29.0: - resolution: {integrity: sha512-pVxQFCcaYUEAH853+v7yoI/qzhxXSq1bTb9obMYGYAN1c3Hen+XDCEvr296XhstrwlSTNgOR7mCSD4JPjbJe5A==} + mdn-data@2.35.0: + resolution: {integrity: sha512-k+1+dEIm2z/BEfdvYzT/fIyAGeDBo/1AJNJI4ilFXJRYoxP/3Ds+GP8shAOIq8jYXY9N5nGB3Ag104NsagmLAA==} mdurl@2.1.0: resolution: {integrity: sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==} @@ -5057,6 +5054,10 @@ packages: resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} engines: {node: '>=18'} + node-releases@2.0.56: + resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==} + engines: {node: '>=18'} + nopt@10.0.1: resolution: {integrity: sha512-df3sBr/6ax9hSGuC3CspvLlbnX8cP5L5nZwXF8cGN8l0zSWR6BvzmQ6jPUKjvo6+/xdpkNvEcucBNUdBeeV13g==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -5182,10 +5183,6 @@ packages: resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==} engines: {node: '>=14.16'} - p-event@6.0.1: - resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} - engines: {node: '>=16.17'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -5198,10 +5195,6 @@ packages: resolution: {integrity: sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw==} engines: {node: '>=18'} - p-timeout@6.1.4: - resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} - engines: {node: '>=14.16'} - pac-proxy-agent@9.1.0: resolution: {integrity: sha512-1aU+1mpj3DrQPfo3gh+3Gap3G5x+axnMx1P/y0ZF2ch7kb2meyOCAH8K2k9d27ROsTE7TnAerzxqF9aon2jqnA==} engines: {node: '>= 20'} @@ -5721,10 +5714,6 @@ packages: striptags@3.2.0: resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} - super-regex@1.1.0: - resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} - engines: {node: '>=18'} - supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -5763,10 +5752,6 @@ packages: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} - time-span@5.1.0: - resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} - engines: {node: '>=12'} - timers-ext@0.1.8: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} @@ -6051,6 +6036,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -6215,9 +6206,6 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} - web-worker@1.5.0: - resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -6362,6 +6350,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -6566,7 +6559,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -6921,9 +6914,9 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/css-tree@4.0.5': + '@eslint/css-tree@4.1.1': dependencies: - mdn-data: 2.29.0 + mdn-data: 2.35.0 source-map-js: 1.2.1 '@eslint/js@10.0.1(eslint@10.10.0(jiti@2.7.0))': @@ -8373,7 +8366,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) '@vitest/expect@4.1.11': dependencies: @@ -8384,14 +8377,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) '@vitest/pretty-format@4.1.11': dependencies: @@ -8576,6 +8569,8 @@ snapshots: baseline-browser-mapping@2.11.20: {} + baseline-browser-mapping@2.11.25: {} + basic-ftp@5.3.1: {} bcrypt-pbkdf@1.0.2: @@ -8619,6 +8614,14 @@ snapshots: node-releases: 2.0.54 update-browserslist-db: 1.3.2(browserslist@4.28.8) + browserslist@4.29.0: + dependencies: + baseline-browser-mapping: 2.11.25 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.431 + node-releases: 2.0.56 + update-browserslist-db: 1.3.3(browserslist@4.29.0) + buffer-equal-constant-time@1.0.1: {} bufferutil@1.1.0: @@ -8631,7 +8634,7 @@ snapshots: dependencies: node-gyp-build: 4.8.4 - builtin-modules@5.3.0: {} + builtin-modules@5.4.0: {} byte-counter@0.1.0: {} @@ -8773,15 +8776,13 @@ snapshots: content-type@2.0.0: {} - convert-hrtime@5.0.0: {} - convert-source-map@2.0.0: {} cookie@1.1.1: {} core-js-compat@3.50.0: dependencies: - browserslist: 4.28.8 + browserslist: 4.29.0 core-js@2.6.12: {} @@ -8973,6 +8974,8 @@ snapshots: electron-to-chromium@1.5.416: {} + electron-to-chromium@1.5.431: {} + emoji-regex@8.0.0: {} empathic@2.0.1: {} @@ -9197,11 +9200,11 @@ snapshots: dependencies: eslint: 10.10.0(jiti@2.7.0) - eslint-plugin-unicorn@74.0.0(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-unicorn@75.0.0(eslint@10.10.0(jiti@2.7.0)): dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) - '@eslint/css-tree': 4.0.5 - browserslist: 4.28.8 + '@eslint/css-tree': 4.1.1 + browserslist: 4.29.0 change-case: 5.4.4 ci-info: 4.4.0 core-js-compat: 3.50.0 @@ -9210,16 +9213,16 @@ snapshots: eslint: 10.10.0(jiti@2.7.0) find-up-simple: 1.0.1 globals: 17.12.0 + identifier-regex: 1.1.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 - is-identifier: 1.1.0 pluralize: 8.0.0 quote-js-string: 0.1.0 regjsparser: 0.13.2 reserved-identifiers: 1.2.0 semver: 7.8.5 strip-indent: 4.1.1 - yaml: 2.9.0 + yaml: 2.9.1 eslint-plugin-yml@3.8.1(eslint@10.10.0(jiti@2.7.0)): dependencies: @@ -9449,8 +9452,6 @@ snapshots: fsevents@2.3.3: optional: true - function-timeout@1.0.2: {} - gaxios@7.3.1: dependencies: extend: 3.0.2 @@ -9796,7 +9797,7 @@ snapshots: is-builtin-module@5.0.0: dependencies: - builtin-modules: 5.3.0 + builtin-modules: 5.4.0 is-docker@2.2.1: {} @@ -9810,11 +9811,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-identifier@1.1.0: - dependencies: - identifier-regex: 1.1.0 - super-regex: 1.1.0 - is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -10103,12 +10099,6 @@ snapshots: '@babel/types': 7.29.8 source-map-js: 1.2.1 - make-asynchronous@1.1.0: - dependencies: - p-event: 6.0.1 - type-fest: 4.41.0 - web-worker: 1.5.0 - make-dir@4.0.0: dependencies: semver: 7.8.5 @@ -10236,7 +10226,7 @@ snapshots: mdn-data@2.27.1: {} - mdn-data@2.29.0: {} + mdn-data@2.35.0: {} mdurl@2.1.0: {} @@ -10570,6 +10560,8 @@ snapshots: node-releases@2.0.54: {} + node-releases@2.0.56: {} + nopt@10.0.1: dependencies: abbrev: 5.0.0 @@ -10631,7 +10623,7 @@ snapshots: openapi3-ts@4.6.1: dependencies: - yaml: 2.9.0 + yaml: 2.9.1 optionator@0.9.4: dependencies: @@ -10769,10 +10761,6 @@ snapshots: p-cancelable@4.0.1: {} - p-event@6.0.1: - dependencies: - p-timeout: 6.1.4 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -10783,8 +10771,6 @@ snapshots: p-map@7.0.8: {} - p-timeout@6.1.4: {} - pac-proxy-agent@9.1.0: dependencies: agent-base: 9.0.0 @@ -11398,12 +11384,6 @@ snapshots: striptags@3.2.0: {} - super-regex@1.1.0: - dependencies: - function-timeout: 1.0.2 - make-asynchronous: 1.1.0 - time-span: 5.1.0 - supports-color@10.2.2: {} supports-color@7.2.0: @@ -11440,10 +11420,6 @@ snapshots: dependencies: real-require: 1.0.0 - time-span@5.1.0: - dependencies: - convert-hrtime: 5.0.0 - timers-ext@0.1.8: dependencies: es5-ext: 0.10.64 @@ -11699,6 +11675,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.3.3(browserslist@4.29.0): + dependencies: + browserslist: 4.29.0 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -11756,16 +11738,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0): + vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11779,12 +11761,12 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 - yaml: 2.9.0 + yaml: 2.9.1 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11801,7 +11783,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.0) + vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -11822,8 +11804,6 @@ snapshots: web-streams-polyfill@3.3.3: {} - web-worker@1.5.0: {} - webidl-conversions@3.0.1: {} webidl-conversions@8.0.1: {} @@ -11978,6 +11958,8 @@ snapshots: yaml@2.9.0: {} + yaml@2.9.1: {} + yargs-parser@21.1.1: {} yargs-parser@22.0.0: {} diff --git a/scripts/workflow/build-routes.ts b/scripts/workflow/build-routes.ts index 89f77b0b9577..6c318efc1ca6 100644 --- a/scripts/workflow/build-routes.ts +++ b/scripts/workflow/build-routes.ts @@ -69,25 +69,26 @@ for (const namespace in namespaces) { const parsedDomain = parse(new URL('https://' + radarItem.source[0]).hostname); const subdomain = parsedDomain.subdomain || '.'; const domain = parsedDomain.domain; - if (domain) { - if (!Object.hasOwn(radar, domain)) { - radar[domain] = { - _name: namespaceData.name, - }; - } - if (!Object.hasOwn(radar[domain], subdomain)) { - radar[domain][subdomain] = []; - } - radar[domain][subdomain].push({ - title: radarItem.title || data.name, - docs: `https://docs.rsshub.app/routes/${categories[0]}`, - source: radarItem.source.map((source) => { - const sourceURL = new URL('https://' + source); - return sourceURL.pathname + sourceURL.search + sourceURL.hash; - }), - target: radarItem.target ? `/${namespace}${radarItem.target}` : realPath, - }); + if (!domain) { + continue; } + if (!Object.hasOwn(radar, domain)) { + radar[domain] = { + _name: namespaceData.name, + }; + } + if (!Object.hasOwn(radar[domain], subdomain)) { + radar[domain][subdomain] = []; + } + radar[domain][subdomain].push({ + title: radarItem.title || data.name, + docs: `https://docs.rsshub.app/routes/${categories[0]}`, + source: radarItem.source.map((source) => { + const sourceURL = new URL('https://' + source); + return sourceURL.pathname + sourceURL.search + sourceURL.hash; + }), + target: radarItem.target ? `/${namespace}${radarItem.target}` : realPath, + }); } } data.module = `() => import('@/routes/${namespace}/${data.location}')`; diff --git a/scripts/workflow/test-issue/call-maintainer.mjs b/scripts/workflow/test-issue/call-maintainer.mjs index 1e35f3b1df26..06a1cd669e16 100644 --- a/scripts/workflow/test-issue/call-maintainer.mjs +++ b/scripts/workflow/test-issue/call-maintainer.mjs @@ -143,18 +143,19 @@ export default async function callMaintainer({ github, context, core }) { continue; } - if (main.length > 0) { - const pingStr = main - .map((e) => { - if (dndUsernames.has(e)) { - return `\`@${e}\``; // Wrap in an inline code block to make sure no mention will be sent - } - return `@${e}`; - }) - .join(' '); - comments += `- \`${route}\`: ${pingStr}\n`; - successCount += 1; + if (main.length === 0) { + continue; } + const pingStr = main + .map((e) => { + if (dndUsernames.has(e)) { + return `\`@${e}\``; // Wrap in an inline code block to make sure no mention will be sent + } + return `@${e}`; + }) + .join(' '); + comments += `- \`${route}\`: ${pingStr}\n`; + successCount += 1; } const labels = ['']; diff --git a/tests/cache-atomic-backends.test.ts b/tests/cache-atomic-backends.test.ts index c68a91e5d57e..665d37be8589 100644 --- a/tests/cache-atomic-backends.test.ts +++ b/tests/cache-atomic-backends.test.ts @@ -47,9 +47,11 @@ describe('atomic cache backends', () => { await cache.globalCache.set('same-request', '0', 60); expect(await cache.globalCache.claim('same-request', 60)).toBe(true); - if (backend === 'redis') { - expect(state.eval).toHaveBeenCalledTimes(3); - expect(state.eval).toHaveBeenCalledWith(expect.stringContaining("redis.call('GET', KEYS[1])"), 1, 'same-request', 60); + if (backend !== 'redis') { + return; } + + expect(state.eval).toHaveBeenCalledTimes(3); + expect(state.eval).toHaveBeenCalledWith(expect.stringContaining("redis.call('GET', KEYS[1])"), 1, 'same-request', 60); }); }); From e6654148760e26b987ea5eaf44877cb7187316af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:19:27 +0000 Subject: [PATCH 0890/1190] chore(deps): bump @scalar/hono-api-reference in the hono group (#23349) Bumps the hono group with 1 update: [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono). Updates `@scalar/hono-api-reference` from 0.12.1 to 0.12.2 - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) --- updated-dependencies: - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.12.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 274 +++++++++++++++++++++++++------------------------ 2 files changed, 142 insertions(+), 134 deletions(-) diff --git a/package.json b/package.json index 17644074a050..16e5ac714c21 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@oxc-parser/binding-wasm32-wasip1": "0.0.1", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.12.1", + "@scalar/hono-api-reference": "0.12.2", "@sentry/node": "10.75.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a950f57f6ee0..870d17ecb272 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,8 +89,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.12.1 - version: 0.12.1(hono@4.13.8) + specifier: 0.12.2 + version: 0.12.2(hono@4.13.8) '@sentry/node': specifier: 10.75.0 version: 10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) @@ -364,7 +364,7 @@ importers: version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 - version: 1.11.0(rollup@4.63.3) + version: 1.11.0(rollup@4.63.4) '@vitest/coverage-v8': specifier: 4.1.11 version: 4.1.11(vitest@4.1.11) @@ -2489,141 +2489,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.63.3': - resolution: {integrity: sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==} + '@rollup/rollup-android-arm-eabi@4.63.4': + resolution: {integrity: sha512-I+BSHzTAhKN2n7ZwGZsegGcZjDpLqFOMAtJz/u6uFGe0pUFbq56dEHjqJV/ZUdRJtNXNxA+hREUatZBvMR3Oiw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.63.3': - resolution: {integrity: sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==} + '@rollup/rollup-android-arm64@4.63.4': + resolution: {integrity: sha512-pu3BdjS2LtEzRu2elmGzS3fIeWSZy4BMDIaLNwjorO76+k2d0LMluijhsDx3KQyQBQ/lLUZCQA9/s6csvUfuhw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.63.3': - resolution: {integrity: sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==} + '@rollup/rollup-darwin-arm64@4.63.4': + resolution: {integrity: sha512-xfSrj9MHnWK9GaSqT9U0ImHtH/N8WZlHLx4cZHiuLcqs640hvZ3hLPd5UR2AZS57FaE8HrRUSpltbZdWRxHiDA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.63.3': - resolution: {integrity: sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==} + '@rollup/rollup-darwin-x64@4.63.4': + resolution: {integrity: sha512-bqU99PLJb/dqb3S0GIMdeuyAEETSUgZBoqXYd3Sd+WCsV+MmPhnN6JrotWyir31+QgH7EvvE5/mwGJlEoci8Fw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.63.3': - resolution: {integrity: sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==} + '@rollup/rollup-freebsd-arm64@4.63.4': + resolution: {integrity: sha512-JinsFZ5G40oXQb+sUuiA5x689vhr6dDYK0H0NL+rwKdL6CqnmYN8PE4ZwfRSoIjrCxqTQG/SLfTtSvHeGxoVlw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.63.3': - resolution: {integrity: sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==} + '@rollup/rollup-freebsd-x64@4.63.4': + resolution: {integrity: sha512-GAdA4UxpiNm27cLHr2GqXBpAD0x9FqwYBY7/YSP0Ss0/PNi4k8gbviqpIpYbVSRBaS2ZcegXEzgTQMbRNCwxCw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.63.3': - resolution: {integrity: sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==} + '@rollup/rollup-linux-arm-gnueabihf@4.63.4': + resolution: {integrity: sha512-qDd6NoA1znaLjp4jR5U/KWCdLAKDJNB8W9ChbbDaKbo0xA+Atln5HK6LFCZ4oJQpemtRZA288DCirFRjrspptw==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.63.3': - resolution: {integrity: sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==} + '@rollup/rollup-linux-arm-musleabihf@4.63.4': + resolution: {integrity: sha512-WtB5Tz5KTNINb8ZA+8sQ7bmjuS1JrRT7YverYIhUGdWWDlpzVWmIwuZE+jidkEXUn1l0zrEkaIMa8dHF3NGcsA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.63.3': - resolution: {integrity: sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==} + '@rollup/rollup-linux-arm64-gnu@4.63.4': + resolution: {integrity: sha512-VcQ3L1tjnkKzWjryAVaFhHEWcqOfICX9uxVVoDzm2t0DpgKRHd2zOpVrJc0xsWeBZcBFyYROCIBdyR/fS174pg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.63.3': - resolution: {integrity: sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==} + '@rollup/rollup-linux-arm64-musl@4.63.4': + resolution: {integrity: sha512-6+ZQX6P5s0cMDN2Ypb8Lbm2+/sZYmZjdaYny992ujUU9UKi/4CWoJWsl1pNvjWJHNHGK51m+jKGLlh1ylb2ifQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.63.3': - resolution: {integrity: sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==} + '@rollup/rollup-linux-loong64-gnu@4.63.4': + resolution: {integrity: sha512-D72ZnvkFkBXOfzMMQLcwfPLyGkKb7HZ9/mf97B7v6/P5Lbv4oFOtSY/uHbS8lH6uKUOxoKiuokdb50XZSzzbJw==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.63.3': - resolution: {integrity: sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==} + '@rollup/rollup-linux-loong64-musl@4.63.4': + resolution: {integrity: sha512-piU6BxeqA3O9KSu3kRCIQQtNqFFaTu21SEV4FwaRZowpnj3bLaWPZHw+xFqCs0XlJ+aOH3PTRWGoglH+mKA/OA==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.63.3': - resolution: {integrity: sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==} + '@rollup/rollup-linux-ppc64-gnu@4.63.4': + resolution: {integrity: sha512-/5PGpHwqt2EEEOUs1XwzubE/ucr0dWDQ+to3zqi4Ds7EWpwtQ79wXc4JBoxqj/OwpawTsKWzJxHfSuBOq3DrWA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.63.3': - resolution: {integrity: sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==} + '@rollup/rollup-linux-ppc64-musl@4.63.4': + resolution: {integrity: sha512-cX3beZDLWt7G2oJF+nhChiT+qtaihs+S2xi7ziGmVB+2pwPng6D0Ed0HmElQOgv2UsUmSJJLGwpBao/3TDx3VA==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.63.3': - resolution: {integrity: sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==} + '@rollup/rollup-linux-riscv64-gnu@4.63.4': + resolution: {integrity: sha512-1uz2mGWHyptR7DgHHrlbdRAjXK7v7elGZ9lMja910/RP+ZYbX6xAmCiU9UZSX4hqmgtHMv6lr5l3kq1HIOpcag==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.63.3': - resolution: {integrity: sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==} + '@rollup/rollup-linux-riscv64-musl@4.63.4': + resolution: {integrity: sha512-nLS8topojxyz7SRpKR2IODRpQ0XPZ+xaOXvT3+hqK/Uy8Lo5HFgkkIBiIrCu5tL5YqzTvgovGw55PwpahTAGig==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.63.3': - resolution: {integrity: sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==} + '@rollup/rollup-linux-s390x-gnu@4.63.4': + resolution: {integrity: sha512-gs7DRKotr3l3q+jGPQBjH0ng1FjlEDm5ueQrkw5JtQvtLyEIcLASqAEaor56BhkKRzk+IcQzrcanBdb/bBQn8g==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.63.3': - resolution: {integrity: sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==} + '@rollup/rollup-linux-x64-gnu@4.63.4': + resolution: {integrity: sha512-791ET7W17NnScOZM7h4dX5hYspxE28htPFsb1awY/NRR8+PRNkS53e475rDdxXXDrP+kwnCcNWg9CX5ztn/Aqw==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.63.3': - resolution: {integrity: sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==} + '@rollup/rollup-linux-x64-musl@4.63.4': + resolution: {integrity: sha512-iwZQRcmj7g88g3tzefIrQY7qvmuA/cfYwhrDtTBhsmukO4U2huVO5W+86XacUMRvdSFVAc6kZUZy21JaRwiB9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.63.3': - resolution: {integrity: sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==} + '@rollup/rollup-openbsd-x64@4.63.4': + resolution: {integrity: sha512-dVHFp9gRWrdTpnqQuGfCwd7hOQDatK1VCP2iWhLY/cGrOQs/ucFzJ6A5SRqbXX12ZDI8EUuejSM5kwg+ja7Png==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.63.3': - resolution: {integrity: sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==} + '@rollup/rollup-openharmony-arm64@4.63.4': + resolution: {integrity: sha512-t3NlauOW6gxZVVFcBEnO62Cb4wbyDFL416gTg1uFI/2tgqYQlf69FbSE115Ajre9I+c26Lk4mcmdFUsS/DGifQ==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.63.3': - resolution: {integrity: sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==} + '@rollup/rollup-win32-arm64-msvc@4.63.4': + resolution: {integrity: sha512-xWuIaSye5FWZF8+UYtVEcHtRJDN5kN9Kfgxx3Kq8XIov9KSKbc1fiqQCm90SKrgQbUXZelbnUhnlUJmfSE7P9A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.63.3': - resolution: {integrity: sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==} + '@rollup/rollup-win32-ia32-msvc@4.63.4': + resolution: {integrity: sha512-9ALJJUOg/ZflMJepVo2PlgsGxSaxN7SQ4Z8GoZfVlarWr6r3rkHUNsd/zAio7p4YMtChSMXPionxej4Hkf6CXQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.63.3': - resolution: {integrity: sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==} + '@rollup/rollup-win32-x64-gnu@4.63.4': + resolution: {integrity: sha512-blj9z5qx/Pv4WU0W1NMFDB97e0JH5ed+aZGywW8WCvp/NhWX/4PFAq5uu6Q0AebNn+Vo6KzUYDT++JzTT5ojlQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.63.3': - resolution: {integrity: sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==} + '@rollup/rollup-win32-x64-msvc@4.63.4': + resolution: {integrity: sha512-Erx822VRBwLa124shbj+wNXe//BOgMEctDV0m1aqTQdNO1S69DgNUCFKC1RCeZfixs1J31l6igk1ziyXErbigQ==} cpu: [x64] os: [win32] @@ -2636,26 +2636,26 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.4.0': - resolution: {integrity: sha512-a8OUod1LZbNqkPv73ijRPL3MH2/3E92I5Awa9mGXtCaIVavY3JcF7+qZW4GOtffihTfuD/CxhZ+CnQhCZds2oQ==} + '@scalar/client-side-rendering@0.4.1': + resolution: {integrity: sha512-4Ha2ihIPFwmhheSU6U9DlpjgRQ2YOqq2JSmc4ubT2ERApaA/HzuZ1IMyLXDvRICI+KbDducfGw5th9O+TQf+6A==} engines: {node: '>=22'} - '@scalar/helpers@0.11.3': - resolution: {integrity: sha512-4zPzuNTXObDUtZS93xzAoK83ddHDgBGifv/vKFe6bHqEl5i+IMLTTtEHOaaOZK4dusPEXcXsU5TBSaY/I6Mi+A==} + '@scalar/helpers@0.12.0': + resolution: {integrity: sha512-rcX0rFLiWWc0VBr/E+HkatubL0I0ZzxASQkd3032NWEMAEfNd2TiJ9kH46HwbSAOOl696ghxymWegjpjjvxwzA==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.12.1': - resolution: {integrity: sha512-RbAMvXihbZLFn3aFLQ2cW/AVIyPILujCUbaBcFVBztL6mbcDk87H10xCa5Bl5xVKalmoJwnUIV+8zMxNizh07w==} + '@scalar/hono-api-reference@0.12.2': + resolution: {integrity: sha512-VrdMxw/HmejceR9rH0eu4uYKWBILnts5Qj2tgzplufoQbPc+FiFsRKGQ9ZNtZ4CZuvrhEj1y/HrmBX5Zz2sqpg==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/schemas@0.9.0': - resolution: {integrity: sha512-yYRlIWzw+7HuIX4z7rk7tg4y1nERwvvWKbolZOm7LveSTrppllGKyjtnIqS5uXsmJddERxuurSgDW224gGJlFQ==} + '@scalar/schemas@0.10.0': + resolution: {integrity: sha512-jFqfd+oqqlTfs87V+QkIrR9DiRv+/h1x3GzSGbGe1qrM6togeXwvVG4aurn+LZ+Ej9of54/6FmoI8DPuLOKx7A==} engines: {node: '>=22'} - '@scalar/types@0.19.0': - resolution: {integrity: sha512-EKeoWgUlP+uepbM/zEHKbsdpBNyOmSrw6DdU/WC0p53gz5uRzI7d/IEecIP9SQMUkResPR9DmWeWfFQftG7vqg==} + '@scalar/types@0.20.0': + resolution: {integrity: sha512-+05slk/Q6MRMqEQt6eM77Jt9DnDPAvfcc/6r7mbrXhIqJSp3oAPrixpAnp1knBSlFaxgixcK/GMKw9QDfD+Zgg==} engines: {node: '>=22'} '@scalar/validation@0.6.3': @@ -3728,7 +3728,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -5524,8 +5524,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.63.3: - resolution: {integrity: sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==} + rollup@4.63.4: + resolution: {integrity: sha512-4U0liVayNIoLp3GFl1FcI8561WepLnZ1rqfraGh7S9B3Ur5F9S283y8Futii7RUU2C/97tOBmBy7nYvhoiOpbQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5928,6 +5928,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.10.0: + resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} + engines: {node: '>=20'} + type-fest@5.8.0: resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} engines: {node: '>=20'} @@ -7827,87 +7831,87 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.63.3)': + '@rollup/pluginutils@5.4.0(rollup@4.63.4)': dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 picomatch: 4.0.7 optionalDependencies: - rollup: 4.63.3 + rollup: 4.63.4 - '@rollup/rollup-android-arm-eabi@4.63.3': + '@rollup/rollup-android-arm-eabi@4.63.4': optional: true - '@rollup/rollup-android-arm64@4.63.3': + '@rollup/rollup-android-arm64@4.63.4': optional: true - '@rollup/rollup-darwin-arm64@4.63.3': + '@rollup/rollup-darwin-arm64@4.63.4': optional: true - '@rollup/rollup-darwin-x64@4.63.3': + '@rollup/rollup-darwin-x64@4.63.4': optional: true - '@rollup/rollup-freebsd-arm64@4.63.3': + '@rollup/rollup-freebsd-arm64@4.63.4': optional: true - '@rollup/rollup-freebsd-x64@4.63.3': + '@rollup/rollup-freebsd-x64@4.63.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.63.3': + '@rollup/rollup-linux-arm-gnueabihf@4.63.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.63.3': + '@rollup/rollup-linux-arm-musleabihf@4.63.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.63.3': + '@rollup/rollup-linux-arm64-gnu@4.63.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.63.3': + '@rollup/rollup-linux-arm64-musl@4.63.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.63.3': + '@rollup/rollup-linux-loong64-gnu@4.63.4': optional: true - '@rollup/rollup-linux-loong64-musl@4.63.3': + '@rollup/rollup-linux-loong64-musl@4.63.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.63.3': + '@rollup/rollup-linux-ppc64-gnu@4.63.4': optional: true - '@rollup/rollup-linux-ppc64-musl@4.63.3': + '@rollup/rollup-linux-ppc64-musl@4.63.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.63.3': + '@rollup/rollup-linux-riscv64-gnu@4.63.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.63.3': + '@rollup/rollup-linux-riscv64-musl@4.63.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.63.3': + '@rollup/rollup-linux-s390x-gnu@4.63.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.63.3': + '@rollup/rollup-linux-x64-gnu@4.63.4': optional: true - '@rollup/rollup-linux-x64-musl@4.63.3': + '@rollup/rollup-linux-x64-musl@4.63.4': optional: true - '@rollup/rollup-openbsd-x64@4.63.3': + '@rollup/rollup-openbsd-x64@4.63.4': optional: true - '@rollup/rollup-openharmony-arm64@4.63.3': + '@rollup/rollup-openharmony-arm64@4.63.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.63.3': + '@rollup/rollup-win32-arm64-msvc@4.63.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.63.3': + '@rollup/rollup-win32-ia32-msvc@4.63.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.63.3': + '@rollup/rollup-win32-x64-gnu@4.63.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.63.3': + '@rollup/rollup-win32-x64-msvc@4.63.4': optional: true '@rss3/api-core@0.0.25': @@ -7925,29 +7929,29 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.4.0': + '@scalar/client-side-rendering@0.4.1': dependencies: - '@scalar/schemas': 0.9.0 - '@scalar/types': 0.19.0 + '@scalar/schemas': 0.10.0 + '@scalar/types': 0.20.0 '@scalar/validation': 0.6.3 - '@scalar/helpers@0.11.3': {} + '@scalar/helpers@0.12.0': {} - '@scalar/hono-api-reference@0.12.1(hono@4.13.8)': + '@scalar/hono-api-reference@0.12.2(hono@4.13.8)': dependencies: - '@scalar/client-side-rendering': 0.4.0 + '@scalar/client-side-rendering': 0.4.1 hono: 4.13.8 - '@scalar/schemas@0.9.0': + '@scalar/schemas@0.10.0': dependencies: - '@scalar/helpers': 0.11.3 + '@scalar/helpers': 0.12.0 '@scalar/validation': 0.6.3 - '@scalar/types@0.19.0': + '@scalar/types@0.20.0': dependencies: - '@scalar/helpers': 0.11.3 + '@scalar/helpers': 0.12.0 nanoid: 5.1.16 - type-fest: 5.8.0 + type-fest: 5.10.0 zod: 4.6.5 '@scalar/validation@0.6.3': {} @@ -8335,10 +8339,10 @@ snapshots: dependencies: '@typescript/old': typescript@6.0.3 - '@vercel/nft@1.11.0(rollup@4.63.3)': + '@vercel/nft@1.11.0(rollup@4.63.4)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.4.0(rollup@4.63.3) + '@rollup/pluginutils': 5.4.0(rollup@4.63.4) acorn: 8.18.0 acorn-import-attributes: 1.9.5(acorn@8.18.0) async-sema: 3.1.1 @@ -11150,36 +11154,36 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.9 '@rolldown/binding-win32-x64-msvc': 1.2.9 - rollup@4.63.3: + rollup@4.63.4: dependencies: '@types/estree': 1.0.9 optionalDependencies: '@napi-rs/lzma-linux-x64-gnu': 1.5.1 - '@rollup/rollup-android-arm-eabi': 4.63.3 - '@rollup/rollup-android-arm64': 4.63.3 - '@rollup/rollup-darwin-arm64': 4.63.3 - '@rollup/rollup-darwin-x64': 4.63.3 - '@rollup/rollup-freebsd-arm64': 4.63.3 - '@rollup/rollup-freebsd-x64': 4.63.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.63.3 - '@rollup/rollup-linux-arm-musleabihf': 4.63.3 - '@rollup/rollup-linux-arm64-gnu': 4.63.3 - '@rollup/rollup-linux-arm64-musl': 4.63.3 - '@rollup/rollup-linux-loong64-gnu': 4.63.3 - '@rollup/rollup-linux-loong64-musl': 4.63.3 - '@rollup/rollup-linux-ppc64-gnu': 4.63.3 - '@rollup/rollup-linux-ppc64-musl': 4.63.3 - '@rollup/rollup-linux-riscv64-gnu': 4.63.3 - '@rollup/rollup-linux-riscv64-musl': 4.63.3 - '@rollup/rollup-linux-s390x-gnu': 4.63.3 - '@rollup/rollup-linux-x64-gnu': 4.63.3 - '@rollup/rollup-linux-x64-musl': 4.63.3 - '@rollup/rollup-openbsd-x64': 4.63.3 - '@rollup/rollup-openharmony-arm64': 4.63.3 - '@rollup/rollup-win32-arm64-msvc': 4.63.3 - '@rollup/rollup-win32-ia32-msvc': 4.63.3 - '@rollup/rollup-win32-x64-gnu': 4.63.3 - '@rollup/rollup-win32-x64-msvc': 4.63.3 + '@rollup/rollup-android-arm-eabi': 4.63.4 + '@rollup/rollup-android-arm64': 4.63.4 + '@rollup/rollup-darwin-arm64': 4.63.4 + '@rollup/rollup-darwin-x64': 4.63.4 + '@rollup/rollup-freebsd-arm64': 4.63.4 + '@rollup/rollup-freebsd-x64': 4.63.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.4 + '@rollup/rollup-linux-arm-musleabihf': 4.63.4 + '@rollup/rollup-linux-arm64-gnu': 4.63.4 + '@rollup/rollup-linux-arm64-musl': 4.63.4 + '@rollup/rollup-linux-loong64-gnu': 4.63.4 + '@rollup/rollup-linux-loong64-musl': 4.63.4 + '@rollup/rollup-linux-ppc64-gnu': 4.63.4 + '@rollup/rollup-linux-ppc64-musl': 4.63.4 + '@rollup/rollup-linux-riscv64-gnu': 4.63.4 + '@rollup/rollup-linux-riscv64-musl': 4.63.4 + '@rollup/rollup-linux-s390x-gnu': 4.63.4 + '@rollup/rollup-linux-x64-gnu': 4.63.4 + '@rollup/rollup-linux-x64-musl': 4.63.4 + '@rollup/rollup-openbsd-x64': 4.63.4 + '@rollup/rollup-openharmony-arm64': 4.63.4 + '@rollup/rollup-win32-arm64-msvc': 4.63.4 + '@rollup/rollup-win32-ia32-msvc': 4.63.4 + '@rollup/rollup-win32-x64-gnu': 4.63.4 + '@rollup/rollup-win32-x64-msvc': 4.63.4 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -11557,6 +11561,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.10.0: + dependencies: + tagged-tag: 1.0.0 + type-fest@5.8.0: dependencies: tagged-tag: 1.0.0 @@ -11753,7 +11761,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 postcss: 8.5.28 - rollup: 4.63.3 + rollup: 4.63.4 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.6.1 From cf5ce5fcd04caff8f7eb931a6e29e0609e6fd135 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:19:45 +0000 Subject: [PATCH 0891/1190] chore(deps): bump dotenv from 18.0.0 to 18.0.1 (#23354) Bumps [dotenv](https://github.com/motdotla/dotenv) from 18.0.0 to 18.0.1. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v18.0.0...v18.0.1) --- updated-dependencies: - dependency-name: dotenv dependency-version: 18.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 16e5ac714c21..e6a73101e5b2 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "currency-symbol-map": "5.1.0", "dayjs": "1.11.23", "destr": "2.0.5", - "dotenv": "18.0.0", + "dotenv": "18.0.1", "entities": "8.1.0", "etag": "1.8.1", "fanfou-sdk": "6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 870d17ecb272..db89520baa2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,8 +116,8 @@ importers: specifier: 2.0.5 version: 2.0.5 dotenv: - specifier: 18.0.0 - version: 18.0.0 + specifier: 18.0.1 + version: 18.0.1 entities: specifier: 8.1.0 version: 8.1.0 @@ -3767,8 +3767,8 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@18.0.0: - resolution: {integrity: sha512-16YSUvsKtKbwrTbESvtTSZzzJUug6jdFdqiBFg9Q6Bk1JY9r0hAtrtTiWxdI9YrsgOTmkjZRdgU0n3LnwGpECw==} + dotenv@18.0.1: + resolution: {integrity: sha512-0eR4m4D/jH5eaI3evo2ZqqMii5mrTCR12v12VChdie6O1gPHs7XAjzTep0CWc1Bd+oNx3vVfbi734dK4zghigw==} engines: {node: '>=12'} hasBin: true @@ -8954,7 +8954,7 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@18.0.0: {} + dotenv@18.0.1: {} dts-resolver@3.0.0(oxc-resolver@11.24.2): optionalDependencies: From 12a2a7e812f3d6a584db81a29a36709fbc351dac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:20:35 +0000 Subject: [PATCH 0892/1190] chore(deps): bump lru-cache from 11.5.2 to 11.5.3 (#23352) Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 11.5.2 to 11.5.3. - [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-lru-cache/compare/v11.5.2...v11.5.3) --- updated-dependencies: - dependency-name: lru-cache dependency-version: 11.5.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index e6a73101e5b2..0da27e203e55 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "jsdom": "30.1.0", "json-bigint": "1.0.0", "jsrsasign": "11.1.5", - "lru-cache": "11.5.2", + "lru-cache": "11.5.3", "lz-string": "1.5.0", "markdown-it": "15.0.2", "mixi2": "0.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db89520baa2c..e006ed1d80a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,8 +170,8 @@ importers: specifier: 11.1.5 version: 11.1.5 lru-cache: - specifier: 11.5.2 - version: 11.5.2 + specifier: 11.5.3 + version: 11.5.3 lz-string: specifier: 1.5.0 version: 1.5.0 @@ -4738,8 +4738,8 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@11.5.2: - resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} engines: {node: 20 || >=22} lru-queue@0.1.0: @@ -6453,14 +6453,14 @@ snapshots: '@csstools/css-color-parser': 4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1) '@csstools/css-tokenizer': 4.0.1 - lru-cache: 11.5.2 + lru-cache: 11.5.3 '@asamuzakjp/dom-selector@9.1.4': dependencies: bidi-js: 1.1.0 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.2 + lru-cache: 11.5.3 '@asteasolutions/zod-to-openapi@9.1.0(zod@4.6.5)': dependencies: @@ -9904,7 +9904,7 @@ snapshots: decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0(@noble/hashes@2.3.0) is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.2 + lru-cache: 11.5.3 parse5: 8.0.1 saxes: 6.0.0 tough-cookie: 6.0.2 @@ -10083,7 +10083,7 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@11.5.2: {} + lru-cache@11.5.3: {} lru-queue@0.1.0: dependencies: @@ -10835,7 +10835,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.5.2 + lru-cache: 11.5.3 minipass: 7.1.3 path-to-regexp@6.3.0: {} From 55cb2309b124e2826dfb03dbce559061c88fd947 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:22:02 +0000 Subject: [PATCH 0893/1190] chore(deps-dev): bump @types/node from 26.6.1 to 26.6.2 (#23353) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.6.1 to 26.6.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.6.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 156 ++++++++++++++++++++++++------------------------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 0da27e203e55..7ffa54fb2e30 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "@types/json-bigint": "1.0.4", "@types/jsrsasign": "10.5.15", "@types/module-alias": "2.0.4", - "@types/node": "26.6.1", + "@types/node": "26.6.2", "@types/sanitize-html": "2.16.1", "@typescript-eslint/eslint-plugin": "8.70.0", "@typescript-eslint/parser": "8.70.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e006ed1d80a5..412d7fdca7dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -307,7 +307,7 @@ importers: version: 1.3.6 '@cloudflare/vitest-plugin': specifier: 1.1.9 - version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': specifier: 5.20260915.1 version: 5.20260915.1 @@ -351,8 +351,8 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 26.6.1 - version: 26.6.1 + specifier: 26.6.2 + version: 26.6.2 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 @@ -379,7 +379,7 @@ importers: version: 10.10.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.6.1)(eslint@10.10.0(jiti@2.7.0)) + version: 9.1.1(@types/node@26.6.2)(eslint@10.10.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) @@ -418,7 +418,7 @@ importers: version: 3.0.5 msw: specifier: 2.15.0 - version: 2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2) + version: 2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2) node-network-devtools: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) @@ -466,13 +466,13 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) wrangler: specifier: 4.131.2 - version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -2831,8 +2831,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@26.6.1': - resolution: {integrity: sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==} + '@types/node@26.6.2': + resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -6556,15 +6556,15 @@ snapshots: optionalDependencies: workerd: 1.20260911.1 - '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) - wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -7103,76 +7103,76 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.6.1)': + '@inquirer/checkbox@4.3.2(@types/node@26.6.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.1) + '@inquirer/core': 10.3.2(@types/node@26.6.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.1) + '@inquirer/type': 3.0.10(@types/node@26.6.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/confirm@5.1.21(@types/node@26.6.1)': + '@inquirer/confirm@5.1.21(@types/node@26.6.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.6.1) - '@inquirer/type': 3.0.10(@types/node@26.6.1) + '@inquirer/core': 10.3.2(@types/node@26.6.2) + '@inquirer/type': 3.0.10(@types/node@26.6.2) optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/confirm@6.1.1(@types/node@26.6.1)': + '@inquirer/confirm@6.1.1(@types/node@26.6.2)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.6.1) - '@inquirer/type': 4.0.7(@types/node@26.6.1) + '@inquirer/core': 11.2.1(@types/node@26.6.2) + '@inquirer/type': 4.0.7(@types/node@26.6.2) optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/core@10.3.2(@types/node@26.6.1)': + '@inquirer/core@10.3.2(@types/node@26.6.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.1) + '@inquirer/type': 3.0.10(@types/node@26.6.2) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/core@11.2.1(@types/node@26.6.1)': + '@inquirer/core@11.2.1(@types/node@26.6.2)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.6.1) + '@inquirer/type': 4.0.7(@types/node@26.6.2) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@inquirer/figures@1.0.15': {} '@inquirer/figures@2.0.7': {} - '@inquirer/select@4.4.2(@types/node@26.6.1)': + '@inquirer/select@4.4.2(@types/node@26.6.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.6.1) + '@inquirer/core': 10.3.2(@types/node@26.6.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.6.1) + '@inquirer/type': 3.0.10(@types/node@26.6.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/type@3.0.10(@types/node@26.6.1)': + '@inquirer/type@3.0.10(@types/node@26.6.2)': optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@inquirer/type@4.0.7(@types/node@26.6.1)': + '@inquirer/type@4.0.7(@types/node@26.6.2)': optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@ioredis/commands@2.0.0': {} @@ -8048,7 +8048,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/caseless@0.12.5': {} @@ -8061,7 +8061,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/crypto-js@4.2.2': {} @@ -8082,11 +8082,11 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/express-serve-static-core@5.1.3': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8100,7 +8100,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/html-to-text@9.0.4': {} @@ -8112,7 +8112,7 @@ snapshots: '@types/jsdom@30.0.0': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/tough-cookie': 4.0.5 parse5: 8.0.1 undici-types: 8.10.2 @@ -8123,7 +8123,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/jsrsasign@10.5.15': {} @@ -8135,7 +8135,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@26.6.1': + '@types/node@26.6.2': dependencies: undici-types: 8.9.0 @@ -8151,7 +8151,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -8161,16 +8161,16 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/statuses@2.0.6': {} @@ -8370,7 +8370,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) '@vitest/expect@4.1.11': dependencies: @@ -8381,14 +8381,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + msw: 2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) '@vitest/pretty-format@4.1.11': dependencies: @@ -9154,12 +9154,12 @@ snapshots: eslint: 10.10.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.6.1)(eslint@10.10.0(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.6.2)(eslint@10.10.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 - '@inquirer/checkbox': 4.3.2(@types/node@26.6.1) - '@inquirer/confirm': 5.1.21(@types/node@26.6.1) - '@inquirer/select': 4.4.2(@types/node@26.6.1) + '@inquirer/checkbox': 4.3.2(@types/node@26.6.2) + '@inquirer/confirm': 5.1.21(@types/node@26.6.2) + '@inquirer/select': 4.4.2(@types/node@26.6.2) eslint: 10.10.0(jiti@2.7.0) eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) optionator: 0.9.4 @@ -9890,7 +9890,7 @@ snapshots: jsdoc-type-pratt-parser@9.2.1: dependencies: '@types/estree': 1.0.9 - '@types/node': 26.6.1 + '@types/node': 26.6.2 jsdom@30.1.0(@noble/hashes@2.3.0): dependencies: @@ -10452,10 +10452,10 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.4(@types/node@26.6.1) + sharp: 0.35.4(@types/node@26.6.2) undici: 7.29.0 workerd: 1.20260911.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -10485,9 +10485,9 @@ snapshots: ms@2.1.3: {} - msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2): + msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2): dependencies: - '@inquirer/confirm': 6.1.1(@types/node@26.6.1) + '@inquirer/confirm': 6.1.1(@types/node@26.6.2) '@mswjs/interceptors': 0.41.9(patch_hash=4812d3969aa7556a2a52094a00be1d5f5e38f768b5f7df238e0128415253c171) '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -10930,7 +10930,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 26.6.1 + '@types/node': 26.6.2 long: 5.3.2 proxy-agent-negotiate@1.1.0: {} @@ -11227,7 +11227,7 @@ snapshots: set-cookie-parser@3.1.2: {} - sharp@0.35.4(@types/node@26.6.1): + sharp@0.35.4(@types/node@26.6.2): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -11258,7 +11258,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.4 '@img/sharp-win32-ia32': 0.35.4 '@img/sharp-win32-x64': 0.35.4 - '@types/node': 26.6.1 + '@types/node': 26.6.2 shebang-command@2.0.0: dependencies: @@ -11746,16 +11746,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1): + vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11764,17 +11764,17 @@ snapshots: rollup: 4.63.4 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 tsx: 4.23.13 yaml: 2.9.1 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.1)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.1)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11791,11 +11791,11 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.6.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) jsdom: 30.1.0(@noble/hashes@2.3.0) transitivePeerDependencies: @@ -11884,13 +11884,13 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260911.1 '@cloudflare/workerd-windows-64': 1.20260911.1 - wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.6.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 workerd: 1.20260911.1 From 74230b9ea3573c3d746d0c61f8be52bcae9571a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:27:07 +0000 Subject: [PATCH 0894/1190] chore(deps-dev): bump oxlint-tsgolint in the oxc group (#23350) Bumps the oxc group with 1 update: [oxlint-tsgolint](https://github.com/oxc-project/tsgolint). Updates `oxlint-tsgolint` from 7.0.2001 to 7.0.2002 - [Release notes](https://github.com/oxc-project/tsgolint/releases) - [Commits](https://github.com/oxc-project/tsgolint/compare/v7.0.2001...v7.0.2002) --- updated-dependencies: - dependency-name: oxlint-tsgolint dependency-version: 7.0.2002 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: oxc ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 66 +++++++++++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 7ffa54fb2e30..5ed7dbc8bbf6 100644 --- a/package.json +++ b/package.json @@ -194,7 +194,7 @@ "oxfmt": "0.68.0", "oxlint": "1.83.0", "oxlint-plugin-eslint": "1.83.0", - "oxlint-tsgolint": "7.0.2001", + "oxlint-tsgolint": "7.0.2002", "remark": "15.0.1", "remark-gfm": "4.0.1", "remark-pangu": "2.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 412d7fdca7dd..421eddbb7ba7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -427,13 +427,13 @@ importers: version: 0.68.0 oxlint: specifier: 1.83.0 - version: 1.83.0(oxlint-tsgolint@7.0.2001) + version: 1.83.0(oxlint-tsgolint@7.0.2002) oxlint-plugin-eslint: specifier: 1.83.0 version: 1.83.0 oxlint-tsgolint: - specifier: 7.0.2001 - version: 7.0.2001 + specifier: 7.0.2002 + version: 7.0.2002 remark: specifier: 15.0.1 version: 15.0.1 @@ -2172,33 +2172,33 @@ packages: cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@7.0.2001': - resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==} + '@oxlint-tsgolint/darwin-arm64@7.0.2002': + resolution: {integrity: sha512-E3yYb/tI+5M3SqGgKwrwUD0nmjA2FMGD1xFbma46qi8Y7+nPTlGY6bZk/hqxijc9Vda/tdtUxQ2d5zGIEX9qlg==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@7.0.2001': - resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==} + '@oxlint-tsgolint/darwin-x64@7.0.2002': + resolution: {integrity: sha512-qEaMySdvzs8faaJwMO4dFtPFCa+degFCz+HbkFQ0REcIJqJ16KmC0smt30FAI9TeEHCoAtz6sU/6acJvi4uaxg==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@7.0.2001': - resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==} + '@oxlint-tsgolint/linux-arm64@7.0.2002': + resolution: {integrity: sha512-bqPObCNIR6tDQSc0Xn9Nu7EfWRXFB+qFzADRl/q6YA8zT9YUg3pGUbmicV2hV91uLpiot74so9ocGjx35SaX6w==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@7.0.2001': - resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==} + '@oxlint-tsgolint/linux-x64@7.0.2002': + resolution: {integrity: sha512-E5CcRALAYdiQMbdYjEqlLOiYuEdt21RtXiyxIoD8bfypdfkpWppacijKOR4i0DtLXFRVwCGQggZBWk5bOIpU1A==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@7.0.2001': - resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==} + '@oxlint-tsgolint/win32-arm64@7.0.2002': + resolution: {integrity: sha512-L+ul0ZFDiz/ZmTwCQ1J/qS7S6s45ktiaFqb+ZIzx+1sSdtdRxm8/H0+p4VDQYrCKBFzKNb6VjUlm48bNEe3BUg==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@7.0.2001': - resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==} + '@oxlint-tsgolint/win32-x64@7.0.2002': + resolution: {integrity: sha512-zkZeJfo4UmYiCzj77u85eCDpdxOiQiSAsV6j9Pvj5e7Vu5+MvwLtK1xmVutE77tJmUJ3eBOSXHR6WgY4AwHHGQ==} cpu: [x64] os: [win32] @@ -5162,8 +5162,8 @@ packages: resolution: {integrity: sha512-9v2O7rkCkT2MzaAoviunkZ+O/BjhjhHXZKg8P0iAPfLYfziQwTHTJWqlY/uGOPAzjdC+VffHcRliMgJkxIttgg==} engines: {node: ^20.19.0 || >=22.12.0} - oxlint-tsgolint@7.0.2001: - resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} + oxlint-tsgolint@7.0.2002: + resolution: {integrity: sha512-rQAj2RAQM57nCwTd3PhtnAkREGW2StqI47tzudNlJF8WwGommM7rVpeP4O1O8/ZiuDZ9f2GnTESzLHGTluwkvQ==} hasBin: true oxlint@1.83.0: @@ -7652,22 +7652,22 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.68.0': optional: true - '@oxlint-tsgolint/darwin-arm64@7.0.2001': + '@oxlint-tsgolint/darwin-arm64@7.0.2002': optional: true - '@oxlint-tsgolint/darwin-x64@7.0.2001': + '@oxlint-tsgolint/darwin-x64@7.0.2002': optional: true - '@oxlint-tsgolint/linux-arm64@7.0.2001': + '@oxlint-tsgolint/linux-arm64@7.0.2002': optional: true - '@oxlint-tsgolint/linux-x64@7.0.2001': + '@oxlint-tsgolint/linux-x64@7.0.2002': optional: true - '@oxlint-tsgolint/win32-arm64@7.0.2001': + '@oxlint-tsgolint/win32-arm64@7.0.2002': optional: true - '@oxlint-tsgolint/win32-x64@7.0.2001': + '@oxlint-tsgolint/win32-x64@7.0.2002': optional: true '@oxlint/binding-android-arm-eabi@1.83.0': @@ -10731,16 +10731,16 @@ snapshots: oxlint-plugin-eslint@1.83.0: {} - oxlint-tsgolint@7.0.2001: + oxlint-tsgolint@7.0.2002: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 7.0.2001 - '@oxlint-tsgolint/darwin-x64': 7.0.2001 - '@oxlint-tsgolint/linux-arm64': 7.0.2001 - '@oxlint-tsgolint/linux-x64': 7.0.2001 - '@oxlint-tsgolint/win32-arm64': 7.0.2001 - '@oxlint-tsgolint/win32-x64': 7.0.2001 - - oxlint@1.83.0(oxlint-tsgolint@7.0.2001): + '@oxlint-tsgolint/darwin-arm64': 7.0.2002 + '@oxlint-tsgolint/darwin-x64': 7.0.2002 + '@oxlint-tsgolint/linux-arm64': 7.0.2002 + '@oxlint-tsgolint/linux-x64': 7.0.2002 + '@oxlint-tsgolint/win32-arm64': 7.0.2002 + '@oxlint-tsgolint/win32-x64': 7.0.2002 + + oxlint@1.83.0(oxlint-tsgolint@7.0.2002): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.83.0 '@oxlint/binding-android-arm64': 1.83.0 @@ -10761,7 +10761,7 @@ snapshots: '@oxlint/binding-win32-arm64-msvc': 1.83.0 '@oxlint/binding-win32-ia32-msvc': 1.83.0 '@oxlint/binding-win32-x64-msvc': 1.83.0 - oxlint-tsgolint: 7.0.2001 + oxlint-tsgolint: 7.0.2002 p-cancelable@4.0.1: {} From 45c394bd59dffc07322dbb5340c241eebc0b7226 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 20:34:09 +0800 Subject: [PATCH 0895/1190] chore(deps): bump the nix group with 2 updates (#23355) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `ccb4a99` to `2ba2f9e` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/ccb4a99d35474afd07634ee194174ae40f7d98af...2ba2f9ed32596289874c58981a4d3e56cadd639d) Updates `nixpkgs` from `b1b8759` to `e554fab` - [Commits](https://github.com/NixOS/nixpkgs/compare/b1b875982b17dabde9b4a37f3e229e74913e6db3...e554fab72f81915600f3f449b786fd9af40439a5) --- updated-dependencies: - dependency-name: devenv dependency-version: 2ba2f9ed32596289874c58981a4d3e56cadd639d dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: e554fab72f81915600f3f449b786fd9af40439a5 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 8709e48db0c4..3dd110d9e354 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789691577, - "narHash": "sha256-mS9lQo1gpKzwWMYgzdxc4yv4sIdwqI0GfFMCbV4LZtA=", + "lastModified": 1789773873, + "narHash": "sha256-lFR8A3JzpXn4Az9quklprOje/+Njyh9+bP+CE1TCIxI=", "owner": "cachix", "repo": "devenv", - "rev": "ccb4a99d35474afd07634ee194174ae40f7d98af", + "rev": "2ba2f9ed32596289874c58981a4d3e56cadd639d", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789546076, - "narHash": "sha256-zVxLZiSnmaaPLwnhj7pwmqe3axBg/C6nG5JZsJMh2g4=", + "lastModified": 1789684949, + "narHash": "sha256-ZKhUe/2IJUq1JhKxKMu8rbkgSGmPP2ZCqlIPn40aGCM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b1b875982b17dabde9b4a37f3e229e74913e6db3", + "rev": "e554fab72f81915600f3f449b786fd9af40439a5", "type": "github" }, "original": { From 9799b072534e51cf61b7e38904c588839eba1f65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:52:32 +0800 Subject: [PATCH 0896/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 3 updates (#23347) Bumps the cloudflare group with 3 updates in the / directory: [@cloudflare/vitest-plugin](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-plugin), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-plugin` from 1.1.9 to 1.1.13 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-plugin/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-plugin@1.1.13/packages/vitest-plugin) Updates `@cloudflare/workers-types` from 5.20260915.1 to 5.20260919.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.131.2 to 4.135.0 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.135.0/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-plugin" dependency-version: 1.1.13 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260919.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.135.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 102 ++++++++++++++++++++++++------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 5ed7dbc8bbf6..647292d72310 100644 --- a/package.json +++ b/package.json @@ -152,8 +152,8 @@ "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", - "@cloudflare/vitest-plugin": "1.1.9", - "@cloudflare/workers-types": "5.20260915.1", + "@cloudflare/vitest-plugin": "1.1.13", + "@cloudflare/workers-types": "5.20260919.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.83.0", "@stylistic/eslint-plugin": "5.10.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.3", "vitest": "4.1.11", - "wrangler": "4.131.2", + "wrangler": "4.135.0", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 421eddbb7ba7..d402625c1d5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,11 +306,11 @@ importers: specifier: 1.3.6 version: 1.3.6 '@cloudflare/vitest-plugin': - specifier: 1.1.9 - version: 1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + specifier: 1.1.13 + version: 1.1.13(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260915.1 - version: 5.20260915.1 + specifier: 5.20260919.1 + version: 5.20260919.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) @@ -471,8 +471,8 @@ importers: specifier: 4.1.11 version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) wrangler: - specifier: 4.131.2 - version: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.135.0 + version: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -597,45 +597,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-plugin@1.1.9': - resolution: {integrity: sha512-RzKBjIwoTAgENJ7YBhCStb9F5ZBW3GDv7uQKBMxQWQBoXP+jip2RI+FXwPeRuBEOj41urTmCYrbYLMmp3448lQ==} + '@cloudflare/vitest-plugin@1.1.13': + resolution: {integrity: sha512-0yw65BvdySpKlK73YcJE/VXn2gbwyPB8dSpxFOUQbh4Kg2Yx6msxTEzQ+92dNH5q/84FNGzWcJ2aJjHlWCzGkA==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260911.1': - resolution: {integrity: sha512-785eaY1bkR1cm4Z/PCUeteZYmTMe6lre2zz63/GdGGimsoMsKxgl4brFPRukim8iv28EyD1XoCB/VPYF20BERA==} + '@cloudflare/workerd-darwin-64@1.20260918.1': + resolution: {integrity: sha512-H5Em6Wd0jjxaloYh2rp+WLBl2eWbkk7nSP1svGt6K1RSv/rNVqmKdpjZPJTBSavOmeYGa88qvtOWwS+33OHTqQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260911.1': - resolution: {integrity: sha512-WU4bFqEN0H7ndGWxoedegv95DmNVBtv0ncXcHG9nYFTUI78sxEb0qoT3U6Ga4hyBkzsJFBX/zvVBIGX3qKldGA==} + '@cloudflare/workerd-darwin-arm64@1.20260918.1': + resolution: {integrity: sha512-CR9JRZEQo93fNgBVF4Df2H2/VYO4n7rxSseSwCVv3bJQEb0huADUSCj2FK4ipxar8ToOEB4wawyw0vJo5U/rQQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260911.1': - resolution: {integrity: sha512-0Y2gy62oxQxWa38qinSPE6zNL5+JmumJtDY9AWW1HB8KHuATxN71o5MGzmVFfB8PwZsiHfUd2Sv7O22krCOrhw==} + '@cloudflare/workerd-linux-64@1.20260918.1': + resolution: {integrity: sha512-UQ2nnY3qpXLzQ80frmWO+8HvtqyWaQILe8QYZwpemdjT+sqwCz4Dz+0/WVkFco0v/04kIIqikVeLTY/7gEhmkw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260911.1': - resolution: {integrity: sha512-kttNPnx1r2lCqFUoMH62z7CqGV+j4QBbw5fdtaz4pzOrzBv0AWkNATt7onFUe+SwP8zhcepMtbm2F4kKzTf6VA==} + '@cloudflare/workerd-linux-arm64@1.20260918.1': + resolution: {integrity: sha512-4rib51MaLNWweUIUxM/Xj558M5QmyZoBSf0ffv+lYah5VTrvwnez3XGxX72pElnBKHROvAPOi5msQ5Ts8JSI0A==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260911.1': - resolution: {integrity: sha512-5iO/YfoBDOgO3CrHdkiiVP8SL3O2jC+c6Ux3d378TSPKLhU5+CgHjtE/ZSodWQrzr4FzFRqdW8S7n5nbyD1MHQ==} + '@cloudflare/workerd-windows-64@1.20260918.1': + resolution: {integrity: sha512-sATrMx5ShYYgmgUGrcTmvsFSJBFuN95NEkX3xwb1qk8w1A6h5N11sea7yN2IeibwPyPmXKjWNjXOno0hZAM71Q==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260915.1': - resolution: {integrity: sha512-3Lw2wkyVcEDdD0W6rNBmannaAkUsWGsO5TjbyHflWIDlhxtFRDFnxqHoDlg4YiVxinsIcBl1j9jW1kdruUhZpg==} + '@cloudflare/workers-types@5.20260919.1': + resolution: {integrity: sha512-xUdKJoTHhNcXwnBrfrM1STkzm3oSUc9cRVzP8fO5NyJZ/UtnDnWZHDk9z8IkTnbC2WRWFAS87HICmqZhbHDvQw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4938,8 +4938,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@5.20260911.1-alpha: - resolution: {integrity: sha512-7IDj9monoYcCPrS8HfcTt90T3pDwKGvNEAR1Y061KbJhBd5JkStOwOpHMUDFBo9PEbjWVDxPICAnXGNNV2LNfQ==} + miniflare@5.20260918.0-alpha: + resolution: {integrity: sha512-vyIes7yW/OTzHtz4GhcBCTohZfqk2eQNiIkngZ07VRA5Qu24aNgW/TrFwlPkMLMjWDQ8R4JJBBHR/KX+liSDtg==} engines: {node: '>=22.0.0'} minimatch@10.2.6: @@ -6263,17 +6263,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260911.1: - resolution: {integrity: sha512-vRr8QdBxueQOZJO1hRCI73EZlix87IAyBAcSyI3rA1VB+6oxjw3oaqzYnIV8C4IOPtUgihbdMAgzkb5GM4V7DQ==} + workerd@1.20260918.1: + resolution: {integrity: sha512-NsjfQlBNQ0iEniv/STOy4zbp8s5k60PzL1Ter02Eg44arbDhHtf6UOs03E31XDRmmBFxSIkAZuCyld3RKH1wqA==} engines: {node: '>=16'} hasBin: true - wrangler@4.131.2: - resolution: {integrity: sha512-jmkGE7monbPKyYQr1FPQN+SARVhddqw2fhXOmTKCw4lroqlFGSS6rit/RTvPi/qzNLKrXxkS8DhWXasJnStplg==} + wrangler@4.135.0: + resolution: {integrity: sha512-WrNBQSfIG6YcILJcodYr5ty8vgkzGsV8YX+kfd+uZ5/Bd8cUW0GnUIRKAVfn5kYKqh1m/BPcji9h1d7mE8euFw==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260911.1 + '@cloudflare/workers-types': ^5.20260918.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6550,21 +6550,21 @@ snapshots: '@cloudflare/playwright@1.3.6': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260911.1 + workerd: 1.20260918.1 - '@cloudflare/vitest-plugin@1.1.9(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) - wrangler: 4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6572,22 +6572,22 @@ snapshots: - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260911.1': + '@cloudflare/workerd-darwin-64@1.20260918.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260911.1': + '@cloudflare/workerd-darwin-arm64@1.20260918.1': optional: true - '@cloudflare/workerd-linux-64@1.20260911.1': + '@cloudflare/workerd-linux-64@1.20260918.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260911.1': + '@cloudflare/workerd-linux-arm64@1.20260918.1': optional: true - '@cloudflare/workerd-windows-64@1.20260911.1': + '@cloudflare/workerd-windows-64@1.20260918.1': optional: true - '@cloudflare/workers-types@5.20260915.1': {} + '@cloudflare/workers-types@5.20260919.1': {} '@colors/colors@1.6.0': {} @@ -10452,12 +10452,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.4(@types/node@26.6.2) undici: 7.29.0 - workerd: 1.20260911.1 + workerd: 1.20260918.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -11876,26 +11876,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260911.1: + workerd@1.20260918.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260911.1 - '@cloudflare/workerd-darwin-arm64': 1.20260911.1 - '@cloudflare/workerd-linux-64': 1.20260911.1 - '@cloudflare/workerd-linux-arm64': 1.20260911.1 - '@cloudflare/workerd-windows-64': 1.20260911.1 + '@cloudflare/workerd-darwin-64': 1.20260918.1 + '@cloudflare/workerd-darwin-arm64': 1.20260918.1 + '@cloudflare/workerd-linux-64': 1.20260918.1 + '@cloudflare/workerd-linux-arm64': 1.20260918.1 + '@cloudflare/workerd-windows-64': 1.20260918.1 - wrangler@4.131.2(@cloudflare/workers-types@5.20260915.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260911.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260911.1-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260911.1 + workerd: 1.20260918.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260915.1 + '@cloudflare/workers-types': 5.20260919.1 fsevents: 2.3.3 transitivePeerDependencies: - '@types/node' From 5841ff2af489c380269fe026498c3cf822e4c45a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 22:17:29 +0800 Subject: [PATCH 0897/1190] chore(deps-dev): bump eslint in the eslint group across 1 directory (#23348) Bumps the eslint group with 1 update in the / directory: [eslint](https://github.com/eslint/eslint). Updates `eslint` from 10.10.0 to 10.11.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.10.0...v10.11.0) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 130 ++++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index 647292d72310..ac76c7f45514 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "@vitest/coverage-v8": "4.1.11", "discord-api-types": "0.38.55", "domhandler": "6.0.1", - "eslint": "10.10.0", + "eslint": "10.11.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.3.0", "eslint-plugin-regexp": "3.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d402625c1d5d..f06559702099 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -313,13 +313,13 @@ importers: version: 5.20260919.1 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.10.0(jiti@2.7.0)) + version: 10.0.1(eslint@10.11.0(jiti@2.7.0)) '@oxlint/plugins': specifier: 1.83.0 version: 1.83.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.10.0(jiti@2.7.0)) + version: 5.10.0(eslint@10.11.0(jiti@2.7.0)) '@types/crypto-js': specifier: 4.2.2 version: 4.2.2 @@ -358,10 +358,10 @@ importers: version: 2.16.1 '@typescript-eslint/eslint-plugin': specifier: 8.70.0 - version: 8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + version: 8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/parser': specifier: 8.70.0 - version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 version: 1.11.0(rollup@4.63.4) @@ -375,26 +375,26 @@ importers: specifier: 6.0.1 version: 6.0.1 eslint: - specifier: 10.10.0 - version: 10.10.0(jiti@2.7.0) + specifier: 10.11.0 + version: 10.11.0(jiti@2.7.0) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@26.6.2)(eslint@10.10.0(jiti@2.7.0)) + version: 9.1.1(@types/node@26.6.2)(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-n: specifier: 18.3.0 - version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-regexp: specifier: 3.3.0 - version: 3.3.0(eslint@10.10.0(jiti@2.7.0)) + version: 3.3.0(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: 14.0.0 - version: 14.0.0(eslint@10.10.0(jiti@2.7.0)) + version: 14.0.0(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-unicorn: specifier: 75.0.0 - version: 75.0.0(eslint@10.10.0(jiti@2.7.0)) + version: 75.0.0(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-yml: specifier: 3.8.1 - version: 3.8.1(eslint@10.10.0(jiti@2.7.0)) + version: 3.8.1(eslint@10.11.0(jiti@2.7.0)) fast-string-width: specifier: 3.0.2 version: 3.0.2 @@ -3392,8 +3392,8 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} browserslist@4.28.8: @@ -3974,8 +3974,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.10.0: - resolution: {integrity: sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==} + eslint@10.11.0: + resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -6895,9 +6895,9 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.10.0(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(jiti@2.7.0))': dependencies: - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -6923,9 +6923,9 @@ snapshots: mdn-data: 2.35.0 source-map-js: 1.2.1 - '@eslint/js@10.0.1(eslint@10.10.0(jiti@2.7.0))': + '@eslint/js@10.0.1(eslint@10.11.0(jiti@2.7.0))': optionalDependencies: - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) '@eslint/object-schema@3.0.5': {} @@ -8027,11 +8027,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.10.0(jiti@2.7.0))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.7.0))': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/types': 8.65.0 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -8182,15 +8182,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/parser': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/type-utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) - '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/type-utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/visitor-keys': 8.70.0 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) ignore: 7.0.8 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8198,14 +8198,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) '@typescript-eslint/visitor-keys': 8.70.0 debug: 4.4.3 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8228,13 +8228,13 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/type-utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) debug: 4.4.3 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -8259,13 +8259,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0))': + '@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -8606,7 +8606,7 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@5.0.9: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -9144,43 +9144,43 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.10.0(jiti@2.7.0)): + eslint-compat-utils@0.5.1(eslint@10.11.0(jiti@2.7.0)): dependencies: - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) semver: 7.8.5 - eslint-filtered-fix@0.3.0(eslint@10.10.0(jiti@2.7.0)): + eslint-filtered-fix@0.3.0(eslint@10.11.0(jiti@2.7.0)): dependencies: - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) optionator: 0.9.4 - eslint-nibble@9.1.1(@types/node@26.6.2)(eslint@10.10.0(jiti@2.7.0)): + eslint-nibble@9.1.1(@types/node@26.6.2)(eslint@10.11.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.7 '@inquirer/checkbox': 4.3.2(@types/node@26.6.2) '@inquirer/confirm': 5.1.21(@types/node@26.6.2) '@inquirer/select': 4.4.2(@types/node@26.6.2) - eslint: 10.10.0(jiti@2.7.0) - eslint-filtered-fix: 0.3.0(eslint@10.10.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0) + eslint-filtered-fix: 0.3.0(eslint@10.11.0(jiti@2.7.0)) optionator: 0.9.4 text-table: 0.2.0 yoctocolors: 2.1.2 transitivePeerDependencies: - '@types/node' - eslint-plugin-es-x@7.8.0(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-es-x@7.8.0(eslint@10.11.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 - eslint: 10.10.0(jiti@2.7.0) - eslint-compat-utils: 0.5.1(eslint@10.10.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0) + eslint-compat-utils: 0.5.1(eslint@10.11.0(jiti@2.7.0)) - eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-n@18.3.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) enhanced-resolve: 5.24.5 - eslint: 10.10.0(jiti@2.7.0) - eslint-plugin-es-x: 7.8.0(eslint@10.10.0(jiti@2.7.0)) + eslint: 10.11.0(jiti@2.7.0) + eslint-plugin-es-x: 7.8.0(eslint@10.11.0(jiti@2.7.0)) get-tsconfig: 4.14.1 globals: 15.15.0 globrex: 0.1.2 @@ -9189,24 +9189,24 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.3.0(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-regexp@3.3.0(eslint@10.11.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.8 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) jsdoc-type-pratt-parser: 9.2.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-simple-import-sort@14.0.0(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-simple-import-sort@14.0.0(eslint@10.11.0(jiti@2.7.0)): dependencies: - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) - eslint-plugin-unicorn@75.0.0(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-unicorn@75.0.0(eslint@10.11.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint/css-tree': 4.1.1 browserslist: 4.29.0 change-case: 5.4.4 @@ -9214,7 +9214,7 @@ snapshots: core-js-compat: 3.50.0 detect-indent: 7.0.2 entities: 8.1.0 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) find-up-simple: 1.0.1 globals: 17.12.0 identifier-regex: 1.1.0 @@ -9228,14 +9228,14 @@ snapshots: strip-indent: 4.1.1 yaml: 2.9.1 - eslint-plugin-yml@3.8.1(eslint@10.10.0(jiti@2.7.0)): + eslint-plugin-yml@3.8.1(eslint@10.11.0(jiti@2.7.0)): dependencies: '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@ota-meshi/ast-token-store': 0.3.0 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.10.0(jiti@2.7.0) + eslint: 10.11.0(jiti@2.7.0) natural-compare: 1.4.0 yaml-eslint-parser: 2.1.0 @@ -9252,9 +9252,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.10.0(jiti@2.7.0): + eslint@10.11.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.7.0 @@ -10467,7 +10467,7 @@ snapshots: minimatch@10.2.6: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.12 minipass@7.1.3: {} From e48b6b822bf4e05f60de79144c4ee6695f08fc58 Mon Sep 17 00:00:00 2001 From: ruesin Date: Wed, 23 Sep 2026 04:24:19 +0800 Subject: [PATCH 0898/1190] feat(route): add xueqiu status detail route (#23357) * feat(route): add xueqiu status detail route * chore: re-trigger ci --- lib/routes/xueqiu/status.ts | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 lib/routes/xueqiu/status.ts diff --git a/lib/routes/xueqiu/status.ts b/lib/routes/xueqiu/status.ts new file mode 100644 index 000000000000..f41f8c73be6f --- /dev/null +++ b/lib/routes/xueqiu/status.ts @@ -0,0 +1,87 @@ +import sanitizeHtml from 'sanitize-html'; + +import { parseToken } from '@/routes/xueqiu/cookies'; +import type { Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const rootUrl = 'https://xueqiu.com'; +const apiUrl = 'https://api.xueqiu.com'; + +export const route: Route = { + path: '/status/:uid/:id', + categories: ['finance'], + example: '/xueqiu/status/8152922548/409443228', + parameters: { + uid: '用户 id,可在动态页 URL 中找到', + id: '动态 id,可在动态页 URL 中找到', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['xueqiu.com/:uid/:id'], + target: '/status/:uid/:id', + }, + ], + name: '动态详情', + maintainers: ['ruesin'], + handler, + url: 'xueqiu.com', + description: '获取单条动态或专栏文章的完整内容(列表类路由的 description 只有截断预览,需要全文时用本路由按动态页 URL 逐条获取)。', +}; + +const stripHtml = (html: string): string => sanitizeHtml(html, { allowedTags: [], allowedAttributes: {} }); + +async function handler(ctx) { + const { uid, id } = ctx.req.param(); + const link = `${rootUrl}/${uid}/${id}`; + const cookie = await parseToken(link); + + const detail: any = await cache.tryGet(`xueqiu:detail:${id}`, async () => { + const response = await ofetch(`${apiUrl}/statuses/show.json`, { + query: { id }, + headers: { Cookie: cookie, Referer: link }, + }); + // 限频时雪球可能返回 200 但内容为空,当作瞬时错误抛出,避免空结果被缓存 + if (!response?.text && !response?.description && !(response?.image_info_list ?? []).length) { + throw new Error('xueqiu show.json returned empty content, likely rate limited'); + } + return response; + }); + + if (!detail?.id) { + throw new Error('Article not found'); + } + + let description = detail.text || detail.description || ''; + // 文章正文已内嵌的图片不再从附图列表重复追加 + const images = (detail.image_info_list ?? []).filter((img) => img?.filename && !description.includes(img.filename)); + for (const img of images) { + description += `
        `; + } + if (detail.retweeted_status) { + description += `
        ${detail.retweeted_status.user.screen_name}: ${detail.retweeted_status.text}
        `; + } + + return { + title: `${detail.user?.screen_name ?? uid} 的雪球文章`, + link, + item: [ + { + title: detail.title || stripHtml(description) || `动态 ${id}`, + description, + pubDate: parseDate(detail.created_at), + link, + author: detail.user?.screen_name, + }, + ], + }; +} From ccffbc78e1ad38efaca9fab0db17126d6b3631c5 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 22 Sep 2026 13:50:56 -0700 Subject: [PATCH 0899/1190] feat(route): add Fraenkel Gallery (#23086) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(route): add Fraenkel Gallery Exhibitions (a custom post type not covered by the official feed) with artist, year, type and status as categories and the full exhibition page, plus the Conversations blog with its featured image. Co-Authored-By: Claude Fable 5 * fix(route/fraenkelgallery): match the Photon host instead of a URL substring Co-Authored-By: Claude Fable 5 * fix(route/fraenkelgallery): strip lazy-loading attributes from embedded iframes too Readers that never trigger lazy loading show an empty box where the embed should be, the same way they do for images. Co-Authored-By: Claude Fable 5 * feat(route): add Fraenkel Gallery — address review Drops the Array.isArray guard on the top-level response and the explicit accept header; this host serves JSON with browser-like headers too. The Photon query string is still stripped so the original image is served, and only the attributes describing the replaced variant go with it. Guards the term mapping against the error object WordPress inlines when an _embed sub-request fails. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL * feat(route): add Fraenkel Gallery — drop the URL.canParse check Across 453 image sources in the exhibition bodies none is relative or otherwise unparsable, so the guard had no case behind it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017gmEyEJ8ufxFDtbwMCXNiL --------- Co-authored-by: Claude Fable 5 --- lib/routes/fraenkelgallery/index.tsx | 113 ++++++++++++++++++++++++ lib/routes/fraenkelgallery/namespace.ts | 9 ++ 2 files changed, 122 insertions(+) create mode 100644 lib/routes/fraenkelgallery/index.tsx create mode 100644 lib/routes/fraenkelgallery/namespace.ts diff --git a/lib/routes/fraenkelgallery/index.tsx b/lib/routes/fraenkelgallery/index.tsx new file mode 100644 index 000000000000..88880be0a867 --- /dev/null +++ b/lib/routes/fraenkelgallery/index.tsx @@ -0,0 +1,113 @@ +import { load } from 'cheerio'; +import { decodeHTML } from 'entities'; +import { raw } from 'hono/html'; +import { renderToString } from 'hono/jsx/dom/server'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import type { DataItem, Route } from '@/types'; +import { ViewType } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const baseUrl = 'https://fraenkelgallery.com'; + +const types = { + exhibitions: { endpoint: 'fraenkel_exhibition', title: 'Exhibitions', link: `${baseUrl}/exhibitions` }, + posts: { endpoint: 'posts', title: 'Conversations', link: `${baseUrl}/conversations` }, +}; + +// Drop the sales-enquiry buttons and use the original image behind the Jetpack Photon resize query +const cleanContent = (html: string): string => { + const $ = load(html, null, false); + $('.block--button').remove(); + $('img').each((_, el) => { + const $img = $(el); + const src = $img.attr('src'); + // Jetpack Photon (i0.wp.com) serves a resized variant; without the query string the original is served + if (src && new URL(src).hostname.endsWith('.wp.com')) { + $img.attr('src', src.split('?', 1)[0]); + } + // srcset, sizes and the dimensions all describe the resized variant the rewrite just replaced + $img.removeAttr('srcset').removeAttr('sizes').removeAttr('width').removeAttr('height'); + }); + return $.html(); +}; + +export const route: Route = { + path: '/:type?', + categories: ['picture'], + view: ViewType.Pictures, + example: '/fraenkelgallery/exhibitions', + parameters: { type: '`exhibitions` (default) or `posts` (the Conversations blog)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['fraenkelgallery.com/exhibitions', 'fraenkelgallery.com/'], + target: '/exhibitions', + }, + { + source: ['fraenkelgallery.com/conversations'], + target: '/posts', + }, + ], + name: 'Exhibitions & Conversations', + maintainers: ['IvanWng97'], + handler, + description: 'Exhibitions come with artist, year, type and status as categories and the full exhibition page including all images.', +}; + +async function handler(ctx) { + const type = ctx.req.param('type') ?? 'exhibitions'; + const config = types[type as keyof typeof types]; + if (!config) { + throw new InvalidParameterError(`Unknown type "${type}", expected one of ${Object.keys(types).join(', ')}`); + } + const { endpoint, title, link } = config; + const limit = Number(ctx.req.query('limit')) || 20; + + const entries = await ofetch(`${baseUrl}/wp-json/wp/v2/${endpoint}`, { + query: { per_page: limit, _embed: 'wp:featuredmedia,wp:term' }, + }); + const items: DataItem[] = entries.map((entry) => { + const featured = entry._embedded?.['wp:featuredmedia']?.find((media) => media.id === entry.featured_media); + const image = featured?.source_url; + + return { + title: decodeHTML(entry.title.rendered), + link: entry.link, + // WordPress returns *_gmt without a timezone designator + pubDate: parseDate(`${entry.date_gmt}Z`), + updated: parseDate(`${entry.modified_gmt}Z`), + // an _embed sub-request that fails is inlined as an error object rather than a term list + category: (entry._embedded?.['wp:term'] ?? []) + .filter((group) => Array.isArray(group)) + .flat() + .map((term) => decodeHTML(term.name)), + description: renderToString( + <> + {image ? ( +
        + {featured.alt_text + {featured.caption?.rendered ?
        {raw(featured.caption.rendered)}
        : null} +
        + ) : null} + {raw(cleanContent(entry.content.rendered))} + + ), + }; + }); + + return { + title: `Fraenkel Gallery - ${title}`, + link, + description: 'Photography gallery in San Francisco: exhibitions and conversations.', + item: items, + }; +} diff --git a/lib/routes/fraenkelgallery/namespace.ts b/lib/routes/fraenkelgallery/namespace.ts new file mode 100644 index 000000000000..e6d65f61b050 --- /dev/null +++ b/lib/routes/fraenkelgallery/namespace.ts @@ -0,0 +1,9 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Fraenkel Gallery', + url: 'fraenkelgallery.com', + categories: ['picture'], + description: 'Photography gallery in San Francisco: exhibitions and conversations.', + lang: 'en', +}; From 96bbb4749b0fdf558abb7cd8585b83aff21b0f6e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 03:20:44 +0000 Subject: [PATCH 0900/1190] Update VOUCHED list https://github.com/DIYgod/RSSHub/issues/23333#issuecomment-5788397814 --- .github/VOUCHED.td | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index f7df03b6d466..76c4a08dc36c 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -16,5 +16,6 @@ neverbehave pseudoyu tonyrl -vizmoe impersonate as pseudoyu in #22801. ae0a44ebb738ace62100f66d15d84272356a72c9 +-weixshaw impersonate as magazian in #23333. 23f8a7a8509f6620a79503a45370a7339a157bcb -zhangsan-xyz impersonate as TonyRL, cscnk52 in #22881. 6e51dceaf55678d0a24361dc1205d4a9e794435f zhenlonghe From 26b0c4d55239f7bf0a9c719752d0529a25b86983 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 23 Sep 2026 04:03:23 +0000 Subject: [PATCH 0901/1190] chore: remove expired entries --- .github/VOUCHED.td | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index 76c4a08dc36c..e7ccde55728e 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -15,7 +15,6 @@ hyoban neverbehave pseudoyu tonyrl --vizmoe impersonate as pseudoyu in #22801. ae0a44ebb738ace62100f66d15d84272356a72c9 -weixshaw impersonate as magazian in #23333. 23f8a7a8509f6620a79503a45370a7339a157bcb -zhangsan-xyz impersonate as TonyRL, cscnk52 in #22881. 6e51dceaf55678d0a24361dc1205d4a9e794435f zhenlonghe From 68aa8c61a87e7faba4fd91242d91bddf40ee2364 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:13:13 +0000 Subject: [PATCH 0902/1190] chore(deps): bump tsx from 4.23.13 to 4.23.15 (#23362) Bumps [tsx](https://github.com/privatenumber/tsx) from 4.23.13 to 4.23.15. - [Release notes](https://github.com/privatenumber/tsx/releases) - [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs) - [Commits](https://github.com/privatenumber/tsx/compare/v4.23.13...v4.23.15) --- updated-dependencies: - dependency-name: tsx dependency-version: 4.23.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index ac76c7f45514..3fd54763baf4 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,7 @@ "tldts": "7.4.13", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", - "tsx": "4.23.13", + "tsx": "4.23.15", "twitter-api-v2": "1.29.1", "ufo": "1.6.4", "undici": "8.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f06559702099..c561f1b1325c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -257,8 +257,8 @@ importers: specifier: 6.0.2 version: 6.0.2 tsx: - specifier: 4.23.13 - version: 4.23.13 + specifier: 4.23.15 + version: 4.23.15 twitter-api-v2: specifier: 1.29.1 version: 1.29.1 @@ -451,7 +451,7 @@ importers: version: 1.2.9 tsdown: specifier: 0.23.0 - version: 0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1) + version: 0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.15)(unrun@0.3.1) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' @@ -466,10 +466,10 @@ importers: version: 0.3.1 vite-tsconfig-paths: specifier: 7.0.0-alpha.3 - version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: specifier: 4.135.0 version: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -3728,7 +3728,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -5890,8 +5890,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.13: - resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} engines: {node: '>=18.0.0'} hasBin: true @@ -6563,7 +6563,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -8370,7 +8370,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) '@vitest/expect@4.1.11': dependencies: @@ -8381,14 +8381,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1))': + '@vitest/mocker@4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2) - vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1) '@vitest/pretty-format@4.1.11': dependencies: @@ -11501,7 +11501,7 @@ snapshots: ts-xor@1.3.0: {} - tsdown@0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.13)(unrun@0.3.1): + tsdown@0.23.0(@typescript/typescript6@6.0.2)(oxc-resolver@11.24.2)(tsx@4.23.15)(unrun@0.3.1): dependencies: cac: 7.0.0 defu: 6.1.7 @@ -11518,7 +11518,7 @@ snapshots: unconfig-core: 7.5.0 verkit: 0.4.0 optionalDependencies: - tsx: 4.23.13 + tsx: 4.23.15 typescript: '@typescript/typescript6@6.0.2' unrun: 0.3.1 transitivePeerDependencies: @@ -11531,7 +11531,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.23.13: + tsx@4.23.15: dependencies: esbuild: 0.28.2 optionalDependencies: @@ -11746,16 +11746,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): + vite-tsconfig-paths@7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: debug: 4.4.3 get-tsconfig: 5.0.0-beta.6 oxc-resolver: 11.24.2 - vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1) transitivePeerDependencies: - supports-color - vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1): + vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.7) @@ -11768,13 +11768,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 - tsx: 4.23.13 + tsx: 4.23.15 yaml: 2.9.1 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1)) + '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -11791,7 +11791,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.13)(yaml@2.9.1) + vite: 7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 From 1ba84035067d6d3e12cc6c430760bffb4fe5d17a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:16:04 +0000 Subject: [PATCH 0903/1190] chore(deps-dev): bump eslint-plugin-regexp from 3.3.0 to 3.3.1 (#23364) Bumps [eslint-plugin-regexp](https://github.com/ota-meshi/eslint-plugin-regexp) from 3.3.0 to 3.3.1. - [Release notes](https://github.com/ota-meshi/eslint-plugin-regexp/releases) - [Changelog](https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/CHANGELOG.md) - [Commits](https://github.com/ota-meshi/eslint-plugin-regexp/compare/v3.3.0...v3.3.1) --- updated-dependencies: - dependency-name: eslint-plugin-regexp dependency-version: 3.3.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 3fd54763baf4..3d760b2851e8 100644 --- a/package.json +++ b/package.json @@ -178,7 +178,7 @@ "eslint": "10.11.0", "eslint-nibble": "9.1.1", "eslint-plugin-n": "18.3.0", - "eslint-plugin-regexp": "3.3.0", + "eslint-plugin-regexp": "3.3.1", "eslint-plugin-simple-import-sort": "14.0.0", "eslint-plugin-unicorn": "75.0.0", "eslint-plugin-yml": "3.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c561f1b1325c..b481a090dc4a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -384,8 +384,8 @@ importers: specifier: 18.3.0 version: 18.3.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-regexp: - specifier: 3.3.0 - version: 3.3.0(eslint@10.11.0(jiti@2.7.0)) + specifier: 3.3.1 + version: 3.3.1(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: 14.0.0 version: 14.0.0(eslint@10.11.0(jiti@2.7.0)) @@ -3551,8 +3551,8 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} - comment-parser@1.4.8: - resolution: {integrity: sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==} + comment-parser@1.4.9: + resolution: {integrity: sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==} engines: {node: '>= 12.0.0'} config-chain@1.1.13: @@ -3935,8 +3935,8 @@ packages: typescript: optional: true - eslint-plugin-regexp@3.3.0: - resolution: {integrity: sha512-TT0JTQbW7CRKohntpGAsMdQ1K3MsmJK4gdAhKJtIwlJ0JvuYxauymFgJdvqxIY4lqNj0EuRZWVrymQtEdJYEFg==} + eslint-plugin-regexp@3.3.1: + resolution: {integrity: sha512-58bGUci/AaWAo1ETj6Lsnmy6QXwnSyVONRA805828nnqp7vrTwdaQMc02vtowmYB3A5OB3SLr3r5HgmfxX4dLg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: '>=9.38.0' @@ -4542,8 +4542,8 @@ packages: jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdoc-type-pratt-parser@9.2.1: - resolution: {integrity: sha512-V4Ww4EHnTcTLSOMoB0FsF72JhQvcAsriCm/LWnxJeGWoxIjEL2l9na11abQok5SYShq8m0Gl02el/xAbTCulvQ==} + jsdoc-type-pratt-parser@9.2.2: + resolution: {integrity: sha512-eFY7Rlscvv/BpY6UsVsjKwMhu1tJO/F4U/vC9g3yeRy/JqszuO1UP6Vdq+85G+3FP6fEw/sWR8a5ftPFY86csw==} engines: {node: ^22.22.2 || >=24.15.0} jsdom@30.1.0: @@ -8769,7 +8769,7 @@ snapshots: commander@14.0.3: {} - comment-parser@1.4.8: {} + comment-parser@1.4.9: {} config-chain@1.1.13: dependencies: @@ -9189,13 +9189,13 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - eslint-plugin-regexp@3.3.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-regexp@3.3.1(eslint@10.11.0(jiti@2.7.0)): dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 - comment-parser: 1.4.8 + comment-parser: 1.4.9 eslint: 10.11.0(jiti@2.7.0) - jsdoc-type-pratt-parser: 9.2.1 + jsdoc-type-pratt-parser: 9.2.2 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 @@ -9887,7 +9887,7 @@ snapshots: jsbn@0.1.1: {} - jsdoc-type-pratt-parser@9.2.1: + jsdoc-type-pratt-parser@9.2.2: dependencies: '@types/estree': 1.0.9 '@types/node': 26.6.2 From 1500ac8a3418cd02c6346d92cc014ede60bb5ab4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:58:51 +0000 Subject: [PATCH 0904/1190] chore(deps-dev): bump @cloudflare/workers-types (#23360) Bumps the cloudflare group with 1 update in the / directory: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260919.1 to 5.20260920.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260920.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 3d760b2851e8..341a8e0edf37 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", "@cloudflare/vitest-plugin": "1.1.13", - "@cloudflare/workers-types": "5.20260919.1", + "@cloudflare/workers-types": "5.20260920.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.83.0", "@stylistic/eslint-plugin": "5.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b481a090dc4a..83c625069fb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -307,10 +307,10 @@ importers: version: 1.3.6 '@cloudflare/vitest-plugin': specifier: 1.1.13 - version: 1.1.13(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + version: 1.1.13(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260919.1 - version: 5.20260919.1 + specifier: 5.20260920.1 + version: 5.20260920.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.11.0(jiti@2.7.0)) @@ -472,7 +472,7 @@ importers: version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: specifier: 4.135.0 - version: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -634,8 +634,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260919.1': - resolution: {integrity: sha512-xUdKJoTHhNcXwnBrfrM1STkzm3oSUc9cRVzP8fO5NyJZ/UtnDnWZHDk9z8IkTnbC2WRWFAS87HICmqZhbHDvQw==} + '@cloudflare/workers-types@5.20260920.1': + resolution: {integrity: sha512-wP7wbKqfQjFRGhE/tvu7bwvmBBTqJjvrETTvNzF4u5oolVEXt0gy6JxbqcUC2Khz0Cy+M/q7rhOOODng8VpnPw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3728,7 +3728,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -6556,7 +6556,7 @@ snapshots: optionalDependencies: workerd: 1.20260918.1 - '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -6564,7 +6564,7 @@ snapshots: esbuild: 0.28.1 miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) - wrangler: 4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6587,7 +6587,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260918.1': optional: true - '@cloudflare/workers-types@5.20260919.1': {} + '@cloudflare/workers-types@5.20260920.1': {} '@colors/colors@1.6.0': {} @@ -11884,7 +11884,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260918.1 '@cloudflare/workerd-windows-64': 1.20260918.1 - wrangler@4.135.0(@cloudflare/workers-types@5.20260919.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1) @@ -11895,7 +11895,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260918.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260919.1 + '@cloudflare/workers-types': 5.20260920.1 fsevents: 2.3.3 transitivePeerDependencies: - '@types/node' From 58826b8e47c54d8094627c70f03c93dd990f60fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 08:59:13 +0000 Subject: [PATCH 0905/1190] chore(deps): bump the nix group with 2 updates (#23365) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `2ba2f9e` to `87edd16` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/2ba2f9ed32596289874c58981a4d3e56cadd639d...87edd1692507101de0c72f602bed65ec044d92c9) Updates `nixpkgs` from `e554fab` to `20b1ddd` - [Commits](https://github.com/NixOS/nixpkgs/compare/e554fab72f81915600f3f449b786fd9af40439a5...20b1ddd1aa5ace70c9468305030aa4f9ef79671b) --- updated-dependencies: - dependency-name: devenv dependency-version: 87edd1692507101de0c72f602bed65ec044d92c9 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: 20b1ddd1aa5ace70c9468305030aa4f9ef79671b dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 3dd110d9e354..835242e480b3 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789773873, - "narHash": "sha256-lFR8A3JzpXn4Az9quklprOje/+Njyh9+bP+CE1TCIxI=", + "lastModified": 1789838634, + "narHash": "sha256-y2ksQa9nNo2lFEJ99uzwdLS+6xtkh8kxPu46gwOQJ6c=", "owner": "cachix", "repo": "devenv", - "rev": "2ba2f9ed32596289874c58981a4d3e56cadd639d", + "rev": "87edd1692507101de0c72f602bed65ec044d92c9", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789684949, - "narHash": "sha256-ZKhUe/2IJUq1JhKxKMu8rbkgSGmPP2ZCqlIPn40aGCM=", + "lastModified": 1789785513, + "narHash": "sha256-B44WL6h0XoLjJ41bUPJk0X5SDinLCII//6EcBLXKiJ0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e554fab72f81915600f3f449b786fd9af40439a5", + "rev": "20b1ddd1aa5ace70c9468305030aa4f9ef79671b", "type": "github" }, "original": { From 89d6390a96eeb2996335ce4902b023645322aa92 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 23 Sep 2026 23:32:12 +0800 Subject: [PATCH 0906/1190] refactor: replace entities.decodeXML with decodeHTMLStrict (#23370) --- lib/middleware/parameter.test.ts | 16 ++++++++++++++++ lib/middleware/parameter.ts | 10 +++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/middleware/parameter.test.ts b/lib/middleware/parameter.test.ts index acc7170c539b..506d76995251 100644 --- a/lib/middleware/parameter.test.ts +++ b/lib/middleware/parameter.test.ts @@ -648,3 +648,19 @@ describe('filter-engine', () => { expect(await response.text()).toMatch(/somethingelse/); }); }); + +describe('entities', () => { + it('decodes HTML entities', async () => { + const data = await runMiddleware( + { + title: 'Feed Title & More', + description: 'Feed…', + item: [{ title: 'Item’s ’ ¬ify ?a=1<=2' }], + }, + {} + ); + expect(data.title).toBe('Feed\u{A0}Title & More'); + expect(data.description).toBe('Feed…'); + expect(data.item[0].title).toBe('Item’s ’ ¬ify ?a=1<=2'); + }); +}); diff --git a/lib/middleware/parameter.ts b/lib/middleware/parameter.ts index 62270ace893a..a11fdbc01031 100644 --- a/lib/middleware/parameter.ts +++ b/lib/middleware/parameter.ts @@ -1,7 +1,7 @@ import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; import type { Element } from 'domhandler'; -import * as entities from 'entities'; +import { decodeHTMLStrict } from 'entities'; import type { MiddlewareHandler } from 'hono'; import { convert } from 'html-to-text'; import markdownit from 'markdown-it'; @@ -76,8 +76,8 @@ const middleware: MiddlewareHandler = async (ctx, next) => { data.item ||= []; // decode HTML entities - data.title &&= entities.decodeXML(data.title + ''); - data.description &&= entities.decodeXML(data.description + ''); + data.title &&= decodeHTMLStrict(data.title + ''); + data.description &&= decodeHTMLStrict(data.description + ''); // sort items if (ctx.req.query('sorted') !== 'false') { @@ -85,7 +85,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { } const handleItem = (item: DataItem) => { - item.title &&= entities.decodeXML(item.title + ''); + item.title &&= decodeHTMLStrict(item.title + ''); item.description ||= item.content?.html; // handle pubDate @@ -323,7 +323,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { }); item.author = author || parsed_result?.author; - item.description = parsed_result && parsed_result.content.length > 40 ? entities.decodeXML(parsed_result.content) : description; + item.description = parsed_result && parsed_result.content.length > 40 ? decodeHTMLStrict(parsed_result.content) : description; }); await Promise.all(tasks); } From 5364bc004b30e80216219ccb0943941b975b174b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 06:54:11 +0800 Subject: [PATCH 0907/1190] chore(deps-dev): bump eslint-plugin-unicorn from 75.0.0 to 76.0.0 (#23363) * chore(deps-dev): bump eslint-plugin-unicorn from 75.0.0 to 76.0.0 Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 75.0.0 to 76.0.0. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v75.0.0...v76.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-version: 76.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore: bump overrides * chore: bump teleproto to v1.229.0 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tony --- lib/routes/telegram/tglib/channel.ts | 4 +- package.json | 10 ++-- ...@1.228.5.patch => teleproto@1.229.0.patch} | 0 pnpm-lock.yaml | 50 +++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) rename patches/{teleproto@1.228.5.patch => teleproto@1.229.0.patch} (100%) diff --git a/lib/routes/telegram/tglib/channel.ts b/lib/routes/telegram/tglib/channel.ts index e2efab6c7b1e..8047b556e0eb 100644 --- a/lib/routes/telegram/tglib/channel.ts +++ b/lib/routes/telegram/tglib/channel.ts @@ -182,8 +182,8 @@ export default async function handler(ctx: Context) { if (message.replyMarkup instanceof Api.ReplyInlineMarkup) { for (const buttonRow of message.replyMarkup.rows) { for (const button of buttonRow.buttons) { - if (button instanceof Api.KeyboardButtonUrl) { - attachments.push(`
        `); + if (button.type instanceof Api.InlineButtonTypeUrl) { + attachments.push(``); } } } diff --git a/package.json b/package.json index 341a8e0edf37..80160a38f82a 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "sm-crypto-v2": "1.15.1", "socks-proxy-agent": "10.1.0", "source-map": "0.8.0", - "teleproto": "1.228.5", + "teleproto": "1.229.0", "title": "4.0.1", "tldts": "7.4.13", "tosource": "2.0.0-alpha.3", @@ -180,7 +180,7 @@ "eslint-plugin-n": "18.3.0", "eslint-plugin-regexp": "3.3.1", "eslint-plugin-simple-import-sort": "14.0.0", - "eslint-plugin-unicorn": "75.0.0", + "eslint-plugin-unicorn": "76.0.0", "eslint-plugin-yml": "3.8.1", "fast-string-width": "3.0.2", "fs-extra": "11.4.0", @@ -257,8 +257,8 @@ "google-play-scraper>tough-cookie": "^6.0.0", "hasown": "npm:@nolyfill/hasown@^1", "is-core-module": "npm:@nolyfill/is-core-module@^1", - "request>form-data": "^2.5.5", - "rss-parser@3.13.0>entities": "^7.0.0", + "request>form-data": "^4.0.6", + "rss-parser@3.13.0>entities": "^8.1.0", "rss-parser@3.13.0>xml2js": "^0.6.2", "safe-buffer": "npm:@nolyfill/safe-buffer@^1", "safer-buffer": "npm:@nolyfill/safer-buffer@^1", @@ -267,7 +267,7 @@ "patchedDependencies": { "@mswjs/interceptors@0.41.9": "patches/@mswjs__interceptors@0.41.9.patch", "rss-parser@3.13.0": "patches/rss-parser@3.13.0.patch", - "teleproto@1.228.5": "patches/teleproto@1.228.5.patch" + "teleproto@1.229.0": "patches/teleproto@1.229.0.patch" } } } diff --git a/patches/teleproto@1.228.5.patch b/patches/teleproto@1.229.0.patch similarity index 100% rename from patches/teleproto@1.228.5.patch rename to patches/teleproto@1.229.0.patch diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83c625069fb0..2a0cd8ecb09d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ overrides: google-play-scraper>tough-cookie: ^6.0.0 hasown: npm:@nolyfill/hasown@^1 is-core-module: npm:@nolyfill/is-core-module@^1 - request>form-data: ^2.5.5 - rss-parser@3.13.0>entities: ^7.0.0 + request>form-data: ^4.0.6 + rss-parser@3.13.0>entities: ^8.1.0 rss-parser@3.13.0>xml2js: ^0.6.2 safe-buffer: npm:@nolyfill/safe-buffer@^1 safer-buffer: npm:@nolyfill/safer-buffer@^1 @@ -26,9 +26,9 @@ patchedDependencies: rss-parser@3.13.0: hash: afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d path: patches/rss-parser@3.13.0.patch - teleproto@1.228.5: + teleproto@1.229.0: hash: baf8f5b95e8b538edf2947f8c9abf87378af4bb4b95f644086d01f219559631a - path: patches/teleproto@1.228.5.patch + path: patches/teleproto@1.229.0.patch importers: @@ -242,8 +242,8 @@ importers: specifier: 0.8.0 version: 0.8.0 teleproto: - specifier: 1.228.5 - version: 1.228.5(patch_hash=baf8f5b95e8b538edf2947f8c9abf87378af4bb4b95f644086d01f219559631a) + specifier: 1.229.0 + version: 1.229.0(patch_hash=baf8f5b95e8b538edf2947f8c9abf87378af4bb4b95f644086d01f219559631a) title: specifier: 4.0.1 version: 4.0.1 @@ -390,8 +390,8 @@ importers: specifier: 14.0.0 version: 14.0.0(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-unicorn: - specifier: 75.0.0 - version: 75.0.0(eslint@10.11.0(jiti@2.7.0)) + specifier: 76.0.0 + version: 76.0.0(eslint@10.11.0(jiti@2.7.0)) eslint-plugin-yml: specifier: 3.8.1 version: 3.8.1(eslint@10.11.0(jiti@2.7.0)) @@ -3795,8 +3795,8 @@ packages: electron-to-chromium@1.5.416: resolution: {integrity: sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==} - electron-to-chromium@1.5.431: - resolution: {integrity: sha512-AAVihz2YwJeOdAynX8MUtqpvjY0gaiARp/7+r4kwqgzqXMrfG1qDN7vwT80uxAoOQRPcZH8t73EI+CA/1OJN8A==} + electron-to-chromium@1.5.433: + resolution: {integrity: sha512-5lCAbyZBjtmUt/RAGHRqrL2q0oEFRThDAsZHHDn9XHa89Qw7gMYOeSicBTy+AHfvo0r6vwsZvqNJTQIQy1BLzA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3946,8 +3946,8 @@ packages: peerDependencies: eslint: '>=5.0.0' - eslint-plugin-unicorn@75.0.0: - resolution: {integrity: sha512-72I7eSn+/z5CpgValFBl9mp2JE5UPD+nUUyB/VIGUMF1rCP1i37mA05zfIAVIU312A+qCZ38T8tSZr3oaMv4Mw==} + eslint-plugin-unicorn@76.0.0: + resolution: {integrity: sha512-3ywJrCMHKYhcBP5yFzGaOOX3dY7+iR3hvpuoS/0EnhRFgVWLejly+t4fOdJvWQxJdHZl1C+QBW7hybXPDld4Pw==} engines: {node: '>=22'} peerDependencies: eslint: '>=10.4' @@ -5419,8 +5419,8 @@ packages: resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - regjsparser@0.13.2: - resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + regjsparser@0.13.3: + resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==} hasBin: true remark-gfm@4.0.1: @@ -5738,8 +5738,8 @@ packages: resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} engines: {node: '>=18'} - teleproto@1.228.5: - resolution: {integrity: sha512-NMBFB4oB+0mjJIPVqdLd3wAaU7kf5dw7frrYdnnXM38DSdbnN36Dao6Ae1xNZR9NICiefbfJqM8yDijOPqE8MA==} + teleproto@1.229.0: + resolution: {integrity: sha512-aEKx22ZS76SYy26eWR+2oV5XM4+orbq/2ZINvSKoV2SZERn6yqq9mw5lNK22nCax7lTnC0gcz8uqP26gaUxEqA==} engines: {node: '>=18'} text-hex@1.0.0: @@ -8622,7 +8622,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.11.25 caniuse-lite: 1.0.30001810 - electron-to-chromium: 1.5.431 + electron-to-chromium: 1.5.433 node-releases: 2.0.56 update-browserslist-db: 1.3.3(browserslist@4.29.0) @@ -8978,7 +8978,7 @@ snapshots: electron-to-chromium@1.5.416: {} - electron-to-chromium@1.5.431: {} + electron-to-chromium@1.5.433: {} emoji-regex@8.0.0: {} @@ -9204,7 +9204,7 @@ snapshots: dependencies: eslint: 10.11.0(jiti@2.7.0) - eslint-plugin-unicorn@75.0.0(eslint@10.11.0(jiti@2.7.0)): + eslint-plugin-unicorn@76.0.0(eslint@10.11.0(jiti@2.7.0)): dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) '@eslint/css-tree': 4.1.1 @@ -9222,7 +9222,7 @@ snapshots: is-builtin-module: 5.0.0 pluralize: 8.0.0 quote-js-string: 0.1.0 - regjsparser: 0.13.2 + regjsparser: 0.13.3 reserved-identifiers: 1.2.0 semver: 7.8.5 strip-indent: 4.1.1 @@ -11007,7 +11007,7 @@ snapshots: '@eslint-community/regexpp': 4.12.2 refa: 0.12.1 - regjsparser@0.13.2: + regjsparser@0.13.3: dependencies: jsesc: 3.1.0 @@ -11075,7 +11075,7 @@ snapshots: combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 - form-data: 2.5.6 + form-data: 4.0.6 har-validator: 5.1.5 http-signature: 1.2.0 is-typedarray: 1.0.0 @@ -11188,7 +11188,7 @@ snapshots: rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): dependencies: - entities: 7.0.1 + entities: 8.1.0 xml2js: 0.6.2 rxjs@6.6.7: @@ -11408,12 +11408,12 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - teleproto@1.228.5(patch_hash=baf8f5b95e8b538edf2947f8c9abf87378af4bb4b95f644086d01f219559631a): + teleproto@1.229.0(patch_hash=baf8f5b95e8b538edf2947f8c9abf87378af4bb4b95f644086d01f219559631a): dependencies: big-integer: 1.6.52 mime: 3.0.0 node-localstorage: 2.2.1 - socks: 2.8.9 + socks: 2.8.10 store2: 2.14.4 text-hex@1.0.0: {} From 4a47437d1b291621a4af161fed4314784feb89b6 Mon Sep 17 00:00:00 2001 From: Eson Wong <95921719+esonwong@users.noreply.github.com> Date: Thu, 24 Sep 2026 08:01:02 +0800 Subject: [PATCH 0908/1190] fix(route/latepost): include cover image and abstract in description (#23121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(route/latepost): include cover image and abstract in description The article body selector `#select-main` excludes the cover image, which the site renders in a sibling block. As a result feed items never carried the cover, even though every recent exclusive and long read has one. Pick it up from `#abstract_img` (exclusives, long reads) or `img.interview-pic` (interviews), falling back to the share image declared in the page script so both layouts are covered. The site serves these over plain http, so upgrade the scheme — readers on https tend to refuse mixed content. Articles genuinely without a cover, such as those in the 晚点早知道 column, are left untouched. Also prepend the one-line abstract that accompanies the cover, which was likewise dropped. Co-Authored-By: Claude Opus 5 * fix(route/latepost): keep abstract escaped and drop unneeded trim Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5 --- lib/routes/latepost/index.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/routes/latepost/index.ts b/lib/routes/latepost/index.ts index 421c88478ada..665ca557f478 100644 --- a/lib/routes/latepost/index.ts +++ b/lib/routes/latepost/index.ts @@ -94,7 +94,21 @@ async function handler(ctx): Promise { const content = load(detailResponse); item.title ??= content('div.article-header-title').text(); - item.description = content('#select-main').html()!.replaceAll('


        ', ''); + + // The cover image lives outside of the article body (#select-main), in either the + // `.abstract` block (exclusives / long reads) or `.interview-pic` (interviews). + // Fall back to the share image embedded in the page script, which covers both layouts. + let cover = content('#abstract_img').attr('src') || content('img.interview-pic').attr('src'); + if (!cover) { + cover = /var\s+imgUrl\s*=\s*'([^']+)'/.exec(detailResponse)?.[1]; + } + // The site serves the cover over plain http, which readers may refuse to load. + const coverHtml = cover ? `` : ''; + + const abstract = content('div.abstract-pic-left').html(); + const abstractHtml = abstract ? `
        ${abstract}
        ` : ''; + + item.description = coverHtml + abstractHtml + content('#select-main').html()!.replaceAll('


        ', ''); item.author = content('div.article-header-author div.author-link a.label').first().text(); item.category = item.category.filter(Boolean); item.guid = `latepost-${item.guid}`; From 44f648c1cdb03dd48995eb1e0f480276e1f2f292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 18:27:25 +0800 Subject: [PATCH 0909/1190] chore(deps): bump devenv from `87edd16` to `c5ee672` in the nix group (#23375) Bumps the nix group with 1 update: [devenv](https://github.com/cachix/devenv). Updates `devenv` from `87edd16` to `c5ee672` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/87edd1692507101de0c72f602bed65ec044d92c9...c5ee672098ceb23c694fd89fdb1ff7478ee386fa) --- updated-dependencies: - dependency-name: devenv dependency-version: c5ee672098ceb23c694fd89fdb1ff7478ee386fa dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 835242e480b3..d7ce56e4179e 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789838634, - "narHash": "sha256-y2ksQa9nNo2lFEJ99uzwdLS+6xtkh8kxPu46gwOQJ6c=", + "lastModified": 1789928656, + "narHash": "sha256-D7RNclo2qbp+r3wR1ac5zR+rD6eTe5LUbdVtO/jJvuA=", "owner": "cachix", "repo": "devenv", - "rev": "87edd1692507101de0c72f602bed65ec044d92c9", + "rev": "c5ee672098ceb23c694fd89fdb1ff7478ee386fa", "type": "github" }, "original": { From cbcd2c93b4137fa6b3033bce6e22a327cc2eb1a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:45:53 +0800 Subject: [PATCH 0910/1190] chore(deps-dev): bump @cloudflare/workers-types in the cloudflare group (#23373) Bumps the cloudflare group with 1 update: [@cloudflare/workers-types](https://github.com/cloudflare/workerd). Updates `@cloudflare/workers-types` from 5.20260920.1 to 5.20260921.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) --- updated-dependencies: - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260921.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 80160a38f82a..4b23c723e4e9 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", "@cloudflare/vitest-plugin": "1.1.13", - "@cloudflare/workers-types": "5.20260920.1", + "@cloudflare/workers-types": "5.20260921.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.83.0", "@stylistic/eslint-plugin": "5.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a0cd8ecb09d..aa26049b876e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -307,10 +307,10 @@ importers: version: 1.3.6 '@cloudflare/vitest-plugin': specifier: 1.1.13 - version: 1.1.13(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + version: 1.1.13(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260920.1 - version: 5.20260920.1 + specifier: 5.20260921.1 + version: 5.20260921.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.11.0(jiti@2.7.0)) @@ -472,7 +472,7 @@ importers: version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: specifier: 4.135.0 - version: 4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -634,8 +634,8 @@ packages: cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260920.1': - resolution: {integrity: sha512-wP7wbKqfQjFRGhE/tvu7bwvmBBTqJjvrETTvNzF4u5oolVEXt0gy6JxbqcUC2Khz0Cy+M/q7rhOOODng8VpnPw==} + '@cloudflare/workers-types@5.20260921.1': + resolution: {integrity: sha512-uXGGKlxIrXx9/I33MIdq1hUv+yVTKCBLIJiQ9PthEgLrqePEELYYRC75I0Qw6JUa3cYtoJI8y6ewyLYHPkIP7g==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -3728,7 +3728,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: - resolution: {gitHosted: true, tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} + resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 discord-api-types@0.38.55: @@ -6556,7 +6556,7 @@ snapshots: optionalDependencies: workerd: 1.20260918.1 - '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 @@ -6564,7 +6564,7 @@ snapshots: esbuild: 0.28.1 miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) - wrangler: 4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6587,7 +6587,7 @@ snapshots: '@cloudflare/workerd-windows-64@1.20260918.1': optional: true - '@cloudflare/workers-types@5.20260920.1': {} + '@cloudflare/workers-types@5.20260921.1': {} '@colors/colors@1.6.0': {} @@ -11884,7 +11884,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260918.1 '@cloudflare/workerd-windows-64': 1.20260918.1 - wrangler@4.135.0(@cloudflare/workers-types@5.20260920.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1) @@ -11895,7 +11895,7 @@ snapshots: unenv: 2.0.0-rc.24 workerd: 1.20260918.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260920.1 + '@cloudflare/workers-types': 5.20260921.1 fsevents: 2.3.3 transitivePeerDependencies: - '@types/node' From 44810f4d7fdb5a30e566ce6fc3e5f7f6152e6235 Mon Sep 17 00:00:00 2001 From: Vincent He Date: Thu, 24 Sep 2026 23:26:04 +0800 Subject: [PATCH 0911/1190] fix(route/v2ex): resolve cloudflare 403 by specifying standard user-agent (#23372) --- lib/routes/v2ex/post.ts | 7 +++++++ lib/routes/v2ex/tab.ts | 7 +++++++ lib/routes/v2ex/topics.ts | 7 ++++++- lib/routes/v2ex/xna.ts | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/routes/v2ex/post.ts b/lib/routes/v2ex/post.ts index 756536940d3f..3112a8710c17 100644 --- a/lib/routes/v2ex/post.ts +++ b/lib/routes/v2ex/post.ts @@ -1,3 +1,4 @@ +import { config } from '@/config'; import type { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -33,12 +34,18 @@ async function handler(ctx) { searchParams: { id: postid, }, + headers: { + 'user-agent': config.ua, + }, }); const { data: replies } = await got('https://www.v2ex.com/api/replies/show.json', { searchParams: { topic_id: postid, }, + headers: { + 'user-agent': config.ua, + }, }); const topic = topicResponse[0]; diff --git a/lib/routes/v2ex/tab.ts b/lib/routes/v2ex/tab.ts index bb0ab2c4b27a..e9ae8a975877 100644 --- a/lib/routes/v2ex/tab.ts +++ b/lib/routes/v2ex/tab.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; @@ -32,6 +33,9 @@ async function handler(ctx) { const response = await got({ method: 'get', url: pageUrl, + headers: { + 'user-agent': config.ua, + }, }); const $ = load(response.data); @@ -46,6 +50,9 @@ async function handler(ctx) { const response = await got({ method: 'get', url: link, + headers: { + 'user-agent': config.ua, + }, }); const $ = load(response.data); diff --git a/lib/routes/v2ex/topics.ts b/lib/routes/v2ex/topics.ts index 85efa60b7710..9965d1fb73a2 100644 --- a/lib/routes/v2ex/topics.ts +++ b/lib/routes/v2ex/topics.ts @@ -1,3 +1,4 @@ +import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import got from '@/utils/got'; @@ -40,7 +41,11 @@ export const route: Route = { async function handler(ctx) { const type = ctx.req.param('type'); - const { data } = await got(`https://www.v2ex.com/api/topics/${type}.json`); + const { data } = await got(`https://www.v2ex.com/api/topics/${type}.json`, { + headers: { + 'user-agent': config.ua, + }, + }); let title; if (type === 'hot') { diff --git a/lib/routes/v2ex/xna.ts b/lib/routes/v2ex/xna.ts index befd869aaa8b..866125698693 100644 --- a/lib/routes/v2ex/xna.ts +++ b/lib/routes/v2ex/xna.ts @@ -1,5 +1,6 @@ import { load } from 'cheerio'; +import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import got from '@/utils/got'; @@ -31,6 +32,9 @@ async function handler(ctx) { const response = await got({ method: 'get', url: pageUrl, + headers: { + 'user-agent': config.ua, + }, }); const $ = load(response.data); From 7f39da6baec4789cf3b9c120be8ec6cafef7a1a4 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 25 Sep 2026 00:36:03 +0800 Subject: [PATCH 0912/1190] fix(route): fix fortnite (#23377) --- lib/routes/fortnite/news.ts | 221 ++++++++++++++++++++++++++---------- 1 file changed, 158 insertions(+), 63 deletions(-) diff --git a/lib/routes/fortnite/news.ts b/lib/routes/fortnite/news.ts index 46afbab1deb6..80a5a88eaf76 100644 --- a/lib/routes/fortnite/news.ts +++ b/lib/routes/fortnite/news.ts @@ -1,99 +1,194 @@ -import type { Route } from '@/types'; +import { escapeText } from 'entities'; +import type { Context } from 'hono'; +import { Agent } from 'undici'; + +import { renderYoutube } from '@/routes/youtube/utils'; +import type { DataItem, Language, Route } from '@/types'; import cache from '@/utils/cache'; -import logger from '@/utils/logger'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import playwright from '@/utils/playwright'; + +const langs = ['ar', 'de', 'en-US', 'es-ES', 'es-MX', 'fr', 'it', 'ja', 'ko', 'pl', 'pt-BR', 'ru', 'tr', 'zh-Hans']; + +const tags = { + 'battle-royale': 'Battle Royale', + 'fortnite-competitive': 'Fortnite Competitive', + 'fortnite-festival': 'Fortnite Festival', + 'fortnite-news': 'Fortnite News', + 'fortnite-og': 'Fortnite OG', + 'fortnite-uefn-and-creative': 'UEFN and Creative', + 'lego-fortnite': 'LEGO Fortnite Odyssey', + 'lego-fortnite-brick-life': 'LEGO Fortnite Brick Life', + ranked: 'Ranked Battle Royale', + reload: 'Reload', + 'rocket-racing': 'Rocket Racing', + 'save-the-world': 'Save the World', +}; export const route: Route = { path: '/news/:options?', categories: ['game'], example: '/fortnite/news', - parameters: { options: 'Params' }, + parameters: { + options: 'Query-style options, `lang` and `tag`, see below', + }, features: { requireConfig: false, - requirePuppeteer: true, + requirePuppeteer: false, antiCrawler: false, supportBT: false, supportPodcast: false, supportScihub: false, }, + radar: [ + { + source: ['www.fortnite.com/news'], + target: '/news', + }, + { + source: ['www.fortnite.com/news/tag/:tag'], + target: '/news/tag=:tag', + }, + ], + name: 'News', maintainers: ['lyqluis'], handler, - description: `- \`options.lang\`, optional, language, eg. \`/fortnite/news/lang=en-US\`, common languages are listed below, more languages are available one the [official website](https://www.fortnite.com/news) - -| English (default) | Spanish | Japanese | French | Korean | Polish | -| ----------------- | ------- | -------- | ------ | ------ | ------ | -| en-US | es-ES | ja | fr | ko | pl |`, + url: 'www.fortnite.com/news', + description: `- \`lang\`, default \`en-US\`, one of ${langs.map((lang) => `\`${lang}\``).join(', ')} +- \`tag\`, optional, one of ${Object.entries(tags) + .map(([value, label]) => `\`${value}\` (${label})`) + .join(', ')}`, }; -async function handler(ctx) { - const options = ctx.req - .param('options') - ?.split('&') - .map((op) => op.split('=')); - - const rootUrl = 'https://www.fortnite.com'; - const path = 'news'; - const language = options?.find((op) => op[0] === 'lang')[1] ?? 'en-US'; - const link = `${rootUrl}/${path}?lang=${language}`; - const apiUrl = `https://www.fortnite.com/api/blog/getPosts?category=&postsPerPage=0&offset=0&locale=${language}&rootPageSlug=blog`; - - // Use Playwright instead of got, which may be blocked by anti-crawling scripts with response code 403. - const context = await playwright(); - const page = await context.newPage(); - - // only document is allowed - await page.route('**/*', (route) => { - const request = route.request(); - request.resourceType() === 'document' ? route.continue() : route.abort(); +const fetchData = async (path: string, lang: string) => { + const values = await ofetch(`https://www.fortnite.com${path}.data`, { + query: { lang }, + dispatcher: new Agent({ connect: { minVersion: 'TLSv1.3' } }), + responseType: 'json', }); - - // log manually (necessary for Playwright) - logger.http(`Requesting ${apiUrl}`); - let data; - try { - const response = await page.goto(apiUrl, { - waitUntil: 'networkidle', - }); - if (!response) { - throw new Error(`No response received from ${apiUrl}`); + const get = (i: number) => { + if (i < 0) { + return null; } - if (!response.ok()) { - const statusText = response.statusText(); - const statusMessage = [response.status(), statusText].filter(Boolean).join(' '); - throw new Error(`Fortnite API responded with ${statusMessage} for ${apiUrl}`); + const value = values[i]; + if (Array.isArray(value)) { + return value.map((element) => get(element)); } - const contentType = response.headers()['content-type']; - if (!contentType?.includes('application/json')) { - throw new Error(`Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'}`); + if (typeof value === 'object') { + return Object.fromEntries(Object.entries(value).map(([key, index]) => [values[Number(key.slice(1))], get(index as number)])); } + return value; + }; + return get(0); +}; - data = await response.json(); - } finally { - await page.close(); - await context.close(); +const blockTags = { + paragraph: 'p', + 'heading-one': 'h1', + 'heading-two': 'h2', + 'heading-three': 'h3', + 'heading-four': 'h4', + 'heading-five': 'h5', + 'heading-six': 'h6', + 'bulleted-list': 'ul', + 'numbered-list': 'ol', + 'list-item': 'li', + 'block-quote': 'blockquote', + table: 'table', + table_head: 'thead', + table_body: 'tbody', + table_row: 'tr', + table_header_cell: 'th', + table_cell: 'td', +}; + +const renderNodes = (nodes): string => + nodes + .map((node) => { + if (node.text !== undefined) { + let html = escapeText(node.text); + if (node.bold) { + html = `${html}`; + } + if (node.italic) { + html = `${html}`; + } + if (node.underline) { + html = `${html}`; + } + return html; + } + const inner = renderNodes(node.children); + if (node.type === 'link') { + return `${inner}`; + } + if (node.type === 'list-item-child') { + return inner; + } + const tag = blockTags[node.type] ?? 'div'; + return `<${tag}>${inner}`; + }) + .join(''); + +const originalImage = (url: string) => url.replace('/resize=fit:clip,width:1920/quality=value:80/', '/'); + +const renderSection = (section) => { + switch (section.__typename) { + case 'ElementRichText': + return renderNodes(section.text.raw.children); + case 'ElementLegacyRichText': + return section.content.markdown; + case 'ElementArticleDivider': + return '
        '; + case 'ElementArticleImage': + return ``; + case 'ElementImageGallery': + return section.images.map((image) => ``).join(''); + case 'ElementArticleVideoYouTube': + return renderYoutube(true, section.youTubeVideoId, undefined, undefined); + case 'ElementArticleVideoEmbed': + return ``; + default: + throw new Error(`Unhandled section type: ${section.__typename}`); } +}; - const { blogList: list } = data; +async function handler(ctx: Context) { + const { lang = 'en-US', tag } = Object.fromEntries(new URLSearchParams(ctx.req.param('options'))); + const baseUrl = 'https://www.fortnite.com'; + const path = tag ? `/news/tag/${tag}` : '/news'; + + const data = await fetchData(path, lang); + const page = data[tag ? 'routes/news.tag.$tag' : 'routes/news._index'].data; + + const list: DataItem[] = [page.heroNewsItem, ...page.recentNewsItems.newsItems].filter(Boolean).map((item) => ({ + title: item.heading, + link: `${baseUrl}${item.link}?lang=${lang}`, + category: item.tags.map((t) => t.label), + image: item.imgSrc, + })); const items = await Promise.all( list.map((item) => - cache.tryGet(item.link, () => - Promise.resolve({ - title: item.title, - link: `${rootUrl}/${path}/${item.slug}?lang=${language}`, - pubDate: parseDate(item.date), - author: item.author, - description: item.content, // includes & full text - }) - ) + cache.tryGet(item.link!, async () => { + const data = await fetchData(new URL(item.link!).pathname, lang); + const article = data['routes/news.$'].data.newsArticle; + + item.pubDate = parseDate(article.publishedDate); + item.description = article.sections.map((section) => renderSection(section)).join(''); + + return item; + }) ) ); return { - title: 'Fortnite News', - link, + title: page.seo.title, + description: page.seo.description, + link: `${baseUrl}${path}?lang=${lang}`, + image: page.seo.ogImage.url, + language: lang as Language, item: items, }; } From 27ef2dd36d8bd91fb9527fc7bafc5fa1a9f3fc0c Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 25 Sep 2026 01:28:54 +0800 Subject: [PATCH 0913/1190] feat(route): add komica (#23376) * feat(route): add komica * feat(route/komica): simplify handler logic and improve category handling --- lib/routes/komica/index.ts | 91 ++++++++++++++++++++++++++++++++++ lib/routes/komica/namespace.ts | 8 +++ 2 files changed, 99 insertions(+) create mode 100644 lib/routes/komica/index.ts create mode 100644 lib/routes/komica/namespace.ts diff --git a/lib/routes/komica/index.ts b/lib/routes/komica/index.ts new file mode 100644 index 000000000000..ef89fb70eb9c --- /dev/null +++ b/lib/routes/komica/index.ts @@ -0,0 +1,91 @@ +import type { Cheerio, CheerioAPI } from 'cheerio'; +import { load } from 'cheerio'; +import type { Element } from 'domhandler'; +import type { Context } from 'hono'; + +import InvalidParameterError from '@/errors/types/invalid-parameter'; +import type { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; +import { isValidHost } from '@/utils/valid-host'; + +const parsePost = ($: CheerioAPI, post: Cheerio, base: string): DataItem => { + const no = post.find('.qlink').attr('data-no'); + const image = post.find('.file-thumb img').attr('data-sync-original'); + const dateMatch = post + .find('.now') + .text() + .match(/(\d{4}\/\d{2}\/\d{2})\(.\) (\d{2}:\d{2}:\d{2})/); + + return { + title: post.find('.title').text(), + link: `${base}pixmicat.php?res=${no}`, + guid: `komica:${base}${no}`, + author: post.find('.name').text(), + pubDate: dateMatch ? timezone(parseDate(`${dateMatch[1]} ${dateMatch[2]}`, 'YYYY/MM/DD HH:mm:ss'), 8) : undefined, + category: post + .find('.category a[href*="mode=category"]') + .toArray() + .map((a) => $(a).text()), + description: (image ? (image.endsWith('.mp4') ? `
        ` : `
        `) : '') + (post.find('.quote').html() ?? ''), + }; +}; + +const handler = async (ctx: Context): Promise => { + const { host, board, category } = ctx.req.param(); + if (!isValidHost(host)) { + throw new InvalidParameterError('Invalid host'); + } + const limit = Number(ctx.req.query('limit') ?? '30'); + const baseUrl = `https://${host}.komica1.org/${board}/`; + + const listUrl = `${baseUrl}pixmicat.php?mode=module&load=mod_threadlist${category ? `&c=${encodeURIComponent(category)}` : ''}`; + const html = await ofetch(listUrl); + const $ = load(html); + + const links = $('tr[class^="ListRow"] a[href^="pixmicat.php?res="]') + .toArray() + .map((a) => new URL($(a).attr('href')!, baseUrl).href) + .slice(0, limit); + + const items = await Promise.all( + links.map((link) => + cache.tryGet(link, async () => { + const threadHtml = await ofetch(link); + const $$ = load(threadHtml); + return parsePost($$, $$('.threadpost'), baseUrl); + }) + ) + ); + + return { + title: `Komica - ${$('h1').text()}${category ? ` - ${category}` : ''}`, + link: listUrl, + language: 'zh-TW', + item: items, + }; +}; + +export const route: Route = { + path: '/:host/:board/:category?', + name: '討論板', + url: 'komica1.org', + maintainers: ['TonyRL'], + handler, + example: '/komica/gita/00b/動畫', + parameters: { + host: '子網域,即討論板網址中 `*.komica1.org` 的第一段,如 `gita`', + board: '討論板路徑,如 `00b`', + category: '類別(列表模式),如 `動畫`、`漫畫`、`掛圖`、`新番捏他`、`新番實況`、`模型`、`軍武`;留空為整個版面', + }, + description: '例如綜合避難所 對應 `/komica/gita/00b`,其「動畫」列表對應 `/komica/gita/00b/動畫`。', + categories: ['bbs'], + radar: [ + { + source: ['komica1.org/:board/'], + target: '/:host/:board', + }, + ], +}; diff --git a/lib/routes/komica/namespace.ts b/lib/routes/komica/namespace.ts new file mode 100644 index 000000000000..961b823c3c92 --- /dev/null +++ b/lib/routes/komica/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Komica', + url: 'komica1.org', + categories: ['bbs'], + lang: 'zh-TW', +}; From 0a3a66a1cb28a645ffe90577a68411886274a2ee Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 25 Sep 2026 03:13:19 +0800 Subject: [PATCH 0914/1190] fix(radar): link radar rules to the route namespace page close #17785 --- lib/api/radar/rules/one.test.ts | 7 +++++++ lib/api/radar/rules/utils.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/api/radar/rules/one.test.ts b/lib/api/radar/rules/one.test.ts index 2b754dd0bc7b..3689ee6b0b39 100644 --- a/lib/api/radar/rules/one.test.ts +++ b/lib/api/radar/rules/one.test.ts @@ -22,3 +22,10 @@ describe('api/radar/rules/one', () => { expect(text).toBe(''); }); }); + +describe('api/radar/rules/docs', () => { + it('links docs to the route namespace page', async () => { + const rules = await getRadarRules(); + expect(rules['github.com']['.'][0].docs).toBe('https://docs.rsshub.app/routes/github'); + }); +}); diff --git a/lib/api/radar/rules/utils.ts b/lib/api/radar/rules/utils.ts index 0c8b92161600..37724d82c1b7 100644 --- a/lib/api/radar/rules/utils.ts +++ b/lib/api/radar/rules/utils.ts @@ -34,7 +34,7 @@ export const getRadarRules = async (): Promise> => { } rules[domain][subdomain].push({ title: radarItem.title || data.name, - docs: `https://docs.rsshub.app/routes/${data.categories?.[0] || 'other'}`, + docs: `https://docs.rsshub.app/routes/${namespace}`, source: radarItem.source.map((source) => { const sourceURL = new URL('https://' + source); return sourceURL.pathname + sourceURL.search + sourceURL.hash; From 8706512f3b3709e0ebacbe2040839db8c605596f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:10:29 +0000 Subject: [PATCH 0915/1190] chore(deps): bump @scalar/hono-api-reference in the hono group (#23379) Bumps the hono group with 1 update: [@scalar/hono-api-reference](https://github.com/scalar/scalar/tree/HEAD/integrations/hono). Updates `@scalar/hono-api-reference` from 0.12.2 to 0.12.3 - [Release notes](https://github.com/scalar/scalar/releases) - [Changelog](https://github.com/scalar/scalar/blob/main/integrations/hono/CHANGELOG.md) - [Commits](https://github.com/scalar/scalar/commits/HEAD/integrations/hono) --- updated-dependencies: - dependency-name: "@scalar/hono-api-reference" dependency-version: 0.12.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: hono ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 4b23c723e4e9..80c6298a8056 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@opentelemetry/semantic-conventions": "1.43.0", "@oxc-parser/binding-wasm32-wasip1": "0.0.1", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.12.2", + "@scalar/hono-api-reference": "0.12.3", "@sentry/node": "10.75.0", "cheerio": "1.2.0", "city-timezones": "1.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa26049b876e..91b5720a289f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,8 +89,8 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.12.2 - version: 0.12.2(hono@4.13.8) + specifier: 0.12.3 + version: 0.12.3(hono@4.13.8) '@sentry/node': specifier: 10.75.0 version: 10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) @@ -2636,16 +2636,16 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/client-side-rendering@0.4.1': - resolution: {integrity: sha512-4Ha2ihIPFwmhheSU6U9DlpjgRQ2YOqq2JSmc4ubT2ERApaA/HzuZ1IMyLXDvRICI+KbDducfGw5th9O+TQf+6A==} + '@scalar/client-side-rendering@0.4.2': + resolution: {integrity: sha512-64bpJgq3TVfN7RsMJzOrY9yrhB1RnV+ys693RfSgXSr9VbrKEfU7bkyXLAsL2MFIv4cqBibn78/E4VwoWR554w==} engines: {node: '>=22'} '@scalar/helpers@0.12.0': resolution: {integrity: sha512-rcX0rFLiWWc0VBr/E+HkatubL0I0ZzxASQkd3032NWEMAEfNd2TiJ9kH46HwbSAOOl696ghxymWegjpjjvxwzA==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.12.2': - resolution: {integrity: sha512-VrdMxw/HmejceR9rH0eu4uYKWBILnts5Qj2tgzplufoQbPc+FiFsRKGQ9ZNtZ4CZuvrhEj1y/HrmBX5Zz2sqpg==} + '@scalar/hono-api-reference@0.12.3': + resolution: {integrity: sha512-e/NFmU/udvHILjbU+Wgu9432/nJ62lNqLvcCTwpHTNQ2H0xL5eH74SDYNscwjeagtUCFP09CgwJ7GiyMNWy2Hg==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 @@ -2654,8 +2654,8 @@ packages: resolution: {integrity: sha512-jFqfd+oqqlTfs87V+QkIrR9DiRv+/h1x3GzSGbGe1qrM6togeXwvVG4aurn+LZ+Ej9of54/6FmoI8DPuLOKx7A==} engines: {node: '>=22'} - '@scalar/types@0.20.0': - resolution: {integrity: sha512-+05slk/Q6MRMqEQt6eM77Jt9DnDPAvfcc/6r7mbrXhIqJSp3oAPrixpAnp1knBSlFaxgixcK/GMKw9QDfD+Zgg==} + '@scalar/types@0.20.1': + resolution: {integrity: sha512-gJVEwx2e3o5xJGZqFjZSH8dNqKt9x6wu19AdMGyws1hMGrEXcEMJQ3pCzeUdU+nDxg0StapCF90ZdSOrtzRRqQ==} engines: {node: '>=22'} '@scalar/validation@0.6.3': @@ -7929,17 +7929,17 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/client-side-rendering@0.4.1': + '@scalar/client-side-rendering@0.4.2': dependencies: '@scalar/schemas': 0.10.0 - '@scalar/types': 0.20.0 + '@scalar/types': 0.20.1 '@scalar/validation': 0.6.3 '@scalar/helpers@0.12.0': {} - '@scalar/hono-api-reference@0.12.2(hono@4.13.8)': + '@scalar/hono-api-reference@0.12.3(hono@4.13.8)': dependencies: - '@scalar/client-side-rendering': 0.4.1 + '@scalar/client-side-rendering': 0.4.2 hono: 4.13.8 '@scalar/schemas@0.10.0': @@ -7947,7 +7947,7 @@ snapshots: '@scalar/helpers': 0.12.0 '@scalar/validation': 0.6.3 - '@scalar/types@0.20.0': + '@scalar/types@0.20.1': dependencies: '@scalar/helpers': 0.12.0 nanoid: 5.1.16 From 1b63594191bbbc5cc95521e848c212479da9d60d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:19:41 +0000 Subject: [PATCH 0916/1190] chore(deps-dev): bump the typescript-eslint group with 2 updates (#23381) Bumps the typescript-eslint group with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser). Updates `@typescript-eslint/eslint-plugin` from 8.70.0 to 8.70.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.70.0 to 8.70.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.70.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: typescript-eslint - dependency-name: "@typescript-eslint/parser" dependency-version: 8.70.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: typescript-eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- pnpm-lock.yaml | 126 ++++++++++++++++++++++++------------------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index 80c6298a8056..b25d3858d632 100644 --- a/package.json +++ b/package.json @@ -169,8 +169,8 @@ "@types/module-alias": "2.0.4", "@types/node": "26.6.2", "@types/sanitize-html": "2.16.1", - "@typescript-eslint/eslint-plugin": "8.70.0", - "@typescript-eslint/parser": "8.70.0", + "@typescript-eslint/eslint-plugin": "8.70.1", + "@typescript-eslint/parser": "8.70.1", "@vercel/nft": "1.11.0", "@vitest/coverage-v8": "4.1.11", "discord-api-types": "0.38.55", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91b5720a289f..eeb2bbe516de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,11 +357,11 @@ importers: specifier: 2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: 8.70.0 - version: 8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + specifier: 8.70.1 + version: 8.70.1(@typescript-eslint/parser@8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@typescript-eslint/parser': - specifier: 8.70.0 - version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + specifier: 8.70.1 + version: 8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) '@vercel/nft': specifier: 1.11.0 version: 1.11.0(rollup@4.63.4) @@ -2873,39 +2873,39 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.70.0': - resolution: {integrity: sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==} + '@typescript-eslint/eslint-plugin@8.70.1': + resolution: {integrity: sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.70.0 + '@typescript-eslint/parser': ^8.70.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.70.0': - resolution: {integrity: sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==} + '@typescript-eslint/parser@8.70.1': + resolution: {integrity: sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.70.0': - resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} + '@typescript-eslint/project-service@8.70.1': + resolution: {integrity: sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.70.0': - resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} + '@typescript-eslint/scope-manager@8.70.1': + resolution: {integrity: sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.70.0': - resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} + '@typescript-eslint/tsconfig-utils@8.70.1': + resolution: {integrity: sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.70.0': - resolution: {integrity: sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==} + '@typescript-eslint/type-utils@8.70.1': + resolution: {integrity: sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2915,25 +2915,25 @@ packages: resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.70.0': - resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} + '@typescript-eslint/types@8.70.1': + resolution: {integrity: sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.70.0': - resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} + '@typescript-eslint/typescript-estree@8.70.1': + resolution: {integrity: sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.70.0': - resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} + '@typescript-eslint/utils@8.70.1': + resolution: {integrity: sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.70.0': - resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} + '@typescript-eslint/visitor-keys@8.70.1': + resolution: {integrity: sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': @@ -4357,8 +4357,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.8: - resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} + ignore@7.0.9: + resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} engines: {node: '>= 4'} image-size@0.7.5: @@ -8182,57 +8182,57 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': + '@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)))(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/type-utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/visitor-keys': 8.70.0 + '@typescript-eslint/parser': 8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.70.1 + '@typescript-eslint/type-utils': 8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + '@typescript-eslint/utils': 8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + '@typescript-eslint/visitor-keys': 8.70.1 eslint: 10.11.0(jiti@2.7.0) - ignore: 7.0.8 + ignore: 7.0.9 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': + '@typescript-eslint/parser@8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: - '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/visitor-keys': 8.70.0 + '@typescript-eslint/scope-manager': 8.70.1 + '@typescript-eslint/types': 8.70.1 + '@typescript-eslint/typescript-estree': 8.70.1(@typescript/typescript6@6.0.2) + '@typescript-eslint/visitor-keys': 8.70.1 debug: 4.4.3 eslint: 10.11.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.70.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.70.1(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/tsconfig-utils': 8.70.1(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.1 debug: 4.4.3 typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.70.0': + '@typescript-eslint/scope-manager@8.70.1': dependencies: - '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/visitor-keys': 8.70.0 + '@typescript-eslint/types': 8.70.1 + '@typescript-eslint/visitor-keys': 8.70.1 - '@typescript-eslint/tsconfig-utils@8.70.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/tsconfig-utils@8.70.1(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': + '@typescript-eslint/type-utils@8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: - '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) + '@typescript-eslint/types': 8.70.1 + '@typescript-eslint/typescript-estree': 8.70.1(@typescript/typescript6@6.0.2) + '@typescript-eslint/utils': 8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0)) debug: 4.4.3 eslint: 10.11.0(jiti@2.7.0) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -8242,14 +8242,14 @@ snapshots: '@typescript-eslint/types@8.65.0': {} - '@typescript-eslint/types@8.70.0': {} + '@typescript-eslint/types@8.70.1': {} - '@typescript-eslint/typescript-estree@8.70.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@8.70.1(@typescript/typescript6@6.0.2)': dependencies: - '@typescript-eslint/project-service': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/visitor-keys': 8.70.0 + '@typescript-eslint/project-service': 8.70.1(@typescript/typescript6@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.70.1(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.1 + '@typescript-eslint/visitor-keys': 8.70.1 debug: 4.4.3 minimatch: 10.2.6 semver: 7.8.5 @@ -8259,20 +8259,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': + '@typescript-eslint/utils@8.70.1(@typescript/typescript6@6.0.2)(eslint@10.11.0(jiti@2.7.0))': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/types': 8.70.0 - '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/scope-manager': 8.70.1 + '@typescript-eslint/types': 8.70.1 + '@typescript-eslint/typescript-estree': 8.70.1(@typescript/typescript6@6.0.2) eslint: 10.11.0(jiti@2.7.0) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.70.0': + '@typescript-eslint/visitor-keys@8.70.1': dependencies: - '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/types': 8.70.1 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': @@ -9718,7 +9718,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.8: {} + ignore@7.0.9: {} image-size@0.7.5: {} From d840c9ad5a71e5b11c6ce9e4f8f6a6daa39d0680 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:24:03 +0000 Subject: [PATCH 0917/1190] chore(deps): bump tldts from 7.4.13 to 7.4.14 (#23387) Bumps [tldts](https://github.com/remusao/tldts) from 7.4.13 to 7.4.14. - [Release notes](https://github.com/remusao/tldts/releases) - [Changelog](https://github.com/remusao/tldts/blob/master/CHANGELOG.md) - [Commits](https://github.com/remusao/tldts/compare/v7.4.13...v7.4.14) --- updated-dependencies: - dependency-name: tldts dependency-version: 7.4.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index b25d3858d632..e27c79aa2d60 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "source-map": "0.8.0", "teleproto": "1.229.0", "title": "4.0.1", - "tldts": "7.4.13", + "tldts": "7.4.14", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.2", "tsx": "4.23.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eeb2bbe516de..c792cd844fd5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,8 +248,8 @@ importers: specifier: 4.0.1 version: 4.0.1 tldts: - specifier: 7.4.13 - version: 7.4.13 + specifier: 7.4.14 + version: 7.4.14 tosource: specifier: 2.0.0-alpha.3 version: 2.0.0-alpha.3 @@ -5787,11 +5787,11 @@ packages: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true - tldts-core@7.4.13: - resolution: {integrity: sha512-mbYsrih5FRtGxs3Usvl/PqwJsNpp+jsmrdFviiK02teHDG0/HebBG/pqCylje3kzgXYzuLoHJF/0mz9W53t8Xg==} + tldts-core@7.4.14: + resolution: {integrity: sha512-KYkjfJHnIC5t+Gy7hPrMHgJmWc/N9FfmS+fggRPSlpFckidpB9HD6OzPsSTkfDH+MpZgcu2tJPGgLfHl979N1Q==} - tldts@7.4.13: - resolution: {integrity: sha512-iHtaIWWIbMDkCeJdTBzZFGgbluE5J+oHlb2g7+oAz1S1gpuVpabRZdQyd471Vl8UUkcz2vXSL8xZH2kyCe8tfA==} + tldts@7.4.14: + resolution: {integrity: sha512-EahQoi+Q5oqmG3yxRHx+bwn36OaLbtw6jnTFcGjc8fcmktzTgk45xRnzAx8Ch87PFEiqRhoc3FWBmY+LBrvEGQ==} hasBin: true to-no-case@1.0.2: @@ -11452,11 +11452,11 @@ snapshots: tlds@1.261.0: {} - tldts-core@7.4.13: {} + tldts-core@7.4.14: {} - tldts@7.4.13: + tldts@7.4.14: dependencies: - tldts-core: 7.4.13 + tldts-core: 7.4.14 to-no-case@1.0.2: {} @@ -11477,7 +11477,7 @@ snapshots: tough-cookie@6.0.2: dependencies: - tldts: 7.4.13 + tldts: 7.4.14 tr46@0.0.3: {} From bb97e6067fa9ace625ba1047d7996e05b7050918 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:24:12 +0000 Subject: [PATCH 0918/1190] chore(deps): bump @honeybadger-io/js from 6.16.3 to 6.16.4 (#23385) Bumps [@honeybadger-io/js](https://github.com/honeybadger-io/honeybadger-js) from 6.16.3 to 6.16.4. - [Release notes](https://github.com/honeybadger-io/honeybadger-js/releases) - [Commits](https://github.com/honeybadger-io/honeybadger-js/compare/@honeybadger-io/js@6.16.3...@honeybadger-io/js@6.16.4) --- updated-dependencies: - dependency-name: "@honeybadger-io/js" dependency-version: 6.16.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index e27c79aa2d60..3923e0336a2a 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@bbob/plugin-helper": "4.4.1", "@bbob/preset-html5": "4.4.1", "@googleapis/youtube": "39.0.0", - "@honeybadger-io/js": "6.16.3", + "@honeybadger-io/js": "6.16.4", "@hono/node-server": "2.1.1", "@hono/zod-openapi": "1.6.3", "@jocmp/mercury-parser": "3.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c792cd844fd5..3a5cae285c59 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,8 +47,8 @@ importers: specifier: 39.0.0 version: 39.0.0 '@honeybadger-io/js': - specifier: 6.16.3 - version: 6.16.3 + specifier: 6.16.4 + version: 6.16.4 '@hono/node-server': specifier: 2.1.1 version: 2.1.1(hono@4.13.8) @@ -1240,8 +1240,8 @@ packages: resolution: {integrity: sha512-ojVk6q2GUXqW80szrk/zp2XC1YQFJWQvGHdNNyc1RPGTekguSMpa7Pp9jKitHpdYIsbmEnj1wPTyZassOfSLeg==} engines: {node: '>=14'} - '@honeybadger-io/js@6.16.3': - resolution: {integrity: sha512-AsQaFji3Qu9z7r7RAhkCzkaveWvqGglA+ytaVOUnAsIYihojrnV1D3t0T34/gRHsjme23VWop4X3buSXPq8jzw==} + '@honeybadger-io/js@6.16.4': + resolution: {integrity: sha512-7ZHUVpU7/BeR16fGGzJKa70Q9JB266qBEAno+kvJs22PbSqcAge8DiI8ge701n9FtI7olXIRHOehfOIhmXX0iQ==} engines: {node: '>=14'} hasBin: true peerDependencies: @@ -2744,8 +2744,8 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/aws-lambda@8.10.162': - resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} + '@types/aws-lambda@8.10.163': + resolution: {integrity: sha512-+4zuoEB3S8RIhimtOFT7zAEk2SbpwrKjjGl9CyYnQR6k08uynxfauIIB0pDU1ssr05F8oyGiETwpn+8eXZMqPw==} '@types/bluebird@3.5.42': resolution: {integrity: sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==} @@ -6954,10 +6954,10 @@ snapshots: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.16.3': + '@honeybadger-io/js@6.16.4': dependencies: '@honeybadger-io/core': 6.11.1 - '@types/aws-lambda': 8.10.162 + '@types/aws-lambda': 8.10.163 '@types/express': 5.0.6 '@hono/node-server@2.1.1(hono@4.13.8)': @@ -8041,7 +8041,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@types/aws-lambda@8.10.162': {} + '@types/aws-lambda@8.10.163': {} '@types/bluebird@3.5.42': {} From a4d83c658b53a71d5b77a393642c7c3a104e834d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:25:40 +0000 Subject: [PATCH 0919/1190] chore(deps): bump dotenv from 18.0.1 to 18.0.2 (#23386) Bumps [dotenv](https://github.com/motdotla/dotenv) from 18.0.1 to 18.0.2. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v18.0.1...v18.0.2) --- updated-dependencies: - dependency-name: dotenv dependency-version: 18.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3923e0336a2a..74a94d03a22c 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "currency-symbol-map": "5.1.0", "dayjs": "1.11.23", "destr": "2.0.5", - "dotenv": "18.0.1", + "dotenv": "18.0.2", "entities": "8.1.0", "etag": "1.8.1", "fanfou-sdk": "6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a5cae285c59..dce56991d23f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,8 +116,8 @@ importers: specifier: 2.0.5 version: 2.0.5 dotenv: - specifier: 18.0.1 - version: 18.0.1 + specifier: 18.0.2 + version: 18.0.2 entities: specifier: 8.1.0 version: 8.1.0 @@ -3767,8 +3767,8 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@18.0.1: - resolution: {integrity: sha512-0eR4m4D/jH5eaI3evo2ZqqMii5mrTCR12v12VChdie6O1gPHs7XAjzTep0CWc1Bd+oNx3vVfbi734dK4zghigw==} + dotenv@18.0.2: + resolution: {integrity: sha512-wl0U3CxC0iobaryqEKpFJxzyp4AxQFYAKj/WWszXZr87vVHLi2yJv2syax5Vxt+u02xqNgE277rsc3lOKg9sPg==} engines: {node: '>=12'} hasBin: true @@ -8954,7 +8954,7 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@18.0.1: {} + dotenv@18.0.2: {} dts-resolver@3.0.0(oxc-resolver@11.24.2): optionalDependencies: From 12b66f3eda50b93f2b6c230b21b69693b404caf9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:28:40 +0000 Subject: [PATCH 0920/1190] chore(deps): bump jsdom from 30.1.0 to 30.1.1 (#23388) Bumps [jsdom](https://github.com/jsdom/jsdom) from 30.1.0 to 30.1.1. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](https://github.com/jsdom/jsdom/compare/v30.1.0...v30.1.1) --- updated-dependencies: - dependency-name: jsdom dependency-version: 30.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 78 +++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 74a94d03a22c..b51c3a8278c8 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "instagram-private-api": "1.46.1", "ioredis": "6.0.0", "ip-regex": "5.0.0", - "jsdom": "30.1.0", + "jsdom": "30.1.1", "json-bigint": "1.0.0", "jsrsasign": "11.1.5", "lru-cache": "11.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dce56991d23f..e3125c452bfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,8 +161,8 @@ importers: specifier: 5.0.0 version: 5.0.0 jsdom: - specifier: 30.1.0 - version: 30.1.0(@noble/hashes@2.3.0) + specifier: 30.1.1 + version: 30.1.1(@noble/hashes@2.3.0) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -469,7 +469,7 @@ importers: version: 7.0.0-alpha.3(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) vitest: specifier: 4.1.11 - version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) + version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: specifier: 4.135.0 version: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -497,12 +497,12 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@asamuzakjp/css-color@7.0.0': - resolution: {integrity: sha512-IxddtGnnoidVSO6O/aiTueptbTThHvi2qGHu9/AWuD1J7i86Ox+jl+Y5SXcDH1RixzQlP2cjH/PjnlImZiAurQ==} + '@asamuzakjp/css-color@7.0.1': + resolution: {integrity: sha512-C9duntabagkBZ1LebM7FKmphR4Q1pBclLxVbZETQV0akkFjV0ooFxo8FlvAQyKj9F6l8rEnmidgcTlpyxFYizg==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} - '@asamuzakjp/dom-selector@9.1.4': - resolution: {integrity: sha512-ynDE7RDZM1z+YuSU+iGhRp8WcSXHqK9+G32ZEzsL35TKSoy72fYR7VfrJCQkWe3ciiIWGSTAIBc9XB5jVpqyTw==} + '@asamuzakjp/dom-selector@9.2.1': + resolution: {integrity: sha512-NT4s3yZLjovPpliRpTvdsdzyPjqRqiCZj9MxnarBihbaY5MbAG7DWAJcrLlhmC7xKTNCXsTELcqB8YsqpLnUFQ==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} '@asteasolutions/zod-to-openapi@9.1.0': @@ -1223,8 +1223,8 @@ packages: resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@exodus/bytes@1.15.1': - resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + '@exodus/bytes@1.15.2': + resolution: {integrity: sha512-ctkytT+9TXtv6uiVj6Y7IjKh1+PdBGAD34xd0PpQ/qF+R7O/hoEQbZmN89OaCEaZn5I1A9+ZoPxD03unuFhyhQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -4273,9 +4273,9 @@ packages: hookified@2.2.0: resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} - html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-encoding-sniffer@7.0.0: + resolution: {integrity: sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w==} + engines: {node: ^22.13.0 || >=24.0.0} html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -4546,8 +4546,8 @@ packages: resolution: {integrity: sha512-eFY7Rlscvv/BpY6UsVsjKwMhu1tJO/F4U/vC9g3yeRy/JqszuO1UP6Vdq+85G+3FP6fEw/sWR8a5ftPFY86csw==} engines: {node: ^22.22.2 || >=24.15.0} - jsdom@30.1.0: - resolution: {integrity: sha512-h/Q+H+KmlGf49ITw3TrykSVbg04S75S2O7ZtJc9gMGOWBrQRZmx5AJIKLtJ01e6Kszl+/tpFthCVqxNifxhoqg==} + jsdom@30.1.1: + resolution: {integrity: sha512-FahmoPK5vbPc+jxV1iErMHmAZypCZ942NHF4+qqaWAuvaKKTBZxawnmAtrbGWLU7MtlxfqIP0qw6aSI+aWGtLg==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} peerDependencies: canvas: ^3.2.3 @@ -6202,9 +6202,9 @@ packages: engines: {node: '>=6.0'} hasBin: true - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + w3c-xmlserializer@6.0.0: + resolution: {integrity: sha512-4Nsy8K5Tr6SPDH9jhKJOHf7ChDrc1zufZTVSF7x72hwuEXBqxqk9G6cK+K2NRUtB3iELRJqjXb4JPDMBjMTl2Q==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} @@ -6234,8 +6234,8 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@17.1.1: - resolution: {integrity: sha512-ohjk1mdUebJVadRt3bAhQhx8lSnISq+GDttK79LFl8EHQkAPvzwctoasC4hs8tBt6kLAncBWWyq1N52qEfKvDw==} + whatwg-url@17.1.2: + resolution: {integrity: sha512-TEZA+Zqxin7Jjsm2cjRohCmen5awh+hT6Zi3VZdqZlNRk7zvOI/9WpBFg/DWlA56bWnzwm6DuB8NS0EsxQH9uQ==} engines: {node: ^22.14.0 || >=24.0.0} whatwg-url@5.0.0: @@ -6447,7 +6447,7 @@ snapshots: '@actions/io@3.0.2': {} - '@asamuzakjp/css-color@7.0.0': + '@asamuzakjp/css-color@7.0.1': dependencies: '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) '@csstools/css-color-parser': 4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1) @@ -6455,7 +6455,7 @@ snapshots: '@csstools/css-tokenizer': 4.0.1 lru-cache: 11.5.3 - '@asamuzakjp/dom-selector@9.1.4': + '@asamuzakjp/dom-selector@9.2.1': dependencies: bidi-js: 1.1.0 css-tree: 3.2.1 @@ -6563,7 +6563,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.28.1 miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: @@ -6939,7 +6939,7 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@exodus/bytes@1.15.1(@noble/hashes@2.3.0)': + '@exodus/bytes@1.15.2(@noble/hashes@2.3.0)': optionalDependencies: '@noble/hashes': 2.3.0 @@ -8370,7 +8370,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) + vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) '@vitest/expect@4.1.11': dependencies: @@ -9615,9 +9615,9 @@ snapshots: hookified@2.2.0: {} - html-encoding-sniffer@6.0.0(@noble/hashes@2.3.0): + html-encoding-sniffer@7.0.0(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) + '@exodus/bytes': 1.15.2(@noble/hashes@2.3.0) transitivePeerDependencies: - '@noble/hashes' @@ -9892,27 +9892,27 @@ snapshots: '@types/estree': 1.0.9 '@types/node': 26.6.2 - jsdom@30.1.0(@noble/hashes@2.3.0): + jsdom@30.1.1(@noble/hashes@2.3.0): dependencies: - '@asamuzakjp/css-color': 7.0.0 - '@asamuzakjp/dom-selector': 9.1.4 + '@asamuzakjp/css-color': 7.0.1 + '@asamuzakjp/dom-selector': 9.2.1 '@bramus/specificity': 2.4.2 '@csstools/css-syntax-patches-for-csstree': 1.1.14(css-tree@3.2.1) - '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) + '@exodus/bytes': 1.15.2(@noble/hashes@2.3.0) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.3.0) decimal.js: 10.6.0 - html-encoding-sniffer: 6.0.0(@noble/hashes@2.3.0) + html-encoding-sniffer: 7.0.0(@noble/hashes@2.3.0) is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.3 parse5: 8.0.1 saxes: 6.0.0 tough-cookie: 6.0.2 undici: 8.10.2 - w3c-xmlserializer: 5.0.0 + w3c-xmlserializer: 6.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 17.1.1(@noble/hashes@2.3.0) + whatwg-url: 17.1.2(@noble/hashes@2.3.0) xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -11771,7 +11771,7 @@ snapshots: tsx: 4.23.15 yaml: 2.9.1 - vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.0(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)): + vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)): dependencies: '@vitest/expect': 4.1.11 '@vitest/mocker': 4.1.11(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) @@ -11797,7 +11797,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@types/node': 26.6.2 '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) - jsdom: 30.1.0(@noble/hashes@2.3.0) + jsdom: 30.1.1(@noble/hashes@2.3.0) transitivePeerDependencies: - msw @@ -11806,7 +11806,7 @@ snapshots: acorn: 8.18.0 acorn-walk: 8.3.5 - w3c-xmlserializer@5.0.0: + w3c-xmlserializer@6.0.0: dependencies: xml-name-validator: 5.0.0 @@ -11826,15 +11826,15 @@ snapshots: whatwg-url@16.0.1(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) + '@exodus/bytes': 1.15.2(@noble/hashes@2.3.0) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - '@noble/hashes' - whatwg-url@17.1.1(@noble/hashes@2.3.0): + whatwg-url@17.1.2(@noble/hashes@2.3.0): dependencies: - '@exodus/bytes': 1.15.1(@noble/hashes@2.3.0) + '@exodus/bytes': 1.15.2(@noble/hashes@2.3.0) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: From f2655fd688266d17cc0d1b88efe326636ebde17d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:30:30 +0000 Subject: [PATCH 0921/1190] chore(deps): bump @sentry/node from 10.75.0 to 10.75.1 (#23389) Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 10.75.0 to 10.75.1. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/10.75.0...10.75.1) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-version: 10.75.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 50 +++++++++++++++++++++++++------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index b51c3a8278c8..a43dae4ba232 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "@oxc-parser/binding-wasm32-wasip1": "0.0.1", "@rss3/sdk": "0.0.25", "@scalar/hono-api-reference": "0.12.3", - "@sentry/node": "10.75.0", + "@sentry/node": "10.75.1", "cheerio": "1.2.0", "city-timezones": "1.3.4", "cross-env": "10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3125c452bfc..91614b065d5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,8 +92,8 @@ importers: specifier: 0.12.3 version: 0.12.3(hono@4.13.8) '@sentry/node': - specifier: 10.75.0 - version: 10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) + specifier: 10.75.1 + version: 10.75.1(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1)) cheerio: specifier: 1.2.0 version: 1.2.0 @@ -2677,12 +2677,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.75.0': - resolution: {integrity: sha512-5wDQpQqjJ6RHdPR+z6Q+47XlwoxRKBv0yyB0LKHqL/1azPOQbR+nllyLmmQDcoJpaksuLSmLA1q6hFX7gjDT2w==} + '@sentry/core@10.75.1': + resolution: {integrity: sha512-+/+UanewqPK+oJvDQIHWKLUqnsa0iymEThOPCaFBA8ulbQh9k8lsz8V+NtJwP4G3ncclfzp15tzqVwn6iugV4Q==} engines: {node: '>=18'} - '@sentry/node-core@10.75.0': - resolution: {integrity: sha512-+E3KSX1oMqhpWQ23hj6NblIVUzLy3T2oceU7DfMX1s1ar+npaQZxR5K0/cMGcQgS1LLm8Jqwqo/lrZlKTCDm5A==} + '@sentry/node-core@10.75.1': + resolution: {integrity: sha512-HR0Iy7oNFOP26cwaRi33ZZnvlUSMFzXbZoRgYtBAkqUHDNQA6LWG1N1OoIdKNRJzDkGu/zefbM50Amqgyy1u1Q==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2702,20 +2702,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.75.0': - resolution: {integrity: sha512-XdYW+SEiscQnuugh1hMldfnHurmleKSTSDqgfro6Y1yd7s0r2csnZ5/SEYxIeL4lSl1QXg1lIA1pBmAXjcdnKA==} + '@sentry/node@10.75.1': + resolution: {integrity: sha512-qiWGwh0KiBoq4vEl0FbxFKw0xnOKxuKCaEVdtwMZLAGw1ef77NOCdie7e/Fmcsk9c++Ck4D8NmIjQm6BidRVSQ==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.75.0': - resolution: {integrity: sha512-reJoMtuHMuaiztoDVoaeitc22eHlTAvz2qpFhibWyzukJlF8oXB+o8EvDN9mmDDMedU89c//cXRcnPBlJvUxcw==} + '@sentry/opentelemetry@10.75.1': + resolution: {integrity: sha512-leRKyFkEgV47Qo2wKCMEIYxPgnp67C+wYFg0GPreX5owRde0l18F5BEmsYUU57WAjBKBDo2idrrEEQaU15uH8g==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.75.0': - resolution: {integrity: sha512-7fIa9vFGNzB13hmxl8Sip3xImSkqpc4wETzuQ7CLzVOwPvwI6y/gVEm0pxRhtJSq8SzfOp26eJWl80u8v78Osg==} + '@sentry/server-utils@10.75.1': + resolution: {integrity: sha512-HuA/bCtthA5x/AjYD5WUbG6CBcYJb0WAWKNgrgNkUWtll6eMqG3+7LO4PBcGRbZtlg1OFgClJm4iQFgRN7+ZZg==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': @@ -7968,15 +7968,15 @@ snapshots: '@sentry/conventions@0.16.0': {} - '@sentry/core@10.75.0': + '@sentry/core@10.75.1': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.75.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.75.0 - '@sentry/opentelemetry': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/core': 10.75.1 + '@sentry/opentelemetry': 10.75.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) import-in-the-middle: 3.5.1 optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -7985,34 +7985,34 @@ snapshots: '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.75.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.75.1(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.75.0 - '@sentry/node-core': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.75.0 + '@sentry/core': 10.75.1 + '@sentry/node-core': 10.75.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.222.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.75.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.75.1 import-in-the-middle: 3.5.1 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.75.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.75.0 + '@sentry/core': 10.75.1 - '@sentry/server-utils@10.75.0': + '@sentry/server-utils@10.75.1': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.75.0 + '@sentry/core': 10.75.1 '@sindresorhus/is@4.6.0': {} From 6702485b213fa626eaee42d843f735838b07f26d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:19:29 +0800 Subject: [PATCH 0922/1190] chore(deps-dev): bump the cloudflare group across 1 directory with 3 updates (#23378) Bumps the cloudflare group with 3 updates in the / directory: [@cloudflare/vitest-plugin](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/vitest-plugin), [@cloudflare/workers-types](https://github.com/cloudflare/workerd) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler). Updates `@cloudflare/vitest-plugin` from 1.1.13 to 1.2.1 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Changelog](https://github.com/cloudflare/workers-sdk/blob/main/packages/vitest-plugin/CHANGELOG.md) - [Commits](https://github.com/cloudflare/workers-sdk/commits/@cloudflare/vitest-plugin@1.2.1/packages/vitest-plugin) Updates `@cloudflare/workers-types` from 5.20260921.1 to 5.20260922.1 - [Release notes](https://github.com/cloudflare/workerd/releases) - [Changelog](https://github.com/cloudflare/workerd/blob/main/RELEASE.md) - [Commits](https://github.com/cloudflare/workerd/commits) Updates `wrangler` from 4.135.0 to 4.136.1 - [Release notes](https://github.com/cloudflare/workers-sdk/releases) - [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.136.1/packages/wrangler) --- updated-dependencies: - dependency-name: "@cloudflare/vitest-plugin" dependency-version: 1.2.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: "@cloudflare/workers-types" dependency-version: 5.20260922.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare - dependency-name: wrangler dependency-version: 4.136.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cloudflare ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 6 +-- pnpm-lock.yaml | 106 ++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index a43dae4ba232..151615fa1122 100644 --- a/package.json +++ b/package.json @@ -152,8 +152,8 @@ "@bbob/types": "4.4.1", "@cloudflare/containers": "0.3.7", "@cloudflare/playwright": "1.3.6", - "@cloudflare/vitest-plugin": "1.1.13", - "@cloudflare/workers-types": "5.20260921.1", + "@cloudflare/vitest-plugin": "1.2.1", + "@cloudflare/workers-types": "5.20260922.1", "@eslint/js": "10.0.1", "@oxlint/plugins": "1.83.0", "@stylistic/eslint-plugin": "5.10.0", @@ -207,7 +207,7 @@ "unrun": "0.3.1", "vite-tsconfig-paths": "7.0.0-alpha.3", "vitest": "4.1.11", - "wrangler": "4.135.0", + "wrangler": "4.136.1", "yaml-eslint-parser": "2.1.0" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91614b065d5a..0b38bbf23a37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,11 +306,11 @@ importers: specifier: 1.3.6 version: 1.3.6 '@cloudflare/vitest-plugin': - specifier: 1.1.13 - version: 1.1.13(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) + specifier: 1.2.1 + version: 1.2.1(@cloudflare/workers-types@5.20260922.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11) '@cloudflare/workers-types': - specifier: 5.20260921.1 - version: 5.20260921.1 + specifier: 5.20260922.1 + version: 5.20260922.1 '@eslint/js': specifier: 10.0.1 version: 10.0.1(eslint@10.11.0(jiti@2.7.0)) @@ -471,8 +471,8 @@ importers: specifier: 4.1.11 version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) wrangler: - specifier: 4.135.0 - version: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.136.1 + version: 4.136.1(@cloudflare/workers-types@5.20260922.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.1.0 version: 2.1.0 @@ -588,8 +588,8 @@ packages: playwright-core: optional: true - '@cloudflare/unenv-preset@2.16.1': - resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} + '@cloudflare/unenv-preset@2.16.2': + resolution: {integrity: sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ==} peerDependencies: unenv: 2.0.0-rc.24 workerd: '>1.20260305.0 <2.0.0-0' @@ -597,45 +597,45 @@ packages: workerd: optional: true - '@cloudflare/vitest-plugin@1.1.13': - resolution: {integrity: sha512-0yw65BvdySpKlK73YcJE/VXn2gbwyPB8dSpxFOUQbh4Kg2Yx6msxTEzQ+92dNH5q/84FNGzWcJ2aJjHlWCzGkA==} + '@cloudflare/vitest-plugin@1.2.1': + resolution: {integrity: sha512-k1Y6Z6l1kOh7FYTERL2/MNkED8cp3TbQUxhmhZd+IrkKP59Phhs4Df27iTA1eOKeSTft5EQu4bfdhbyd6GzSmA==} peerDependencies: '@vitest/runner': ^4.1.0 '@vitest/snapshot': ^4.1.0 vitest: ^4.1.0 - '@cloudflare/workerd-darwin-64@1.20260918.1': - resolution: {integrity: sha512-H5Em6Wd0jjxaloYh2rp+WLBl2eWbkk7nSP1svGt6K1RSv/rNVqmKdpjZPJTBSavOmeYGa88qvtOWwS+33OHTqQ==} + '@cloudflare/workerd-darwin-64@1.20260921.1': + resolution: {integrity: sha512-3iB2WnYOlZ29T+1zhCwbHFExCBp6E9bgmDUMryATYwrIGEQ1YbvR78m4ydm56XKN/d/yF3803ivMGfZMYDtiMg==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260918.1': - resolution: {integrity: sha512-CR9JRZEQo93fNgBVF4Df2H2/VYO4n7rxSseSwCVv3bJQEb0huADUSCj2FK4ipxar8ToOEB4wawyw0vJo5U/rQQ==} + '@cloudflare/workerd-darwin-arm64@1.20260921.1': + resolution: {integrity: sha512-FpqVR7IQXVBmGtajyonEmhmb5UAsmV7dTaIkpemmHZXHEw7uYpkhkzKPjc4BOPhNQy8iwt2p+RZBPMY3Y7/bvQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260918.1': - resolution: {integrity: sha512-UQ2nnY3qpXLzQ80frmWO+8HvtqyWaQILe8QYZwpemdjT+sqwCz4Dz+0/WVkFco0v/04kIIqikVeLTY/7gEhmkw==} + '@cloudflare/workerd-linux-64@1.20260921.1': + resolution: {integrity: sha512-riAJIohaVp5A8Sqy4yKlzHOaLPOICMf5oey+jC2rm45RVT+wK8+7UU0d31Dy/02Nc8YUkobAFwNVjX06P8WQ5g==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260918.1': - resolution: {integrity: sha512-4rib51MaLNWweUIUxM/Xj558M5QmyZoBSf0ffv+lYah5VTrvwnez3XGxX72pElnBKHROvAPOi5msQ5Ts8JSI0A==} + '@cloudflare/workerd-linux-arm64@1.20260921.1': + resolution: {integrity: sha512-tnJu08tT7s0XWDqp3O0H/vCp0voy9OqVAzspb89biMo1dh8IiEpnyXnoPmdJ7H4qBnXCmXgy0kuEphuvpDPj9w==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260918.1': - resolution: {integrity: sha512-sATrMx5ShYYgmgUGrcTmvsFSJBFuN95NEkX3xwb1qk8w1A6h5N11sea7yN2IeibwPyPmXKjWNjXOno0hZAM71Q==} + '@cloudflare/workerd-windows-64@1.20260921.1': + resolution: {integrity: sha512-VgNcRPstoZMb1G94JTrx+jU24GtkkazNfox0gnF/2fkuXpcfW/M0e0xvdMovYfwt8ZxG5AB2ZNvanD6ufBwiuQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260921.1': - resolution: {integrity: sha512-uXGGKlxIrXx9/I33MIdq1hUv+yVTKCBLIJiQ9PthEgLrqePEELYYRC75I0Qw6JUa3cYtoJI8y6ewyLYHPkIP7g==} + '@cloudflare/workers-types@5.20260922.1': + resolution: {integrity: sha512-zZVBBFJan5Kw+flEgSHqqCZ0LvHPv5LvfP2igmQiLvZ4fFQYsI4epN9cGiCQqaCyhCzQuqWEyK1Lamzm3QJAmw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -4938,8 +4938,8 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - miniflare@5.20260918.0-alpha: - resolution: {integrity: sha512-vyIes7yW/OTzHtz4GhcBCTohZfqk2eQNiIkngZ07VRA5Qu24aNgW/TrFwlPkMLMjWDQ8R4JJBBHR/KX+liSDtg==} + miniflare@5.20260921.0-alpha: + resolution: {integrity: sha512-vHH/unOYvV2jA1Q9SdkmzrQhhMoksdwg5jegu6ZeKaaRzgxZhVbt1NdTpQjHF2VTgiBjgP8SiUlUMfruB3N3SQ==} engines: {node: '>=22.0.0'} minimatch@10.2.6: @@ -6263,17 +6263,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260918.1: - resolution: {integrity: sha512-NsjfQlBNQ0iEniv/STOy4zbp8s5k60PzL1Ter02Eg44arbDhHtf6UOs03E31XDRmmBFxSIkAZuCyld3RKH1wqA==} + workerd@1.20260921.1: + resolution: {integrity: sha512-4HyG7G1W4ksa6tUZ8bV2jxDRWuL5PXnHm9+Z1sjFPb9OZNoYtXz4y7QQRh4ibi0BF/lOmlAVjbhkUqsAVZuUKA==} engines: {node: '>=16'} hasBin: true - wrangler@4.135.0: - resolution: {integrity: sha512-WrNBQSfIG6YcILJcodYr5ty8vgkzGsV8YX+kfd+uZ5/Bd8cUW0GnUIRKAVfn5kYKqh1m/BPcji9h1d7mE8euFw==} + wrangler@4.136.1: + resolution: {integrity: sha512-xGdBeGMbdFJ7KgndpFyXsncg+kSQ46pD378LTFvyEGUnVvHGAF5ka1shqMpQIrg8n5AFmqajoTxi4E7w7FfEwQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^5.20260918.1 + '@cloudflare/workers-types': ^5.20260921.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6550,21 +6550,21 @@ snapshots: '@cloudflare/playwright@1.3.6': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1)': + '@cloudflare/unenv-preset@2.16.2(unenv@2.0.0-rc.24)(workerd@1.20260921.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260918.1 + workerd: 1.20260921.1 - '@cloudflare/vitest-plugin@1.1.13(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': + '@cloudflare/vitest-plugin@1.2.1(@cloudflare/workers-types@5.20260922.1)(@types/node@26.6.2)(@vitest/runner@4.1.11)(@vitest/snapshot@4.1.11)(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vitest@4.1.11)': dependencies: '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 cjs-module-lexer: 1.2.3 esbuild: 0.28.1 - miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260921.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) vitest: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@26.6.2)(@vitest/coverage-v8@4.1.11)(jsdom@30.1.1(@noble/hashes@2.3.0))(msw@2.15.0(@types/node@26.6.2)(@typescript/typescript6@6.0.2))(vite@7.3.1(@types/node@26.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.15)(yaml@2.9.1)) - wrangler: 4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + wrangler: 4.136.1(@cloudflare/workers-types@5.20260922.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 4.4.3 transitivePeerDependencies: - '@cloudflare/workers-types' @@ -6572,22 +6572,22 @@ snapshots: - bufferutil - utf-8-validate - '@cloudflare/workerd-darwin-64@1.20260918.1': + '@cloudflare/workerd-darwin-64@1.20260921.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260918.1': + '@cloudflare/workerd-darwin-arm64@1.20260921.1': optional: true - '@cloudflare/workerd-linux-64@1.20260918.1': + '@cloudflare/workerd-linux-64@1.20260921.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260918.1': + '@cloudflare/workerd-linux-arm64@1.20260921.1': optional: true - '@cloudflare/workerd-windows-64@1.20260918.1': + '@cloudflare/workerd-windows-64@1.20260921.1': optional: true - '@cloudflare/workers-types@5.20260921.1': {} + '@cloudflare/workers-types@5.20260922.1': {} '@colors/colors@1.6.0': {} @@ -10452,12 +10452,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@5.20260921.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.4(@types/node@26.6.2) undici: 7.29.0 - workerd: 1.20260918.1 + workerd: 1.20260921.1 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -11876,26 +11876,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260918.1: + workerd@1.20260921.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260918.1 - '@cloudflare/workerd-darwin-arm64': 1.20260918.1 - '@cloudflare/workerd-linux-64': 1.20260918.1 - '@cloudflare/workerd-linux-arm64': 1.20260918.1 - '@cloudflare/workerd-windows-64': 1.20260918.1 + '@cloudflare/workerd-darwin-64': 1.20260921.1 + '@cloudflare/workerd-darwin-arm64': 1.20260921.1 + '@cloudflare/workerd-linux-64': 1.20260921.1 + '@cloudflare/workerd-linux-arm64': 1.20260921.1 + '@cloudflare/workerd-windows-64': 1.20260921.1 - wrangler@4.135.0(@cloudflare/workers-types@5.20260921.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.136.1(@cloudflare/workers-types@5.20260922.1)(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1) + '@cloudflare/unenv-preset': 2.16.2(unenv@2.0.0-rc.24)(workerd@1.20260921.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260918.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 5.20260921.0-alpha(@types/node@26.6.2)(bufferutil@4.1.0)(utf-8-validate@5.0.10) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260918.1 + workerd: 1.20260921.1 optionalDependencies: - '@cloudflare/workers-types': 5.20260921.1 + '@cloudflare/workers-types': 5.20260922.1 fsevents: 2.3.3 transitivePeerDependencies: - '@types/node' From 71aa020625fba2016b90165ffd86ca018600e146 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:27:33 +0800 Subject: [PATCH 0923/1190] chore(deps): bump the nix group with 2 updates (#23382) Bumps the nix group with 2 updates: [devenv](https://github.com/cachix/devenv) and [nixpkgs](https://github.com/NixOS/nixpkgs). Updates `devenv` from `c5ee672` to `1c57b5d` - [Release notes](https://github.com/cachix/devenv/releases) - [Commits](https://github.com/cachix/devenv/compare/c5ee672098ceb23c694fd89fdb1ff7478ee386fa...1c57b5dea0d400af97053fdd1a536fca17378f73) Updates `nixpkgs` from `20b1ddd` to `44a9189` - [Commits](https://github.com/NixOS/nixpkgs/compare/20b1ddd1aa5ace70c9468305030aa4f9ef79671b...44a91898084f46797b5fac650c7e8c9ac38c43d4) --- updated-dependencies: - dependency-name: devenv dependency-version: 1c57b5dea0d400af97053fdd1a536fca17378f73 dependency-type: direct:production dependency-group: nix - dependency-name: nixpkgs dependency-version: 44a91898084f46797b5fac650c7e8c9ac38c43d4 dependency-type: direct:production dependency-group: nix ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index d7ce56e4179e..df00feca7609 100644 --- a/flake.lock +++ b/flake.lock @@ -64,11 +64,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1789928656, - "narHash": "sha256-D7RNclo2qbp+r3wR1ac5zR+rD6eTe5LUbdVtO/jJvuA=", + "lastModified": 1789991752, + "narHash": "sha256-SiH+H00IutELZBg7BBNo6r5DU6E+DG7DjrbeX5+QLa0=", "owner": "cachix", "repo": "devenv", - "rev": "c5ee672098ceb23c694fd89fdb1ff7478ee386fa", + "rev": "1c57b5dea0d400af97053fdd1a536fca17378f73", "type": "github" }, "original": { @@ -278,11 +278,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789785513, - "narHash": "sha256-B44WL6h0XoLjJ41bUPJk0X5SDinLCII//6EcBLXKiJ0=", + "lastModified": 1789921291, + "narHash": "sha256-Ft/BRnIqw1MywFoXydKobjjWmDFgDdYtSpJliE8+yUw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "20b1ddd1aa5ace70c9468305030aa4f9ef79671b", + "rev": "44a91898084f46797b5fac650c7e8c9ac38c43d4", "type": "github" }, "original": { From 372cca9932c76b5f1dcb033bb56c2cd5aea2f8fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Sep 2026 00:34:54 +0800 Subject: [PATCH 0924/1190] chore(deps): bump the oxc group across 1 directory with 5 updates (#23380) * chore(deps): bump the oxc group across 1 directory with 5 updates Bumps the oxc group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [oxc-parser](https://github.com/oxc-project/oxc/tree/HEAD/napi/parser) | `0.150.0` | `0.151.0` | | [@oxlint/plugins](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugins) | `1.83.0` | `1.85.0` | | [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) | `0.68.0` | `0.70.0` | | [oxlint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint) | `1.83.0` | `1.85.0` | | [oxlint-plugin-eslint](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint-plugin-eslint) | `1.83.0` | `1.85.0` | Updates `oxc-parser` from 0.150.0 to 0.151.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/parser/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.151.0/napi/parser) Updates `@oxlint/plugins` from 1.83.0 to 1.85.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.85.0/npm/oxlint-plugins) Updates `oxfmt` from 0.68.0 to 0.70.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.70.0/npm/oxfmt) Updates `oxlint` from 1.83.0 to 1.85.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.85.0/npm/oxlint) Updates `oxlint-plugin-eslint` from 1.83.0 to 1.85.0 - [Release notes](https://github.com/oxc-project/oxc/releases) - [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxlint-plugin-eslint/CHANGELOG.md) - [Commits](https://github.com/oxc-project/oxc/commits/oxlint_v1.85.0/npm/oxlint-plugin-eslint) --- updated-dependencies: - dependency-name: "@oxlint/plugins" dependency-version: 1.85.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxc-parser dependency-version: 0.151.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxfmt dependency-version: 0.70.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint dependency-version: 1.85.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc - dependency-name: oxlint-plugin-eslint dependency-version: 1.85.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: oxc ... Signed-off-by: dependabot[bot] * chore: disable unicorn rules that overlaps with oxc native one * style: fix no-unnecessary-boolean-literal-compare * style: fix no-unnecessary-type-conversion * style: add all oxc/* correctness rules --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tony --- .oxlintrc.json | 81 ++-- lib/config.ts | 6 +- lib/middleware/parameter.ts | 11 +- lib/routes/36kr/utils.ts | 4 +- lib/routes/atcoder/contest.ts | 2 +- lib/routes/cas/sim/kyjz.ts | 2 +- lib/routes/cnki/journals.ts | 2 +- lib/routes/ddosi/category.ts | 4 +- lib/routes/ddosi/index.ts | 6 +- lib/routes/denonbu/news.ts | 2 +- lib/routes/hiring.cafe/jobs.tsx | 4 +- lib/routes/hrbeu/job/calendar.ts | 2 +- lib/routes/iresearch/report.ts | 2 +- lib/routes/jimmyspa/news.ts | 2 +- lib/routes/meta/ai-blog.ts | 2 +- lib/routes/nankai/yzb.ts | 2 +- lib/routes/nintendo/templates/eshop-hk.tsx | 2 +- lib/routes/polymarket/leaderboard.ts | 4 +- lib/routes/polymarket/positions.ts | 10 +- lib/routes/polymarket/series.ts | 4 +- lib/routes/polymarket/utils.ts | 2 +- lib/routes/qingting/podcast.ts | 2 +- lib/routes/showstart/service.ts | 4 +- lib/routes/skeb/utils.tsx | 2 +- lib/routes/syosetu/ranking-isekai.ts | 2 +- lib/routes/syosetu/ranking-r18.ts | 2 +- lib/routes/syosetu/ranking.ts | 2 +- lib/routes/telegram/tglib/client.ts | 6 +- lib/routes/ycwb/index.tsx | 2 +- lib/routes/zsxq/utils.ts | 2 +- lib/utils/cache/memory.ts | 6 +- lib/utils/cache/redis.ts | 4 +- package.json | 10 +- pnpm-lock.yaml | 515 +++++++++++---------- 34 files changed, 370 insertions(+), 345 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 0c7e2ae92097..e021c191c03b 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -10,7 +10,7 @@ "es2026": true, "node": true }, - "plugins": ["eslint", "typescript", "node", "unicorn", "import"], + "plugins": ["eslint", "typescript", "node", "unicorn", "import", "oxc"], "jsPlugins": [ { "name": "n", "specifier": "eslint-plugin-n" }, { "name": "unicorn-js", "specifier": "eslint-plugin-unicorn" }, @@ -38,20 +38,20 @@ "no-dupe-else-if": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", - // "no-empty-character-class": "error", -> off, handled by eslint-plugin-regexp + // "no-empty-character-class": "error", off, handled by eslint-plugin-regexp "no-empty-pattern": "error", "no-ex-assign": "error", "no-fallthrough": "error", "no-func-assign": "error", "no-import-assign": "error", - // "no-invalid-regexp": "error", -> off, handled by eslint-plugin-regexp + // "no-invalid-regexp": "error", off, handled by eslint-plugin-regexp "no-irregular-whitespace": "error", "no-loss-of-precision": "error", "no-misleading-character-class": "error", "no-new-native-nonconstructor": "error", "no-nonoctal-decimal-escape": "error", "no-obj-calls": "error", - // "no-prototype-builtins": "error", -> off + // "no-prototype-builtins": "error", off "no-self-assign": "error", "no-setter-return": "error", "no-sparse-arrays": "error", @@ -64,8 +64,8 @@ "no-unsafe-negation": "error", "no-unsafe-optional-chaining": "error", "no-unused-private-class-members": "error", - // "no-unused-vars": "error", -> off for @typescript-eslint/no-unused-vars - // "no-useless-backreference": "error", -> off, handled by eslint-plugin-regexp + // "no-unused-vars": "error", off, handled by @typescript-eslint/no-unused-vars + // "no-useless-backreference": "error", off, handled by eslint-plugin-regexp "use-isnan": "error", "valid-typeof": "error", // #endregion @@ -160,7 +160,7 @@ "unicorn/consistent-empty-array-spread": "error", "unicorn/consistent-existence-index-check": "error", "unicorn-js/consistent-export-decorator-position": "error", - // "unicorn/consistent-function-scoping": "error", -> warn + // "unicorn/consistent-function-scoping": "error", warn "unicorn-js/consistent-json-file-read": "error", "unicorn-js/consistent-optional-chaining": "error", "unicorn/consistent-template-literal-escape": "error", @@ -181,7 +181,7 @@ "unicorn/new-for-builtins": "error", "unicorn/no-abusive-eslint-disable": "error", "unicorn/no-accessor-recursion": "error", - "unicorn-js/no-accidental-bitwise-operator": "error", + // "unicorn-js/no-accidental-bitwise-operator": "error", off, handled by oxc/bad-bitwise-operator "unicorn/no-anonymous-default-export": "error", // "unicorn/no-array-callback-reference": "error", "unicorn-js/no-array-concat-in-loop": "error", @@ -199,19 +199,19 @@ // "unicorn/no-await-expression-member": "error", "unicorn/no-await-in-promise-methods": "error", // "unicorn-js/no-blob-to-file": "error", // unused - "unicorn-js/no-boolean-sort-comparator": "error", + // "unicorn-js/no-boolean-sort-comparator": "error", off, handled by tsc // "unicorn-js/no-break-in-nested-loop": "error", // unopinionated // "unicorn-js/no-canvas-to-image": "error", // unused - "unicorn-js/no-chained-comparison": "error", - "unicorn-js/no-collection-bracket-access": "error", + // "unicorn-js/no-chained-comparison": "error", off, handled by oxc/bad-comparison-sequence + // "unicorn-js/no-collection-bracket-access": "error", off, handled by tsc "unicorn-js/no-computed-property-existence-check": "error", "unicorn-js/no-confusing-array-splice": "error", "unicorn/no-confusing-array-with": "error", "unicorn/no-console-spaces": "error", - "unicorn-js/no-constant-zero-expression": "error", + // "unicorn-js/no-constant-zero-expression": "error", off, handled by oxc/erasing-op // "unicorn-js/no-declarations-before-early-exit": "error", // unopinionated "unicorn/no-document-cookie": "error", - "unicorn-js/no-double-comparison": "error", + // "unicorn-js/no-double-comparison": "error", off, handled by oxc/double-comparisons "unicorn-js/no-duplicate-if-branches": "error", "unicorn-js/no-duplicate-logical-operands": "error", "unicorn-js/no-duplicate-loops": "error", @@ -226,8 +226,8 @@ "unicorn-js/no-incorrect-query-selector": "error", // "unicorn-js/no-incorrect-template-string-interpolation": "error", // unopinionated, too many false positives in v68 "unicorn/no-instanceof-builtins": "error", - "unicorn-js/no-invalid-argument-count": "error", - "unicorn-js/no-invalid-character-comparison": "error", + // "unicorn-js/no-invalid-argument-count": "error", off, handled by tsc + // "unicorn-js/no-invalid-character-comparison": "error", off, handled by oxc/bad-char-at-comparison "unicorn/no-invalid-fetch-options": "error", "unicorn/no-invalid-remove-event-listener": "error", "unicorn-js/no-invalid-well-known-symbol-methods": "error", @@ -237,9 +237,9 @@ "unicorn-js/no-loop-iterable-mutation": "error", "unicorn/no-magic-array-flat-depth": "error", "unicorn-js/no-mismatched-map-key": "error", - "unicorn-js/no-misrefactored-assignment": "error", + // "unicorn-js/no-misrefactored-assignment": "error", off, handled by oxc/misrefactored-assign-op "unicorn-js/no-multiple-promise-resolver-calls": "error", - // "unicorn/no-named-default": "error", -> use import/no-named-default + // "unicorn/no-named-default": "error", use import/no-named-default "unicorn-js/no-negated-array-predicate": "error", "unicorn-js/no-negated-comparison": "error", "no-negated-condition": "off", @@ -250,7 +250,7 @@ "unicorn/no-new-array": "error", "unicorn/no-new-buffer": "error", "unicorn-js/no-non-function-verb-prefix": "error", - "unicorn-js/no-nonstandard-builtin-properties": "error", + // "unicorn-js/no-nonstandard-builtin-properties": "error", off, handled by tsc // "unicorn/no-null": "error", // "unicorn/no-object-as-default-parameter": "error", "unicorn-js/no-object-methods-with-collections": "error", @@ -268,15 +268,15 @@ "unicorn-js/no-this-outside-of-class": "error", // "unicorn-js/no-transition-all": "error", // css // "unicorn-js/no-top-level-assignment-in-function": "error", // unopinionated - // "unicorn-js/no-top-level-side-effects": "error", // allows dayjs.extend() + // "unicorn-js/no-top-level-side-effects": "error", // off, allows dayjs.extend() "unicorn/no-typeof-undefined": "error", "unicorn-js/no-uncalled-method": "error", - "unicorn-js/no-undeclared-class-members": "error", + // "unicorn-js/no-undeclared-class-members": "error", off, handled by tsc "unicorn/no-unnecessary-array-flat-depth": "error", "unicorn-js/no-unnecessary-array-flat-map": "error", "unicorn/no-unnecessary-array-splice-count": "error", "unicorn/no-unnecessary-await": "error", - "unicorn-js/no-unnecessary-boolean-comparison": "error", + // "unicorn-js/no-unnecessary-boolean-comparison": "error", off, handled by @typescript-eslint/no-unnecessary-boolean-literal-compare "unicorn-js/no-unnecessary-fetch-options": "error", "unicorn-js/no-unnecessary-global-this": "error", "unicorn-js/no-unnecessary-nested-ternary": "error", @@ -289,17 +289,17 @@ "unicorn/no-unreadable-iife": "error", "unicorn-js/no-unreadable-object-destructuring": "error", "unicorn-js/no-unsafe-buffer-conversion": "error", - "unicorn-js/no-unsafe-promise-all-settled-values": "error", + // "unicorn-js/no-unsafe-promise-all-settled-values": "error", off, handled by tsc "unicorn-js/no-unsafe-property-key": "error", // "unicorn-js/no-unsafe-sqlite-interpolation": "error", // unused "unicorn-js/no-unsafe-string-replacement": "error", "unicorn-js/no-unused-builtin-method-return": "error", "unicorn-js/no-unused-iterator-helper": "error", "unicorn-js/no-useless-boolean-cast": "error", - "unicorn-js/no-useless-coercion": "error", + // "unicorn-js/no-useless-coercion": "error", off, handled by @typescript-eslint/no-unnecessary-type-conversion "unicorn/no-useless-collection-argument": "error", "unicorn-js/no-useless-compound-assignment": "error", - "unicorn-js/no-useless-concat": "error", + // "unicorn-js/no-useless-concat": "error", off, handled by no-useless-concat "unicorn-js/no-useless-continue": "error", "unicorn-js/no-useless-delete-check": "error", "unicorn-js/no-useless-else": "error", @@ -315,7 +315,7 @@ "unicorn/no-useless-spread": "error", "unicorn-js/no-useless-set-construction": "error", // "unicorn/no-useless-switch-case": "error", - "unicorn-js/no-useless-template-literals": "error", + // "unicorn-js/no-useless-template-literals": "error", off, handled by @typescript-eslint/no-unnecessary-template-expression and no-implicit-coercion // "unicorn/no-useless-undefined": "error", "unicorn-js/no-using-resource-escape": "error", "unicorn-js/no-xor-as-exponentiation": "error", @@ -384,7 +384,7 @@ "unicorn/prefer-logical-operator-over-ternary": "error", "unicorn-js/prefer-map-from-entries": "error", "unicorn-js/prefer-math-abs": "error", - "unicorn-js/prefer-math-constants": "error", + // "unicorn-js/prefer-math-constants": "error", off, handled by oxc/approx-constant "unicorn/prefer-math-min-max": "error", "unicorn/prefer-math-trunc": "error", // "unicorn-js/prefer-minimal-ternary": "error", // unopinionated, fixes have less readability @@ -455,13 +455,13 @@ // "unicorn/prevent-abbreviations": "error", "unicorn/relative-url-style": "error", "unicorn/require-array-join-separator": "error", - "unicorn-js/require-array-sort-compare": "error", + // "unicorn-js/require-array-sort-compare": "error", off, handled by @typescript-eslint/require-array-sort-compare "unicorn-js/require-css-escape": "error", "unicorn/require-module-attributes": "error", "unicorn/require-module-specifiers": "error", "unicorn/require-number-to-fixed-digits-argument": "error", "unicorn-js/require-passive-events": "error", - "unicorn-js/require-proxy-trap-boolean-return": "error", + // "unicorn-js/require-proxy-trap-boolean-return": "error", off, handled by tsc // "unicorn-js/single-line-block-comment-style": "error", // "unicorn/switch-case-braces": "error", "unicorn-js/switch-case-break-position": "error", @@ -546,7 +546,7 @@ "arrow-body-style": "error", "block-scoped-var": "error", "curly": "error", - // "dot-notation": "error", -> use @typescript-eslint/dot-notation + // "dot-notation": "error", use @typescript-eslint/dot-notation "eqeqeq": "error", "default-case": ["warn", { "commentPattern": "^no default$" }], @@ -669,12 +669,15 @@ "@typescript-eslint/no-inferrable-types": ["error", { "ignoreParameters": true, "ignoreProperties": true }], + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", // type-aware "@typescript-eslint/no-unnecessary-template-expression": "error", // type-aware + "@typescript-eslint/no-unnecessary-type-conversion": "error", // type-aware "@typescript-eslint/no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true }], "@typescript-eslint/no-unused-vars": ["error", { "args": "after-used", "argsIgnorePattern": "^_" }], "@typescript-eslint/prefer-string-starts-ends-with": "error", // type-aware + "@typescript-eslint/require-array-sort-compare": "error", // type-aware // type-aware // "@typescript-eslint/await-thenable": "off", @@ -764,6 +767,26 @@ "unicorn/text-encoding-identifier-case": "off", // #endregion + // #region --- oxc --- + "oxc/approx-constant": "error", + "oxc/bad-array-method-on-arguments": "error", + "oxc/bad-bitwise-operator": "error", + "oxc/bad-char-at-comparison": "error", + "oxc/bad-comparison-sequence": "error", + "oxc/bad-match-all-arg": "error", + "oxc/bad-min-max-func": "error", + "oxc/bad-object-literal-comparison": "error", + "oxc/bad-replace-all-arg": "error", + "oxc/const-comparisons": "error", + "oxc/double-comparisons": "error", + "oxc/erasing-op": "error", + "oxc/misrefactored-assign-op": "error", + "oxc/missing-throw": "error", + "oxc/number-arg-out-of-range": "error", + "oxc/only-used-in-recursion": "error", + "oxc/uninvoked-array-callback": "error", + // #endregion + // #region --- stylistic --- "@stylistic/arrow-parens": "error", "@stylistic/arrow-spacing": "error", diff --git a/lib/config.ts b/lib/config.ts index 6ad3b09de2ac..a7f2f61ef8de 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1166,12 +1166,12 @@ const calculateValue = () => { telegram: { token: envs.TELEGRAM_TOKEN, session: envs.TELEGRAM_SESSION, - apiId: envs.TELEGRAM_API_ID, + apiId: toInt(envs.TELEGRAM_API_ID), apiHash: envs.TELEGRAM_API_HASH, - maxConcurrentDownloads: envs.TELEGRAM_MAX_CONCURRENT_DOWNLOADS, + maxConcurrentDownloads: toInt(envs.TELEGRAM_MAX_CONCURRENT_DOWNLOADS), proxy: { host: envs.TELEGRAM_PROXY_HOST, - port: envs.TELEGRAM_PROXY_PORT, + port: toInt(envs.TELEGRAM_PROXY_PORT), secret: envs.TELEGRAM_PROXY_SECRET, }, }, diff --git a/lib/middleware/parameter.ts b/lib/middleware/parameter.ts index a11fdbc01031..880f0e9ce08a 100644 --- a/lib/middleware/parameter.ts +++ b/lib/middleware/parameter.ts @@ -76,7 +76,9 @@ const middleware: MiddlewareHandler = async (ctx, next) => { data.item ||= []; // decode HTML entities + // oxlint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion -- routes may return non-string values at runtime data.title &&= decodeHTMLStrict(data.title + ''); + // oxlint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion -- routes may return non-string values at runtime data.description &&= decodeHTMLStrict(data.description + ''); // sort items @@ -85,6 +87,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { } const handleItem = (item: DataItem) => { + // oxlint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion -- routes may return non-string values at runtime item.title &&= decodeHTMLStrict(item.title + ''); item.description ||= item.content?.html; @@ -173,7 +176,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { if (item.category) { // convert single string to array, and filter only string type category Array.isArray(item.category) || (item.category = [item.category]); - item.category = item.category.filter((e) => String(e) === e); + item.category = item.category.filter((e) => typeof e === 'string'); } return item; }; @@ -223,15 +226,15 @@ const middleware: MiddlewareHandler = async (ctx, next) => { if (ctx.req.query('filter_title')) { const titleRegex = makeRegex(ctx.req.query('filter_title')!); - isFilter = titleRegex instanceof RE2JS ? titleRegex.matcher(title).find() : !!titleRegex.test(title); + isFilter = titleRegex instanceof RE2JS ? titleRegex.matcher(title).find() : titleRegex.test(title); } if (ctx.req.query('filter_description')) { const descriptionRegex = makeRegex(ctx.req.query('filter_description')!); - isFilter &&= descriptionRegex instanceof RE2JS ? descriptionRegex.matcher(description).find() : !!descriptionRegex.test(description); + isFilter &&= descriptionRegex instanceof RE2JS ? descriptionRegex.matcher(description).find() : descriptionRegex.test(description); } if (ctx.req.query('filter_author')) { const authorRegex = makeRegex(ctx.req.query('filter_author')!); - isFilter &&= authorRegex instanceof RE2JS ? authorRegex.matcher(author).find() : !!authorRegex.test(author); + isFilter &&= authorRegex instanceof RE2JS ? authorRegex.matcher(author).find() : authorRegex.test(author); } if (ctx.req.query('filter_category')) { const categoryRegex = makeRegex(ctx.req.query('filter_category')!); diff --git a/lib/routes/36kr/utils.ts b/lib/routes/36kr/utils.ts index 754ffa8104f3..fd4699b32a2b 100644 --- a/lib/routes/36kr/utils.ts +++ b/lib/routes/36kr/utils.ts @@ -22,9 +22,7 @@ export const ProcessItem = (item) => CryptoJS.AES.decrypt(cipherTextList[1], key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, - }) - .toString(CryptoJS.enc.Utf8) - .toString() + }).toString(CryptoJS.enc.Utf8) ).articleDetail.articleDetailData.data; item.description = content.widgetContent; } diff --git a/lib/routes/atcoder/contest.ts b/lib/routes/atcoder/contest.ts index 1201752bcade..749840f65669 100644 --- a/lib/routes/atcoder/contest.ts +++ b/lib/routes/atcoder/contest.ts @@ -96,7 +96,7 @@ async function handler(ctx) { ); return { - title: String(isStatus ? `${$(`#contest-table-${rated} h3`).text()} - AtCoder` : $('title').text()), + title: isStatus ? `${$(`#contest-table-${rated} h3`).text()} - AtCoder` : $('title').text(), link: currentUrl, item: items, allowEmpty: true, diff --git a/lib/routes/cas/sim/kyjz.ts b/lib/routes/cas/sim/kyjz.ts index 1de0284748f0..167e4d4a86c5 100644 --- a/lib/routes/cas/sim/kyjz.ts +++ b/lib/routes/cas/sim/kyjz.ts @@ -59,7 +59,7 @@ async function handler() { const reg = /文章来源:(.*?)\|/; item.title = $('p.wztitle').text().trim(); - item.author = reg.exec(author)![1].toString().trim(); + item.author = reg.exec(author)![1].trim(); item.description = $('.TRS_Editor').html(); item.pubDate = parseDate(item.pubDate!); return item; diff --git a/lib/routes/cnki/journals.ts b/lib/routes/cnki/journals.ts index ea81260c87eb..7b3c46e94152 100644 --- a/lib/routes/cnki/journals.ts +++ b/lib/routes/cnki/journals.ts @@ -95,7 +95,7 @@ async function handler(ctx) { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => ProcessItem(item)))); return { - title: String(title), + title, link: journalUrl, item: items, }; diff --git a/lib/routes/ddosi/category.ts b/lib/routes/ddosi/category.ts index 0ddca00a4f5e..3d913da7dd72 100644 --- a/lib/routes/ddosi/category.ts +++ b/lib/routes/ddosi/category.ts @@ -53,8 +53,8 @@ async function handler(ctx) { const date = parseDate(item.find('.meta-date a time').attr('datetime')!); return { - title: String(title), - description: String(description), + title, + description, pubDate: date, link: String(href), }; diff --git a/lib/routes/ddosi/index.ts b/lib/routes/ddosi/index.ts index 345ca5ec8c71..480a0429d2d6 100644 --- a/lib/routes/ddosi/index.ts +++ b/lib/routes/ddosi/index.ts @@ -39,10 +39,10 @@ async function handler() { const date = parseDate(item.find('.meta-date a time').attr('datetime')!); return { - title: String(title), - description: String(description), + title, + description, pubDate: date, - link: String(href), + link: href, }; }); diff --git a/lib/routes/denonbu/news.ts b/lib/routes/denonbu/news.ts index 5454a0df5a83..487d7bb266ef 100644 --- a/lib/routes/denonbu/news.ts +++ b/lib/routes/denonbu/news.ts @@ -126,7 +126,7 @@ async function getToken(): Promise { if (!token) { throw new Error('Failed to get token'); } - cache.set(CACHE_TOKEN_KEY, token, expires ? expires - Number(Date.now()) / 1000 - 1 : 3600); + cache.set(CACHE_TOKEN_KEY, token, expires ? expires - Date.now() / 1000 - 1 : 3600); return token; } diff --git a/lib/routes/hiring.cafe/jobs.tsx b/lib/routes/hiring.cafe/jobs.tsx index 989107144ac1..281ab0ae4eb4 100644 --- a/lib/routes/hiring.cafe/jobs.tsx +++ b/lib/routes/hiring.cafe/jobs.tsx @@ -62,10 +62,10 @@ interface SearchParams { } const validateSearchParams = ({ keywords, page = 0, size = CONFIG.DEFAULT_PAGE_SIZE }: SearchParams): SearchParams => { - const normalizedSize = Math.floor(Number(size)); + const normalizedSize = Math.floor(size); return { keywords: keywords.trim(), - page: Math.max(0, Math.floor(Number(page))), + page: Math.max(0, Math.floor(page)), size: Math.min(Math.max(1, normalizedSize), CONFIG.MAX_PAGE_SIZE), }; }; diff --git a/lib/routes/hrbeu/job/calendar.ts b/lib/routes/hrbeu/job/calendar.ts index cbee84c6c1ab..fbcfc325e30f 100644 --- a/lib/routes/hrbeu/job/calendar.ts +++ b/lib/routes/hrbeu/job/calendar.ts @@ -53,7 +53,7 @@ async function handler() { let link = ''; for (let i = 0, l = response.length; i < l; i++) { // if (response[i].day === Number('10')) { - if (response[i].day === Number(day)) { + if (response[i].day === day) { link = response[i].Items[0].link; } } diff --git a/lib/routes/iresearch/report.ts b/lib/routes/iresearch/report.ts index 340e28f4f681..063f7f7dd8ca 100644 --- a/lib/routes/iresearch/report.ts +++ b/lib/routes/iresearch/report.ts @@ -214,7 +214,7 @@ export const handler = async (ctx: Context): Promise => { const idObj = idOptions.find((option) => option.label === paramId || option.value === paramId); const type: number = typeObj.value; - const id: string | undefined = idObj ? String(idObj.value) : undefined; + const id: string | undefined = idObj?.value; const baseUrl = 'https://www.iresearch.com.cn'; const imageBaseUrl = 'https://pic.iresearch.cn'; diff --git a/lib/routes/jimmyspa/news.ts b/lib/routes/jimmyspa/news.ts index da06d566a7b2..ef5ca382f3ef 100644 --- a/lib/routes/jimmyspa/news.ts +++ b/lib/routes/jimmyspa/news.ts @@ -61,7 +61,7 @@ async function handler(ctx) { const image = $$('a.news_card .card_img img').prop('src') || ''; const link = $$('a.news_card').prop('data-route'); const itemdate = $$('a.news_card div.date').html() || ''; - const pubDate = convertHtmlDateToStandardFormat(itemdate.toString()); + const pubDate = convertHtmlDateToStandardFormat(itemdate); const description = renderDescription({ images: image diff --git a/lib/routes/meta/ai-blog.ts b/lib/routes/meta/ai-blog.ts index fd5f34926f9b..fc01d433a41a 100644 --- a/lib/routes/meta/ai-blog.ts +++ b/lib/routes/meta/ai-blog.ts @@ -94,7 +94,7 @@ async function handler(ctx) { lsd: server.LSD.token, // jazoest: '', __spin_r: String(server.SiteData.__spin_r || ''), - __spin_b: String(server.SiteData.__spin_b || 'trunk'), + __spin_b: server.SiteData.__spin_b || 'trunk', __spin_t: spinT, fb_api_caller_class: 'RelayModern', fb_api_req_friendly_name: 'MetaAIBlogRecentPostSearchQuery', diff --git a/lib/routes/nankai/yzb.ts b/lib/routes/nankai/yzb.ts index 52008cfcf9da..f96a56e8c687 100644 --- a/lib/routes/nankai/yzb.ts +++ b/lib/routes/nankai/yzb.ts @@ -63,7 +63,7 @@ export const route: Route = { const items = await Promise.all( list.map((item) => - cache.tryGet(item.link!.toString(), async () => { + cache.tryGet(item.link!, async () => { const { data: response } = await got(item.link); const $ = load(response); item.description = $('.read').html(); diff --git a/lib/routes/nintendo/templates/eshop-hk.tsx b/lib/routes/nintendo/templates/eshop-hk.tsx index be98813ec643..2837a2cf5138 100644 --- a/lib/routes/nintendo/templates/eshop-hk.tsx +++ b/lib/routes/nintendo/templates/eshop-hk.tsx @@ -252,7 +252,7 @@ export const renderEshopHkDescription = ({ host, attributes, description, galler ) : null} {jsonData?.copyright_text ? <>{jsonData.copyright_text} : null} - {jsonData?.screenshots ? jsonData.screenshots.map((screen, screenIndex) => screen.images?.map((image, imageIndex) => )) : null} + {jsonData?.screenshots ? jsonData.screenshots.map((screen, screenIndex) => screen.images?.map((image, imageIndex) => )) : null} ) : null} diff --git a/lib/routes/polymarket/leaderboard.ts b/lib/routes/polymarket/leaderboard.ts index 6bdef90b5e55..011e8586d830 100644 --- a/lib/routes/polymarket/leaderboard.ts +++ b/lib/routes/polymarket/leaderboard.ts @@ -49,8 +49,8 @@ async function handler(ctx) { title: `#${entry.rank} ${entry.userName || entry.proxyWallet}`, description: `

        Rank: #${entry.rank}

        -

        PnL: $${Number(entry.pnl).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

        -

        Volume: $${Number(entry.vol).toLocaleString()}

        +

        PnL: $${entry.pnl.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

        +

        Volume: $${entry.vol.toLocaleString()}

        ${entry.xUsername ? `

        X: @${entry.xUsername}

        ` : ''} ${entry.verifiedBadge ? '

        ✅ Verified

        ' : ''} ${entry.profileImage ? `${entry.userName || 'Trader'}` : ''} diff --git a/lib/routes/polymarket/positions.ts b/lib/routes/polymarket/positions.ts index de9aa1b94aef..2e430a4b6153 100644 --- a/lib/routes/polymarket/positions.ts +++ b/lib/routes/polymarket/positions.ts @@ -59,11 +59,11 @@ async function handler(ctx) { title: pos.title || `Position #${pos.conditionId.slice(0, 8)}`, description: `

        Outcome: ${pos.outcome || `#${pos.outcomeIndex}`}

        -

        Size: ${Number(pos.size).toLocaleString()}

        -

        Avg Price: $${Number(pos.avgPrice).toFixed(4)}

        -

        Current Price: $${Number(pos.curPrice).toFixed(4)}

        -

        Current Value: $${Number(pos.currentValue).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

        -

        PnL: ${pos.cashPnl >= 0 ? '+' : ''}$${Number(pos.cashPnl).toFixed(2)} (${pos.percentPnl >= 0 ? '+' : ''}${Number(pos.percentPnl).toFixed(1)}%)

        +

        Size: ${pos.size.toLocaleString()}

        +

        Avg Price: $${pos.avgPrice.toFixed(4)}

        +

        Current Price: $${pos.curPrice.toFixed(4)}

        +

        Current Value: $${pos.currentValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

        +

        PnL: ${pos.cashPnl >= 0 ? '+' : ''}$${pos.cashPnl.toFixed(2)} (${pos.percentPnl >= 0 ? '+' : ''}${pos.percentPnl.toFixed(1)}%)

        ${pos.title || 'Position'} `, link: pos.eventSlug ? `https://polymarket.com/event/${pos.eventSlug}` : pos.slug ? `https://polymarket.com/event/${pos.slug}` : 'https://polymarket.com', diff --git a/lib/routes/polymarket/series.ts b/lib/routes/polymarket/series.ts index 98579807d7d1..e64d34e874de 100644 --- a/lib/routes/polymarket/series.ts +++ b/lib/routes/polymarket/series.ts @@ -83,8 +83,8 @@ async function handler(ctx) { title: series.title, description: ` ${series.description ? `

        ${series.description}

        ` : ''} -

        Volume: $${Number(series.volume || 0).toLocaleString()}

        -

        Liquidity: $${Number(series.liquidity || 0).toLocaleString()}

        +

        Volume: $${(series.volume || 0).toLocaleString()}

        +

        Liquidity: $${(series.liquidity || 0).toLocaleString()}

        ${series.image ? `${series.title}` : ''} `, link: `https://polymarket.com/series/${series.slug}`, diff --git a/lib/routes/polymarket/utils.ts b/lib/routes/polymarket/utils.ts index 4af4b5a8835a..b78ba184dd84 100644 --- a/lib/routes/polymarket/utils.ts +++ b/lib/routes/polymarket/utils.ts @@ -14,7 +14,7 @@ export function formatEventDescription(event: Event): string { return ` ${event.description ? `

        ${event.description}

        ` : ''} -

        Volume: $${Number(event.volume || 0).toLocaleString()}

        +

        Volume: $${(event.volume || 0).toLocaleString()}

        ${marketsHtml ? `

        Markets:

          ${marketsHtml}
        ` : ''} ${event.image ? `${event.title}` : ''} `; diff --git a/lib/routes/qingting/podcast.ts b/lib/routes/qingting/podcast.ts index 34ddd1c17112..284c59839884 100644 --- a/lib/routes/qingting/podcast.ts +++ b/lib/routes/qingting/podcast.ts @@ -37,7 +37,7 @@ export const route: Route = { function getMediaUrl(channelId: string, mediaId: string) { const path = `/audiostream/redirect/${channelId}/${mediaId}?access_token=&device_id=MOBILESITE&qingting_id=${qingtingId}&t=${Date.now()}`; - const sign = crypto.createHmac('md5', 'fpMn12&38f_2e').update(path).digest('hex').toString(); + const sign = crypto.createHmac('md5', 'fpMn12&38f_2e').update(path).digest('hex'); return `https://audio.qingting.fm${path}&sign=${sign}`; } diff --git a/lib/routes/showstart/service.ts b/lib/routes/showstart/service.ts index 8a610bb98172..ec8dc7b1bf13 100644 --- a/lib/routes/showstart/service.ts +++ b/lib/routes/showstart/service.ts @@ -179,13 +179,13 @@ async function fetchStyleList(keyword = '') { async function fetchDictionary(cityCode: string, showStyle: string) { const resp = await fetchParams(); - const target = resp.result.find((item) => String(item.cityCode) === cityCode); + const target = resp.result.find((item) => item.cityCode === cityCode); if (!target) { return {}; } return { cityName: target.cityName, - showName: target.styles.find((item) => String(item.key) === showStyle)?.showName, + showName: target.styles.find((item) => item.key === showStyle)?.showName, }; } diff --git a/lib/routes/skeb/utils.tsx b/lib/routes/skeb/utils.tsx index ca88f6158b5a..1c54b395761b 100644 --- a/lib/routes/skeb/utils.tsx +++ b/lib/routes/skeb/utils.tsx @@ -56,7 +56,7 @@ interface Creator { } export function processWork(work: Work): DataItem | null { - if (!work || work.private === true) { + if (!work || work.private) { return null; } diff --git a/lib/routes/syosetu/ranking-isekai.ts b/lib/routes/syosetu/ranking-isekai.ts index a22b98f50405..d2cf60ea7397 100644 --- a/lib/routes/syosetu/ranking-isekai.ts +++ b/lib/routes/syosetu/ranking-isekai.ts @@ -74,7 +74,7 @@ export async function handleIsekaiRanking(type: string, limit: number): Promise< .toSorted((a, b) => (b[pointField] || 0) - (a[pointField] || 0)) .map((novel, index) => ({ title: `#${index + 1} ${novel.title}`, - link: `https://ncode.syosetu.com/${String(novel.ncode).toLowerCase()}`, + link: `https://ncode.syosetu.com/${novel.ncode.toLowerCase()}`, description: renderDescription({ novel }), author: novel.writer, category: novel.keyword.split(/[\s/\u{FF0F}]/u).filter(Boolean), diff --git a/lib/routes/syosetu/ranking-r18.ts b/lib/routes/syosetu/ranking-r18.ts index 3ef08a17f7d4..7a798fb53e6b 100644 --- a/lib/routes/syosetu/ranking-r18.ts +++ b/lib/routes/syosetu/ranking-r18.ts @@ -168,7 +168,7 @@ async function handler(ctx: Context): Promise { const items = result.values.map((novel, index) => ({ title: `#${index + 1} ${novel.title}`, - link: `https://novel18.syosetu.com/${String(novel.ncode).toLowerCase()}`, + link: `https://novel18.syosetu.com/${novel.ncode.toLowerCase()}`, description: renderDescription({ novel }), author: novel.writer, category: novel.keyword.split(/[\s/\u{FF0F}]/u).filter(Boolean), diff --git a/lib/routes/syosetu/ranking.ts b/lib/routes/syosetu/ranking.ts index 19ed7248b415..cddf8eb3d13c 100644 --- a/lib/routes/syosetu/ranking.ts +++ b/lib/routes/syosetu/ranking.ts @@ -255,7 +255,7 @@ async function handler(ctx: Context): Promise { const items = result.values.map((novel, index) => ({ title: `#${index + 1} ${novel.title}`, - link: `https://ncode.syosetu.com/${String(novel.ncode).toLowerCase()}`, + link: `https://ncode.syosetu.com/${novel.ncode.toLowerCase()}`, description: renderDescription({ novel }), author: novel.writer, category: novel.keyword.split(/[\s/\u{FF0F}]/u).filter(Boolean), diff --git a/lib/routes/telegram/tglib/client.ts b/lib/routes/telegram/tglib/client.ts index 1dc5c86811f9..b4a7b1171720 100644 --- a/lib/routes/telegram/tglib/client.ts +++ b/lib/routes/telegram/tglib/client.ts @@ -18,7 +18,7 @@ export async function getClient(authParams?: UserAuthParams, session?: string) { if (client) { return client; } - const apiId = Number(config.telegram.apiId ?? 4); + const apiId = config.telegram.apiId ?? 4; const apiHash = config.telegram.apiHash ?? '014b35b6184100b085b0d0572f9b5103'; const stringSession = new StringSession(session ?? config.telegram.session); @@ -26,12 +26,12 @@ export async function getClient(authParams?: UserAuthParams, session?: string) { connectionRetries: Infinity, autoReconnect: true, retryDelay: 3000, - maxConcurrentDownloads: Number(config.telegram.maxConcurrentDownloads ?? 10), + maxConcurrentDownloads: config.telegram.maxConcurrentDownloads ?? 10, proxy: config.telegram.proxy?.host && config.telegram.proxy.port && config.telegram.proxy.secret ? { ip: config.telegram.proxy.host, - port: Number(config.telegram.proxy.port), + port: config.telegram.proxy.port, MTProxy: true, secret: config.telegram.proxy.secret, } diff --git a/lib/routes/ycwb/index.tsx b/lib/routes/ycwb/index.tsx index a95b5fe467c5..d1b693f314bf 100644 --- a/lib/routes/ycwb/index.tsx +++ b/lib/routes/ycwb/index.tsx @@ -119,7 +119,7 @@ async function handler(ctx) { return { title: `羊城晚报金羊网 - ${nodeName}`, - link: String(nodeLink === '' ? 'https://www.ycwb.com/' : nodeLink), + link: nodeLink === '' ? 'https://www.ycwb.com/' : nodeLink, item: items, }; } diff --git a/lib/routes/zsxq/utils.ts b/lib/routes/zsxq/utils.ts index 0fbaf17cfa4e..9520715aa898 100644 --- a/lib/routes/zsxq/utils.ts +++ b/lib/routes/zsxq/utils.ts @@ -48,7 +48,7 @@ export function generateTopicDataItem(topics: Topic[]): DataItem[] { case 'q&a': title = topic.question?.text?.split('\n', 1)[0] ?? '问答'; description = parseTopicContent(topic.question?.text, topic.question?.images); - description = `
        ${String(topic.question?.owner?.name ?? '匿名用户')} 提问:${description}
        `; + description = `
        ${topic.question?.owner?.name ?? '匿名用户'} 提问:${description}
        `; if (topic.answered) { description += '
        ' + topic.answer?.owner.name + ' 回答:

        '; description += parseTopicContent(topic.answer?.text, topic.answer?.images); diff --git a/lib/utils/cache/memory.ts b/lib/utils/cache/memory.ts index 16020356b993..c6bea3b61fb7 100644 --- a/lib/utils/cache/memory.ts +++ b/lib/utils/cache/memory.ts @@ -18,11 +18,7 @@ export default { }, get: (key: string, refresh = true) => { if (key && status.available && clients.memoryCache) { - let value = clients.memoryCache.get(key, { updateAgeOnGet: refresh }); - if (value) { - value += ''; - } - return value ?? null; + return clients.memoryCache.get(key, { updateAgeOnGet: refresh }) ?? null; } return null; }, diff --git a/lib/utils/cache/redis.ts b/lib/utils/cache/redis.ts index 2aec26661cb0..4c46c28e8455 100644 --- a/lib/utils/cache/redis.ts +++ b/lib/utils/cache/redis.ts @@ -35,7 +35,8 @@ export default { get: async (key: string, refresh = true) => { if (key && status.available && clients.redisClient) { const cacheTtlKey = getCacheTtlKey(key); - let [value, cacheTtl] = await clients.redisClient.mget(key, cacheTtlKey); + const [value, cacheTtlValue] = await clients.redisClient.mget(key, cacheTtlKey); + let cacheTtl = cacheTtlValue; if (value && refresh) { if (cacheTtl) { clients.redisClient.expire(cacheTtlKey, cacheTtl); @@ -46,7 +47,6 @@ export default { // redisClient.set(cacheTtlKey, cacheTtl, 'EX', cacheTtl); } clients.redisClient.expire(key, cacheTtl); - value += ''; } return value || ''; } diff --git a/package.json b/package.json index 151615fa1122..01a6fe4c7214 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "oauth-1.0a": "2.2.6", "ofetch": "1.5.1", "otplib": "13.5.0", - "oxc-parser": "0.150.0", + "oxc-parser": "0.151.0", "p-map": "7.0.8", "pac-proxy-agent": "9.1.0", "patchright": "1.63.0", @@ -155,7 +155,7 @@ "@cloudflare/vitest-plugin": "1.2.1", "@cloudflare/workers-types": "5.20260922.1", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.83.0", + "@oxlint/plugins": "1.85.0", "@stylistic/eslint-plugin": "5.10.0", "@types/crypto-js": "4.2.2", "@types/eslint": "9.6.1", @@ -191,9 +191,9 @@ "mockdate": "3.0.5", "msw": "2.15.0", "node-network-devtools": "1.0.30", - "oxfmt": "0.68.0", - "oxlint": "1.83.0", - "oxlint-plugin-eslint": "1.83.0", + "oxfmt": "0.70.0", + "oxlint": "1.85.0", + "oxlint-plugin-eslint": "1.85.0", "oxlint-tsgolint": "7.0.2002", "remark": "15.0.1", "remark-gfm": "4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b38bbf23a37..9a76a04a712d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,8 +197,8 @@ importers: specifier: 13.5.0 version: 13.5.0 oxc-parser: - specifier: 0.150.0 - version: 0.150.0 + specifier: 0.151.0 + version: 0.151.0 p-map: specifier: 7.0.8 version: 7.0.8 @@ -315,8 +315,8 @@ importers: specifier: 10.0.1 version: 10.0.1(eslint@10.11.0(jiti@2.7.0)) '@oxlint/plugins': - specifier: 1.83.0 - version: 1.83.0 + specifier: 1.85.0 + version: 1.85.0 '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.11.0(jiti@2.7.0)) @@ -423,14 +423,14 @@ importers: specifier: 1.0.30 version: 1.0.30(undici@8.10.2)(utf-8-validate@5.0.10) oxfmt: - specifier: 0.68.0 - version: 0.68.0 + specifier: 0.70.0 + version: 0.70.0 oxlint: - specifier: 1.83.0 - version: 1.83.0(oxlint-tsgolint@7.0.2002) + specifier: 1.85.0 + version: 1.85.0(oxlint-tsgolint@7.0.2002) oxlint-plugin-eslint: - specifier: 1.83.0 - version: 1.83.0 + specifier: 1.85.0 + version: 1.85.0 oxlint-tsgolint: specifier: 7.0.2002 version: 7.0.2002 @@ -1818,106 +1818,106 @@ packages: '@otplib/uri@13.5.0': resolution: {integrity: sha512-LsL1hqTEgJHY40U2eb/Qp6OR1tKkNGwHQTrqqjIuJj0cawGWkLEmeUFK1MHzWBQ4bgy5IdeB5Et7gIDSAH/CRw==} - '@oxc-parser/binding-android-arm-eabi@0.150.0': - resolution: {integrity: sha512-oQef2Zu4Prz1KLKznz3HqZzU9uVoA5PMoDZuuLmqms7hKmKSAPzlaMnLllJq3t+rgKmfJJ2siPrpZfFrW06btw==} + '@oxc-parser/binding-android-arm-eabi@0.151.0': + resolution: {integrity: sha512-QLfrdT2XmHMo8krBiKHtnTtU2ugxruwnIWMcIl5qpCRV9KPZk1s2YZzSHkIjzabLxG6/1yOoDLvqfS3TrJhfWA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.150.0': - resolution: {integrity: sha512-B6ofpoFiAUwIZ0MJ2IgHPvZK8FAtL4qzSZRwOkAKIfxEobE1mQC8nDdiFZj7pUaJiVUVCsfkMsBJKedzO8rZvA==} + '@oxc-parser/binding-android-arm64@0.151.0': + resolution: {integrity: sha512-APzs9QxkLGR85I6Slb93Onsd+9ZdR+PdKIy0g8oOuipiBRtXbEE3PW1QpRn3O2C9dCsfAC8+t7dWGRyByiTqJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.150.0': - resolution: {integrity: sha512-J+9IHKzx/bSz1JetOfD4zKXSK9sOm4/a7+0qomJODcTL6JsRXGeV/ZdkAPSIDydfFmWzYCraMlQEosSZEyBYkQ==} + '@oxc-parser/binding-darwin-arm64@0.151.0': + resolution: {integrity: sha512-uPj085g+2TcYKm4LrPOF6mU/ilb48ghAfLMA+YIxaSAasgDAXO0c5UMjRrB9L59A1aMU9fvPtCGoW9cov9UtFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.150.0': - resolution: {integrity: sha512-v6IPfcAcSYrBWXV5Tce1DmxDMXLhEYpIIWiRFPJopgCVscZdLaV4MRQOUxvL3usQlw+yrwYOjBFB9IwBx+jUiQ==} + '@oxc-parser/binding-darwin-x64@0.151.0': + resolution: {integrity: sha512-gqOyGZsEX9r8i37ONL2X8heUKCbTAY3UvaWlEJKPJYdgaYoIAXSuZrG27legT+0e4m0X/53RupoH6lfbxtMiMw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.150.0': - resolution: {integrity: sha512-AoR/4jD02HET0KO0yNT4nbTE+XJUxiO9jB1X/ycEjUE3WrIJ3IjV/Vf+gskhWLcqqeXfvNnkDtBR7epllAm88A==} + '@oxc-parser/binding-freebsd-x64@0.151.0': + resolution: {integrity: sha512-Uih9lYFoB9k87b8jZ2263b8/HC0AuCQo9qPfu14KjoDmC7AmGvo4pCLiuEVVHjQycUjTO2dNtG39MIGUuCOx3A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.150.0': - resolution: {integrity: sha512-A/hycCFjLUrCmLoL5O/vBp40ohlaXO1Ta3v5hqYZxicYFs129wZmgZJggcW4mYGYbZebQLhup+N8JjeQl8qwyw==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.151.0': + resolution: {integrity: sha512-BW9ryFV0V7h37xAEpb1rx+jI/ArFjXlSvJ2CaPwqxCj+kcJdDMvt5/PpWLRRk56UpNpxpq/5+puTUd0/4OPCZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.150.0': - resolution: {integrity: sha512-pbqahg1Pkz7J4RKmXm30s/iQu99hv64ayXCu8P4p75HcEH5azOdR4eGqiB6lFGkFR3mMpzaYsSKLkS8oJbjmeQ==} + '@oxc-parser/binding-linux-arm-musleabihf@0.151.0': + resolution: {integrity: sha512-68zJuPgiFpZ9FF5WgpJiE9cukBZVNv79Yod2VV/wJLaiws+abCEfcxwVEH0608a7E/0zOO1Pj2iY7UrnUS8ydg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.150.0': - resolution: {integrity: sha512-HV11aRbBQGwqv8bEo+K/6qr88uB4fEe1mhXncMhlVCrj+WpBSraxrUzHaPVmeQRU6x6x8v/3DuCbbo93rpp+fw==} + '@oxc-parser/binding-linux-arm64-gnu@0.151.0': + resolution: {integrity: sha512-s/CCfZQcqoRo4Ij7ML43Yhe+gJZU7zmudd6StA0T9WDFPLaiWMoBlPRmoS39jPpODxPwI/6yPoiyjKOMNzSqCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.150.0': - resolution: {integrity: sha512-k6pVkJqALwtEuP4zukVhGRhdIy4+ofChUIUUsAHHyqDZlNsknmel3JjbggrJiWGaes6h/dIcWmEXsKW4SmK9oQ==} + '@oxc-parser/binding-linux-arm64-musl@0.151.0': + resolution: {integrity: sha512-QSjn7WWZNGDTRlMT0SUGDHZNI9XOhGXWdqhL/FuDR/nh0MOqGQT8OHGBy95mZHne4WvoS0OgnUl/+55u07yBiQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.150.0': - resolution: {integrity: sha512-tEFg39mw/rHO5n8GK2DR4ZMFfsPQZtnQIPbsIpjoQRomJc/2tRfsCSmCT6gNit+NZGoeJG5aH9ATDH5bf0WnoQ==} + '@oxc-parser/binding-linux-ppc64-gnu@0.151.0': + resolution: {integrity: sha512-RnvhC+cOND1dsNqbsirtZa9IWVJCCnxIZxs7nAvW0ZFjqqofwKbR3+TNKPqx2eASRC6+b78rZMLiNbDeDUp8+w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.150.0': - resolution: {integrity: sha512-0JO7IFkoek6HqV589Menx3hJySNXi6quRhO4tq2P7kpEHKEXoDATnoPVUpn5B7gDH2KLkdo751N0uIrGJFCTCw==} + '@oxc-parser/binding-linux-riscv64-gnu@0.151.0': + resolution: {integrity: sha512-fpwItfDNVDgXOMV6GdMHD3QV3NdYWhwDH0E8BRuYNeQyUKchb8sNLvAQg+g1HxENvrYjiLqLWAafOja44/sLig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.150.0': - resolution: {integrity: sha512-7XPzREnyAS5wHU9aB+49Uaqgoo1gVCklHc3DRlc3fJy2tXD/eAJFN1QFz/crj+Ulup5P1+axNREF+/RGaB/IRA==} + '@oxc-parser/binding-linux-riscv64-musl@0.151.0': + resolution: {integrity: sha512-fKchmw5aUlZem7uZZfxsQAl53cFiGWrAKhwjYxvJFiBPQyC9CZ2P7Qhv9unDJSezChu6iJ6XGQWQnRUyLp7j5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.150.0': - resolution: {integrity: sha512-7n7ZxcbRWDFaTdyj8M8p0O9OfzoMKm8O6syBAIgcSutbOALq0Bb9G52Me+XH0anMLZV+NgXc726gqgG2q39vcQ==} + '@oxc-parser/binding-linux-s390x-gnu@0.151.0': + resolution: {integrity: sha512-vDtxikQV1AixMY5pFAW5tDtGHmoexjGpEgtmPBAF3tXvEubm9KucxV0CxtuAIGEZ1aGyvZ74Hfryb7Vvq52F3Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.150.0': - resolution: {integrity: sha512-Vx0GSA9ZCRTUiczoEQnIIyXkMvtEY8uc6IiwLQtMe5ci2sXPqjrry0Ek5fsPP1k2kCzkML3ow9UOOEgnEDi7Bw==} + '@oxc-parser/binding-linux-x64-gnu@0.151.0': + resolution: {integrity: sha512-JHB/TuPCUdnkMq9RXB7C/SZuauJUp1EYHi5DaH7ge7uNlCY4AOhc0QeWOjo2rj308+NjxgTwminglWpqlllAig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.150.0': - resolution: {integrity: sha512-ii4/9m3viDLssMnfXU7/Pni/3nYplApuGayceX4qsVGaqqQJCx3tHIH9M/HWIeSty5i8LjS21zPYT6lVIkwLxw==} + '@oxc-parser/binding-linux-x64-musl@0.151.0': + resolution: {integrity: sha512-rkoZa4Wv5LbUHdCJ+3vC9G68r49gaoaQPg52Jx7+aC60q3QHIiysrDHQlQ3hiIevsmDl0RgzD+Y389q/hb34WA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.150.0': - resolution: {integrity: sha512-ZtoxX5rez36ZWkwtiEhjkCPnlTPoQ2I7lIL0IYZ1yjxMYohbvoOjBmUIZzrf9W/HouONq0omIfTeCWEY+R380w==} + '@oxc-parser/binding-openharmony-arm64@0.151.0': + resolution: {integrity: sha512-zp74XxS9WHSp6ct8LEoTa87F5pJOjFPG0o7dg2YCXaKo0raxabpFMC7NTbefIzWmF9KLIuJF/Rg6CPIu6P/Aaw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -1926,20 +1926,20 @@ packages: resolution: {integrity: sha512-p2X0okYbIkav6eB5zwwVc6eFRycAO4GbGpBCf9IJCI2fUzwoxysWaMXIyZzrq6Pg4/LsEqFb0Vp6TKovK541vA==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} - '@oxc-parser/binding-win32-arm64-msvc@0.150.0': - resolution: {integrity: sha512-VqeRb5JX/bKYBrff7TUDvJyKY0914UzuCkuXIGxJS4FUmvMNfqkCbc7Sq90iMz9DlmAtlggwaBYQ9eg3h3ltiw==} + '@oxc-parser/binding-win32-arm64-msvc@0.151.0': + resolution: {integrity: sha512-xtqapASyiSTv6rdm29kKFoDsxTo1oCmMfhPLGCDbZQ0Y6sU4m7IPEH05NgNEs2SrTErBuldkvS6e0Aqzmu0BgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.150.0': - resolution: {integrity: sha512-EPqJfeZ4Pgg2BJSsCV8GozxV0FDltRzpVtGVa1r2noJu1iu+opOw3gtBWXwIo9odCT/MdFfyHxdy0/CRqs3OqA==} + '@oxc-parser/binding-win32-ia32-msvc@0.151.0': + resolution: {integrity: sha512-9s4L40gxkolXQx1Q675XEZqgI4j+7+os4kZAFHdrDQVapjB9J2iodub7A8VAZ0cGTfhZNLH4D9L26fwEyqu3fg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.150.0': - resolution: {integrity: sha512-n5YMzbqwPQqozbkrexi0lNZrUz5cnPHalYpFWe6Dau7AmKIWNo+y24IwzDuZEQtEdUHuKhXmMMih/MPjOI+CMw==} + '@oxc-parser/binding-win32-x64-msvc@0.151.0': + resolution: {integrity: sha512-temy3DJf08taii88l13vfs5Om/HWECLSAj5Ca7ANMvocrekAgCuPC4GeHLi0mJ0cmGPznfeaaj80wqQoPIRYPA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1947,6 +1947,9 @@ packages: '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + '@oxc-project/types@0.151.0': + resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==} cpu: [arm] @@ -2050,124 +2053,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.68.0': - resolution: {integrity: sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg==} + '@oxfmt/binding-android-arm-eabi@0.70.0': + resolution: {integrity: sha512-Xd7YO4/T2axEj6FTLcj4Why3mTBqFMg+x24xtorT4Lb2+1g82090GH0a/4U1m0pABGYiix2bq1pqkYrmV3f0Sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.68.0': - resolution: {integrity: sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q==} + '@oxfmt/binding-android-arm64@0.70.0': + resolution: {integrity: sha512-x9rlMYyKXdgKdYyUJzGsK1ZV8P4di/J32ipzcS6Jet6p9r9UAh28neXIMtdlSaJJycdi61Z4YkcLKLpk8ueFjg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.68.0': - resolution: {integrity: sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA==} + '@oxfmt/binding-darwin-arm64@0.70.0': + resolution: {integrity: sha512-IUTUPvrBVYy7POh4stXzRdz4IVC/1QSaviCWoyenSlOhGu0X9j5K07vCTM9biLjAA2Zs31l0Rj5vvRpj9n95wA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.68.0': - resolution: {integrity: sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg==} + '@oxfmt/binding-darwin-x64@0.70.0': + resolution: {integrity: sha512-vw745q870oTd6J517O24asoX4/E+eK0nxYIFoedSLqgJ+nI5En7+ZS82iZSHZ69zevQrnOXiyHP01dA+t8xD8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.68.0': - resolution: {integrity: sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw==} + '@oxfmt/binding-freebsd-x64@0.70.0': + resolution: {integrity: sha512-NO14EgSM9dFkcg+MfGPxvsKqXYs9LKaxPrOKXpv1R0rLokGGFDcCq6dBMq18dE4wlpFOovX0UZY2uh1P30O7QA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': - resolution: {integrity: sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q==} + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': + resolution: {integrity: sha512-139OEhHarj9CYoJ/i9gXlPv4KLBGtLj2toseOWYFf09QwlhklaZk+wW3aOvlqoeZtuayvkoSNVWra7WJ21s3VQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.68.0': - resolution: {integrity: sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA==} + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': + resolution: {integrity: sha512-GEh2PY3IWTE0M24eNhTduountANSbWyDmMnzFSQE/nGg/bjPugbUgiGuFu+xdqcQSd/HKSwH80/F2yVVD48yhA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.68.0': - resolution: {integrity: sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og==} + '@oxfmt/binding-linux-arm64-gnu@0.70.0': + resolution: {integrity: sha512-En5i+UJmZSPxuSf47F2Hl5YOzKB0bicQLnGQkeTCMQ35cWLtbrSwACJKfiLqRZrk05DwSnsJkhBRaM3OURtIaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.68.0': - resolution: {integrity: sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A==} + '@oxfmt/binding-linux-arm64-musl@0.70.0': + resolution: {integrity: sha512-WWOoV5W9Im3flVwOVrWn/2DUlOF8v5vcCip+kcNuaMpulRCh6nzzt1Su2vcL2F908YJIXNV3HvegbBHuyLwKHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.68.0': - resolution: {integrity: sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw==} + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': + resolution: {integrity: sha512-YUouneIqW+5n7aE8xx/zeZ6/utr/KH7oykcGoFyd8Uz8uh591T1oKlnoWA3BsRq/ZR42oY1w4MUYvS/0e/MQOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.68.0': - resolution: {integrity: sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA==} + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': + resolution: {integrity: sha512-iEnMf21S5aGVa4hViDGY8sAQ/AHyCu2JPyrQF8P06wtHhSkD1YJBeT4m/KiGewgf7+a5XCYSCRIPcRQa1xwEoQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.68.0': - resolution: {integrity: sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg==} + '@oxfmt/binding-linux-riscv64-musl@0.70.0': + resolution: {integrity: sha512-91Sdniaj20fQzyMeCxMDzTP4c9s4RB8dGQ308xHhDR0n6U7+1Xq7N9klE7mfXq8iV3lRmIGSXi5X23Hn/0XX/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.68.0': - resolution: {integrity: sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ==} + '@oxfmt/binding-linux-s390x-gnu@0.70.0': + resolution: {integrity: sha512-uUV30M6E+2TKKGMaKiwfeL4RZrviHXlUxsrYJ/jFBb+1EZy+pnFT+hF73eeWdzh5NqPOAnw0iiMAIqjqiLZPFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.68.0': - resolution: {integrity: sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ==} + '@oxfmt/binding-linux-x64-gnu@0.70.0': + resolution: {integrity: sha512-ivMcX6kNDPhqtbOaBt/ItFlLlTlXNHLgRuNmxP6Na6UuYXRT10llpJcAPbGeRgjjb3Qzv4jwPp3fB0hui50WNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.68.0': - resolution: {integrity: sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A==} + '@oxfmt/binding-linux-x64-musl@0.70.0': + resolution: {integrity: sha512-w+S+fERxYmlZSyZlJK/U292FjyBoH8cCEj21/tYJX6atX5kNSn+HDkhlFQKT2zcMwUW0uAtUL/bOrlwJZwqRdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.68.0': - resolution: {integrity: sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w==} + '@oxfmt/binding-openharmony-arm64@0.70.0': + resolution: {integrity: sha512-Zlom1Xkx257R8bk4ZI4zJsrGno2opknz1+5v5baka3nn4FPyvNSdh8JUL4CdN1S1OWRMvJ9UJQ+RfIqGGCUEfA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.68.0': - resolution: {integrity: sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w==} + '@oxfmt/binding-win32-arm64-msvc@0.70.0': + resolution: {integrity: sha512-FQgPW5R17vzt7cgrJ8eG/dqX00o2xHsqFeLfw4xzA9FRHpN/DjFo9YDonvIIXGxiEuS9F/jZPnGO+KHNKuCo4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.68.0': - resolution: {integrity: sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA==} + '@oxfmt/binding-win32-ia32-msvc@0.70.0': + resolution: {integrity: sha512-ZfZublNhZ+XBndMiXhkiLlPE+XyGRDa0CweeTL6t1fZypfCh1LTg7e5CvnOeTBunq15MskOcRempumSPGAaCQA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.68.0': - resolution: {integrity: sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA==} + '@oxfmt/binding-win32-x64-msvc@0.70.0': + resolution: {integrity: sha512-HlIZEn+WzLQL0DszNzldiRl/DPRCX5R0Vkt6qeUPR1YHwy52hZZo4x6HoTOVmKRP2wUiwPGtKsihNY/f8KRaBg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2202,130 +2205,130 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.83.0': - resolution: {integrity: sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA==} + '@oxlint/binding-android-arm-eabi@1.85.0': + resolution: {integrity: sha512-q2KO/Zso9UT+OMn0NF9ywn4E4t0MI3yxiDhNyhsQ7DyQJrC4FhFE4TXOi4bktFnOWXTMds8qZSbpv2XwRaNOBg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.83.0': - resolution: {integrity: sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg==} + '@oxlint/binding-android-arm64@1.85.0': + resolution: {integrity: sha512-SxLN3ALjoT9NNdvpjEevGeHvfzTAFrF0NBYB5tzK7/GtCKMze3j1e/m/X2ozqGj2U9hfGG/dg/OG8vpVK4PiDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.83.0': - resolution: {integrity: sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ==} + '@oxlint/binding-darwin-arm64@1.85.0': + resolution: {integrity: sha512-Y/Sup/J4f0f9UGsSd/xyCNTeWL+gepO63GBdEDAfue9nBsnk9zMmnIXx1O6b1V8C90vB5nucYNZ0pbMXAp8zJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.83.0': - resolution: {integrity: sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA==} + '@oxlint/binding-darwin-x64@1.85.0': + resolution: {integrity: sha512-ApOSNC04ynpDTwvBD+//0wyfODRSbEzvRoKpX8teffmc27z8AockwSNeMXGJXn5KP85eahDgR/2llICWLkzcnw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.83.0': - resolution: {integrity: sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w==} + '@oxlint/binding-freebsd-x64@1.85.0': + resolution: {integrity: sha512-bNrVrCOA/kHky3Tu79IXWXe5bhIgLXfUuUEDHlAGOHUk96MkvDZ1ecaQF19rwstrnaqfP1o9nBTqzIr9+ZHkUg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.83.0': - resolution: {integrity: sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw==} + '@oxlint/binding-linux-arm-gnueabihf@1.85.0': + resolution: {integrity: sha512-NUrzOJ1s/EqsVvfn2L/1D8Wro2LPIZUbihL8kOJLh5fEdGEN3rdOGUYq3HwnUIL8sjpoP+4N6RaGrgmMJnaMPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.83.0': - resolution: {integrity: sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA==} + '@oxlint/binding-linux-arm-musleabihf@1.85.0': + resolution: {integrity: sha512-UJXrAT3E/RWkEqXLIs2ehETja1qfgkPb+5gwLIIS+o/6cf+grHvoOXTa5997a/YNQfcJS0DRBTOfZt95cvOI1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.83.0': - resolution: {integrity: sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg==} + '@oxlint/binding-linux-arm64-gnu@1.85.0': + resolution: {integrity: sha512-lK40QLjI0HxigO7CjDDshEtfYIeiYS0020v5BHFPqN4uuQBQxd2K9LNom2dW15o9F1937quSCRVp4ZsVhdbYdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.83.0': - resolution: {integrity: sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew==} + '@oxlint/binding-linux-arm64-musl@1.85.0': + resolution: {integrity: sha512-c2zbdBwGKreHXwRx3gWBuFGJxLhxgsg6YlZ+3H+RgRusU/UEV9jNwJ3HGYK+nRo0LvBa7mt6Kj86xoVotUo8cw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.83.0': - resolution: {integrity: sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g==} + '@oxlint/binding-linux-ppc64-gnu@1.85.0': + resolution: {integrity: sha512-tlt/Hy8lZ97/lCPmCgw/B3k/mwh+BzaIPbPkldZEly7TwLmx0xe2CQcaW2g/rR0dOgS9JNGCZsMEqLhUNMGvaw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.83.0': - resolution: {integrity: sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw==} + '@oxlint/binding-linux-riscv64-gnu@1.85.0': + resolution: {integrity: sha512-3tNR9Xey82X0zKuY1d8hJ6Rc9gwRDurmqGLnQZa5xqOXy8/YyiqFXjAtugkKLY82obOlpK1eSiDRlgcNPuxtIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.83.0': - resolution: {integrity: sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw==} + '@oxlint/binding-linux-riscv64-musl@1.85.0': + resolution: {integrity: sha512-wbGRd5PqCcjkJFHhZuZ2OBSUQY9czlQsoA/cQQB9JK/L9mC5MQgGoKAh+xd8QjA5V+0D3j+Qd1lAWn1I8zlelA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.83.0': - resolution: {integrity: sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ==} + '@oxlint/binding-linux-s390x-gnu@1.85.0': + resolution: {integrity: sha512-3Sn0kSrE4DPZCWV/8o+n4x3aFZxI9ulMnkYlwCbJ8eUVkwRK2IerohE/A/z3SNbCwoPFOCJmGE5Avrq0rrvdvQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.83.0': - resolution: {integrity: sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw==} + '@oxlint/binding-linux-x64-gnu@1.85.0': + resolution: {integrity: sha512-JY2pxxYfB62bAGfejljVCqc44etItehPuAyaeSAdMuEMtwNA00ggMnS66lC1oIhos6oOXUkuU6mZ9bpFh3BqWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.83.0': - resolution: {integrity: sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ==} + '@oxlint/binding-linux-x64-musl@1.85.0': + resolution: {integrity: sha512-5k74vZ6qJBjBHEOlBk9B/iv68Yu0F1Afw/vvT2ar6OGCqEeXLaSjXz2n/IPCbhLG22UoKoYEJTzpYraRdcp6PA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.83.0': - resolution: {integrity: sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA==} + '@oxlint/binding-openharmony-arm64@1.85.0': + resolution: {integrity: sha512-GbAl5qt5TCkPLXTaIISZJnugrcBhra6rodcXc9jYt620UtdsTt71NlNmJmm0frxzFpd54x/G+MkitEJA8I/BoA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.83.0': - resolution: {integrity: sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg==} + '@oxlint/binding-win32-arm64-msvc@1.85.0': + resolution: {integrity: sha512-kjmws5MK0et2swk4ND85D7NVQyDHw162i6whtZDLUA/lo6FQyBZDcmMRCMcVZcNrAhIaftVb00x9ChGDOjjNJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.83.0': - resolution: {integrity: sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA==} + '@oxlint/binding-win32-ia32-msvc@1.85.0': + resolution: {integrity: sha512-eSsIJx9n4yxvOqYTZyPEMyEXRmE60XH7xGAU7i0Qbsn1lf6Za3CWJ9aRd82oSFKXaxhp+sA6/yMJVRIpLpna6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.83.0': - resolution: {integrity: sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q==} + '@oxlint/binding-win32-x64-msvc@1.85.0': + resolution: {integrity: sha512-pBebIPUpKKhWrhSMWhy8TdAZBewiXnfxmaAGxhzxM1068GagqFaTwgKlU6e+UyJ2sPR+VoHouhXuGJkQjsrDvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.83.0': - resolution: {integrity: sha512-qD64wr6SS1DL7YbJQ8WWjyUfFfrJsvAy9Vo0sG/PYUQy30w0uemN2h6j4xuvj4noyETDpzcGC20sv+FH7GrN5A==} + '@oxlint/plugins@1.85.0': + resolution: {integrity: sha512-qqHqML3ME3cxGgFvWJG8wwMnJc9ol5SMdtqkq0k8r7l0USmC+HRXYjzDacME4Dqkx4yqrUsBhj0JYvji8fHxJQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@pinojs/redact@0.4.0': @@ -5138,15 +5141,15 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxc-parser@0.150.0: - resolution: {integrity: sha512-zwajKw1GUa57IOdafIS7/yOObGF2NihwIfZfs5yJTRprMSkiwhWOc/k+n8bwnUPbHr2/XsjfBO8KyTPaW3VpWg==} + oxc-parser@0.151.0: + resolution: {integrity: sha512-cX++XpBDCtSe52KKH3rhbh6xIRidYPpyB2reI3azD4l+Y0ci4JZiUgVXyM/r9rrMnYZf9gdWi+9fvFl1wol7nA==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.68.0: - resolution: {integrity: sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ==} + oxfmt@0.70.0: + resolution: {integrity: sha512-IsHxZ4y0wQLLMhnrJblBJgZsLDzfULrJnAw5j/QqsTlMa/m3AqsbToi+W71uhBGaqlqq/PbbjvHc09TJwdv3Tw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5158,16 +5161,16 @@ packages: vite-plus: optional: true - oxlint-plugin-eslint@1.83.0: - resolution: {integrity: sha512-9v2O7rkCkT2MzaAoviunkZ+O/BjhjhHXZKg8P0iAPfLYfziQwTHTJWqlY/uGOPAzjdC+VffHcRliMgJkxIttgg==} + oxlint-plugin-eslint@1.85.0: + resolution: {integrity: sha512-oF8kTNV1+2d6Tb/VyQUAeiJLD+Rq5E4D65UJzcoy1rkmw1a08L0nJGDA6ypzlIe0uwjEgWBqZSeglh30MTHbrw==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@7.0.2002: resolution: {integrity: sha512-rQAj2RAQM57nCwTd3PhtnAkREGW2StqI47tzudNlJF8WwGommM7rVpeP4O1O8/ZiuDZ9f2GnTESzLHGTluwkvQ==} hasBin: true - oxlint@1.83.0: - resolution: {integrity: sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ==} + oxlint@1.85.0: + resolution: {integrity: sha512-bc26s97nuvPj1ViyPsqmKecVkUWFMEdtayO8MaQ6oiLfs1pj94cQlZZhrh4BPNlr9HQosjhIlwgZKsfcwmcNgg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7469,52 +7472,52 @@ snapshots: dependencies: '@otplib/core': 13.5.0 - '@oxc-parser/binding-android-arm-eabi@0.150.0': + '@oxc-parser/binding-android-arm-eabi@0.151.0': optional: true - '@oxc-parser/binding-android-arm64@0.150.0': + '@oxc-parser/binding-android-arm64@0.151.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.150.0': + '@oxc-parser/binding-darwin-arm64@0.151.0': optional: true - '@oxc-parser/binding-darwin-x64@0.150.0': + '@oxc-parser/binding-darwin-x64@0.151.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.150.0': + '@oxc-parser/binding-freebsd-x64@0.151.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.150.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.151.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.150.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.151.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.150.0': + '@oxc-parser/binding-linux-arm64-gnu@0.151.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.150.0': + '@oxc-parser/binding-linux-arm64-musl@0.151.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.150.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.151.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.150.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.151.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.150.0': + '@oxc-parser/binding-linux-riscv64-musl@0.151.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.150.0': + '@oxc-parser/binding-linux-s390x-gnu@0.151.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.150.0': + '@oxc-parser/binding-linux-x64-gnu@0.151.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.150.0': + '@oxc-parser/binding-linux-x64-musl@0.151.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.150.0': + '@oxc-parser/binding-openharmony-arm64@0.151.0': optional: true '@oxc-parser/binding-wasm32-wasip1@0.0.1': @@ -7523,17 +7526,19 @@ snapshots: '@emnapi/runtime': 2.0.0-alpha.3 '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) - '@oxc-parser/binding-win32-arm64-msvc@0.150.0': + '@oxc-parser/binding-win32-arm64-msvc@0.151.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.150.0': + '@oxc-parser/binding-win32-ia32-msvc@0.151.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.150.0': + '@oxc-parser/binding-win32-x64-msvc@0.151.0': optional: true '@oxc-project/types@0.150.0': {} + '@oxc-project/types@0.151.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.24.2': optional: true @@ -7595,61 +7600,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.68.0': + '@oxfmt/binding-android-arm-eabi@0.70.0': optional: true - '@oxfmt/binding-android-arm64@0.68.0': + '@oxfmt/binding-android-arm64@0.70.0': optional: true - '@oxfmt/binding-darwin-arm64@0.68.0': + '@oxfmt/binding-darwin-arm64@0.70.0': optional: true - '@oxfmt/binding-darwin-x64@0.68.0': + '@oxfmt/binding-darwin-x64@0.70.0': optional: true - '@oxfmt/binding-freebsd-x64@0.68.0': + '@oxfmt/binding-freebsd-x64@0.70.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.68.0': + '@oxfmt/binding-linux-arm64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.68.0': + '@oxfmt/binding-linux-arm64-musl@0.70.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.68.0': + '@oxfmt/binding-linux-riscv64-musl@0.70.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.68.0': + '@oxfmt/binding-linux-s390x-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.68.0': + '@oxfmt/binding-linux-x64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.68.0': + '@oxfmt/binding-linux-x64-musl@0.70.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.68.0': + '@oxfmt/binding-openharmony-arm64@0.70.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.68.0': + '@oxfmt/binding-win32-arm64-msvc@0.70.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.68.0': + '@oxfmt/binding-win32-ia32-msvc@0.70.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.68.0': + '@oxfmt/binding-win32-x64-msvc@0.70.0': optional: true '@oxlint-tsgolint/darwin-arm64@7.0.2002': @@ -7670,64 +7675,64 @@ snapshots: '@oxlint-tsgolint/win32-x64@7.0.2002': optional: true - '@oxlint/binding-android-arm-eabi@1.83.0': + '@oxlint/binding-android-arm-eabi@1.85.0': optional: true - '@oxlint/binding-android-arm64@1.83.0': + '@oxlint/binding-android-arm64@1.85.0': optional: true - '@oxlint/binding-darwin-arm64@1.83.0': + '@oxlint/binding-darwin-arm64@1.85.0': optional: true - '@oxlint/binding-darwin-x64@1.83.0': + '@oxlint/binding-darwin-x64@1.85.0': optional: true - '@oxlint/binding-freebsd-x64@1.83.0': + '@oxlint/binding-freebsd-x64@1.85.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.83.0': + '@oxlint/binding-linux-arm-gnueabihf@1.85.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.83.0': + '@oxlint/binding-linux-arm-musleabihf@1.85.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.83.0': + '@oxlint/binding-linux-arm64-gnu@1.85.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.83.0': + '@oxlint/binding-linux-arm64-musl@1.85.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.83.0': + '@oxlint/binding-linux-ppc64-gnu@1.85.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.83.0': + '@oxlint/binding-linux-riscv64-gnu@1.85.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.83.0': + '@oxlint/binding-linux-riscv64-musl@1.85.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.83.0': + '@oxlint/binding-linux-s390x-gnu@1.85.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.83.0': + '@oxlint/binding-linux-x64-gnu@1.85.0': optional: true - '@oxlint/binding-linux-x64-musl@1.83.0': + '@oxlint/binding-linux-x64-musl@1.85.0': optional: true - '@oxlint/binding-openharmony-arm64@1.83.0': + '@oxlint/binding-openharmony-arm64@1.85.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.83.0': + '@oxlint/binding-win32-arm64-msvc@1.85.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.83.0': + '@oxlint/binding-win32-ia32-msvc@1.85.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.83.0': + '@oxlint/binding-win32-x64-msvc@1.85.0': optional: true - '@oxlint/plugins@1.83.0': {} + '@oxlint/plugins@1.85.0': {} '@pinojs/redact@0.4.0': {} @@ -10659,29 +10664,29 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxc-parser@0.150.0: + oxc-parser@0.151.0: dependencies: - '@oxc-project/types': 0.150.0 + '@oxc-project/types': 0.151.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.150.0 - '@oxc-parser/binding-android-arm64': 0.150.0 - '@oxc-parser/binding-darwin-arm64': 0.150.0 - '@oxc-parser/binding-darwin-x64': 0.150.0 - '@oxc-parser/binding-freebsd-x64': 0.150.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.150.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.150.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.150.0 - '@oxc-parser/binding-linux-arm64-musl': 0.150.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.150.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.150.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.150.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.150.0 - '@oxc-parser/binding-linux-x64-gnu': 0.150.0 - '@oxc-parser/binding-linux-x64-musl': 0.150.0 - '@oxc-parser/binding-openharmony-arm64': 0.150.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.150.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.150.0 - '@oxc-parser/binding-win32-x64-msvc': 0.150.0 + '@oxc-parser/binding-android-arm-eabi': 0.151.0 + '@oxc-parser/binding-android-arm64': 0.151.0 + '@oxc-parser/binding-darwin-arm64': 0.151.0 + '@oxc-parser/binding-darwin-x64': 0.151.0 + '@oxc-parser/binding-freebsd-x64': 0.151.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.151.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.151.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.151.0 + '@oxc-parser/binding-linux-arm64-musl': 0.151.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.151.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.151.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.151.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.151.0 + '@oxc-parser/binding-linux-x64-gnu': 0.151.0 + '@oxc-parser/binding-linux-x64-musl': 0.151.0 + '@oxc-parser/binding-openharmony-arm64': 0.151.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.151.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.151.0 + '@oxc-parser/binding-win32-x64-msvc': 0.151.0 oxc-resolver@11.24.2: optionalDependencies: @@ -10705,31 +10710,31 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.68.0: + oxfmt@0.70.0: dependencies: tinypool: 2.1.2 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.68.0 - '@oxfmt/binding-android-arm64': 0.68.0 - '@oxfmt/binding-darwin-arm64': 0.68.0 - '@oxfmt/binding-darwin-x64': 0.68.0 - '@oxfmt/binding-freebsd-x64': 0.68.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.68.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.68.0 - '@oxfmt/binding-linux-arm64-gnu': 0.68.0 - '@oxfmt/binding-linux-arm64-musl': 0.68.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.68.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.68.0 - '@oxfmt/binding-linux-riscv64-musl': 0.68.0 - '@oxfmt/binding-linux-s390x-gnu': 0.68.0 - '@oxfmt/binding-linux-x64-gnu': 0.68.0 - '@oxfmt/binding-linux-x64-musl': 0.68.0 - '@oxfmt/binding-openharmony-arm64': 0.68.0 - '@oxfmt/binding-win32-arm64-msvc': 0.68.0 - '@oxfmt/binding-win32-ia32-msvc': 0.68.0 - '@oxfmt/binding-win32-x64-msvc': 0.68.0 - - oxlint-plugin-eslint@1.83.0: {} + '@oxfmt/binding-android-arm-eabi': 0.70.0 + '@oxfmt/binding-android-arm64': 0.70.0 + '@oxfmt/binding-darwin-arm64': 0.70.0 + '@oxfmt/binding-darwin-x64': 0.70.0 + '@oxfmt/binding-freebsd-x64': 0.70.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.70.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.70.0 + '@oxfmt/binding-linux-arm64-gnu': 0.70.0 + '@oxfmt/binding-linux-arm64-musl': 0.70.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-musl': 0.70.0 + '@oxfmt/binding-linux-s390x-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-musl': 0.70.0 + '@oxfmt/binding-openharmony-arm64': 0.70.0 + '@oxfmt/binding-win32-arm64-msvc': 0.70.0 + '@oxfmt/binding-win32-ia32-msvc': 0.70.0 + '@oxfmt/binding-win32-x64-msvc': 0.70.0 + + oxlint-plugin-eslint@1.85.0: {} oxlint-tsgolint@7.0.2002: optionalDependencies: @@ -10740,27 +10745,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2002 '@oxlint-tsgolint/win32-x64': 7.0.2002 - oxlint@1.83.0(oxlint-tsgolint@7.0.2002): + oxlint@1.85.0(oxlint-tsgolint@7.0.2002): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.83.0 - '@oxlint/binding-android-arm64': 1.83.0 - '@oxlint/binding-darwin-arm64': 1.83.0 - '@oxlint/binding-darwin-x64': 1.83.0 - '@oxlint/binding-freebsd-x64': 1.83.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.83.0 - '@oxlint/binding-linux-arm-musleabihf': 1.83.0 - '@oxlint/binding-linux-arm64-gnu': 1.83.0 - '@oxlint/binding-linux-arm64-musl': 1.83.0 - '@oxlint/binding-linux-ppc64-gnu': 1.83.0 - '@oxlint/binding-linux-riscv64-gnu': 1.83.0 - '@oxlint/binding-linux-riscv64-musl': 1.83.0 - '@oxlint/binding-linux-s390x-gnu': 1.83.0 - '@oxlint/binding-linux-x64-gnu': 1.83.0 - '@oxlint/binding-linux-x64-musl': 1.83.0 - '@oxlint/binding-openharmony-arm64': 1.83.0 - '@oxlint/binding-win32-arm64-msvc': 1.83.0 - '@oxlint/binding-win32-ia32-msvc': 1.83.0 - '@oxlint/binding-win32-x64-msvc': 1.83.0 + '@oxlint/binding-android-arm-eabi': 1.85.0 + '@oxlint/binding-android-arm64': 1.85.0 + '@oxlint/binding-darwin-arm64': 1.85.0 + '@oxlint/binding-darwin-x64': 1.85.0 + '@oxlint/binding-freebsd-x64': 1.85.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.85.0 + '@oxlint/binding-linux-arm-musleabihf': 1.85.0 + '@oxlint/binding-linux-arm64-gnu': 1.85.0 + '@oxlint/binding-linux-arm64-musl': 1.85.0 + '@oxlint/binding-linux-ppc64-gnu': 1.85.0 + '@oxlint/binding-linux-riscv64-gnu': 1.85.0 + '@oxlint/binding-linux-riscv64-musl': 1.85.0 + '@oxlint/binding-linux-s390x-gnu': 1.85.0 + '@oxlint/binding-linux-x64-gnu': 1.85.0 + '@oxlint/binding-linux-x64-musl': 1.85.0 + '@oxlint/binding-openharmony-arm64': 1.85.0 + '@oxlint/binding-win32-arm64-msvc': 1.85.0 + '@oxlint/binding-win32-ia32-msvc': 1.85.0 + '@oxlint/binding-win32-x64-msvc': 1.85.0 oxlint-tsgolint: 7.0.2002 p-cancelable@4.0.1: {} From 896cf2b4a677383b6079a04002f39f7ea0771522 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 26 Sep 2026 01:29:48 +0800 Subject: [PATCH 0925/1190] fix(route/bilibili): manga update drop wasm (#23391) --- lib/routes/bilibili/manga-update.ts | 174 ++++++-- lib/routes/bilibili/wasm-exec.ts | 661 ---------------------------- 2 files changed, 147 insertions(+), 688 deletions(-) delete mode 100644 lib/routes/bilibili/wasm-exec.ts diff --git a/lib/routes/bilibili/manga-update.ts b/lib/routes/bilibili/manga-update.ts index cae76c3ba73f..62957f59303b 100644 --- a/lib/routes/bilibili/manga-update.ts +++ b/lib/routes/bilibili/manga-update.ts @@ -1,5 +1,6 @@ +import { createDecipheriv } from 'node:crypto'; + import type { Route } from '@/types'; -import cache from '@/utils/cache'; import got from '@/utils/got'; export const route: Route = { @@ -25,56 +26,175 @@ export const route: Route = { handler, }; -// Based on https://github.com/SocialSisterYi/bilibili-API-collect/issues/1168#issuecomment-2620749895 -async function genReqSign(query, body) { - // Don't import on top-level to avoid a cyclic dependency - wasm-exec.js generated via `pnpm build`, which in turn needs wasm-exec.js to import routes correctly - const { Go } = await import('./wasm-exec'); +const ALPHA62 = 'br5NH9CxpvJzmEt3OqoF7jgWMIZ2UnASfc8K6XaLYylT1eVBGh4sdQkDwRP0ui'; +const B64_TABLES = ['UalVqQk8T7+5oJxC6MzIniWDrc3XERGN/wdLyp90mf2tHuvsgeSjZbBF1KAOPhY4', 'zI3XZb8T7+5opF1KJxUaBqQk90mf6MlVCEceSjAOPhY42RGN/wdLytHuvsgniWDr']; +const rotateLeft = (x: number, n: number) => ((x << n) | (x >>> (32 - n))) >>> 0; - // Cache the wasm binary as it's quite large (~2MB) - // Here the binary is saved as base64 as the cache stores strings - const wasmBufferBase64 = await cache.tryGet('bilibili-manga-wasm-20250208', async () => { - const wasmResp = await got('https://s1.hdslb.com/bfs/manga-static/manga-pc/6732b1bf426cfc634293.wasm', { - responseType: 'arrayBuffer', - }); - return Buffer.from(wasmResp.data).toString('base64'); - }); - const wasmBuffer = Buffer.from(wasmBufferBase64, 'base64'); +/** + * MD5-shaped block mixer with its own round function and rotation constants + */ +const processBlock = (block: Uint8Array, [a, b, c, d]: number[]) => { + for (let i = 0; i < 16; i++) { + const w = block[i * 4] | (block[i * 4 + 1] << 8) | (block[i * 4 + 2] << 16) | (block[i * 4 + 3] << 24); + a = (a + ((b & c) | (~b & d)) + w) >>> 0; + b = (b + (c ^ a ^ d) + w) >>> 0; + c = (c + ((b & a) | (~b & d)) + w) >>> 0; + d = (d + (c ^ a ^ b) + w) >>> 0; + a = rotateLeft(a, 5); + b = rotateLeft(b, 7); + c = rotateLeft(c, 3); + d = rotateLeft(d, 11); + } + return [a, b, c, d]; +}; + +/** + * Same as MD5 padding except a length of 55 mod 64 gets a full extra block + */ +const md5v2 = (data: Uint8Array) => { + const zeros = 64 - ((data.length + 9) % 64); + const buf = Buffer.alloc(data.length + 1 + zeros + 8); + buf.set(data); + buf[data.length] = 0x80; + buf.writeBigUInt64LE(BigInt(data.length) * 8n, buf.length - 8); + let state = [0xab_cd_ef_01, 0x12_34_56_78, 0xde_ad_be_ef, 0xfe_ed_ca_fe]; + for (let i = 0; i < buf.length; i += 64) { + state = processBlock(buf.subarray(i, i + 64), state); + } + const hex = state.map((w) => w.toString(16).padStart(8, '0')).join(''); + return Buffer.from([...hex].map((ch) => ALPHA62.codePointAt(ch.codePointAt(0)! % 62)!)); +}; + +/** + * RC4 with `S[i] ^= i` after each KSA swap and a +1 offset on the keystream index + */ +const xrc4 = (key: number[], data: Uint8Array) => { + const S = Uint8Array.from({ length: 256 }, (_, i) => i); + for (let i = 0, j = 0; i < 256; i++) { + j = (j + S[i] + key[i % key.length]) & 255; + [S[i], S[j]] = [S[j], S[i]]; + S[i] ^= i; + } + const out = Buffer.alloc(data.length); + for (let n = 0, i = 0, j = 0; n < data.length; n++) { + i = (i + 1) & 255; + j = (j + S[i]) & 255; + [S[i], S[j]] = [S[j], S[i]]; + out[n] = data[n] ^ S[(S[i] + S[j] + 1) & 255]; + } + return out; +}; - const go = new Go(); - const { instance } = await WebAssembly.instantiate(wasmBuffer, go.importObject); - go.run(instance); - if (void 0 === globalThis.genReqSign) { - throw new Error('WASM function not available'); +const b64V2Enc = (data: Uint8Array, variant: number) => { + const table = B64_TABLES[variant]; + let out = ''; + for (let i = 0; i < data.length; i += 3) { + const v = (data[i] << 16) | ((data[i + 1] ?? 0) << 8) | (data[i + 2] ?? 0); + for (let k = 0; k < 4; k++) { + out += table[(v >>> (18 - 6 * k)) & 63]; + } } + const rem = data.length % 3; + return rem ? out + '='.repeat(3 - rem) : out; +}; - const signature = globalThis.genReqSign(query, body, Date.now()); +// Based on https://github.com/SocialSisterYi/bilibili-API-collect/issues/1168#issuecomment-2620749895 +// https://s1.hdslb.com/bfs/manga-static/manga-pc/6732b1bf426cfc634293.wasm +export const genReqSign = (query: string, body: string, ts: number) => { + const params = new URLSearchParams(query); + const keys = [...new Set(params.keys())].filter((k) => k !== 'ultra_sign').toSorted((a, b) => Number(a > b) - Number(a < b)); + const acc = keys.map((k) => k + params.getAll(k).join('')).join(''); + const bts = BigInt(ts); + const h1 = md5v2(Buffer.from(acc + bts.toString(16))); + const h2 = md5v2(Buffer.from(body + (bts / 128n).toString(2))); + const encBody = b64V2Enc(xrc4([1, 3, 7], Buffer.from(body)), Number(bts % 2n)); + const h3 = md5v2(Buffer.from(encBody)); + const t = Number(bts & 0xff_ff_ff_ffn); + const buf = Buffer.from([ + 1, + Number((bts >> 23n) & 0xffn), + Number((bts >> 31n) & 0xffn), + Number((bts >> 15n) & 0xffn), + 7, + h1[13], + h1[23], + h2[11], + h2[7], + h3[23], + h3[19], + (t >>> 8) & 255, + (t >>> 24) & 255, + (t >>> 16) & 255, + 7, + 0, + 5, + 0, + 4, + 1, + 5, + 0, + 2, + 0, + 1, + 0, + 6, + 2, + 0, + 0, + ]); + // oxlint-disable-next-line unicorn/no-array-reduce + buf[29] = buf.subarray(0, 28).reduce((x, y) => x ^ y, 0); + const out = Buffer.from([buf[0], buf[1], 0, 0, 0, ...[22, 7, 17, 21, 16, 28, 15, 12, 4, 3, 14, 27, 10, 6, 11, 19, 24, 25, 26, 20, 23, 18, 8, 2].map((i) => buf[i])]); + const hA = md5v2(Buffer.concat([out, Buffer.from([h1[7], h2[29]])])); + const hB = md5v2(Buffer.concat([out, Buffer.from([h3[31]])])); + const sign = Buffer.concat([hA.subarray(0, 24), hB.subarray(0, 24)]); + // The 11 hex digits of the timestamp are spliced into fixed positions of the digest + const hex = bts.toString(16); + for (const [pos, i] of [41, 37, 23, 29, 13, 19, 5, 47, 7, 11, 17].entries()) { + sign[i] = hex.codePointAt(pos)!; + } + return sign.toString('latin1'); +}; - return signature.sign; -} +const decryptBytesData = (bytesData: string, buvid3: string, comicId: string) => { + const key = Buffer.from( + [...buvid3] + .filter((_, i) => i % 2 === 0) + .join('') + .slice(0, 18) + + 'web' + + comicId.slice(-3) + ); + const decipher = createDecipheriv('aes-192-cbc', key, key.subarray(0, 16)); + const plain = Buffer.concat([decipher.update(Buffer.from(bytesData, 'base64')), decipher.final()]); + return JSON.parse(plain.toString()); +}; async function handler(ctx) { const comic_id = ctx.req.param('comicid').startsWith('mc') ? ctx.req.param('comicid').replace('mc', '') : ctx.req.param('comicid'); const link = `https://manga.bilibili.com/detail/mc${comic_id}`; const spi_response = await got('https://api.bilibili.com/x/frontend/finger/spi'); + const buvid3 = spi_response.data.data.b_3; - const query = 'device=pc&platform=web&nov=25'; + const query = 'device=pc&platform=web&nov=27&a=810'; const body = JSON.stringify({ comic_id: Number(comic_id), }); - const ultraSign = await genReqSign(query, body); + const ultraSign = genReqSign(query, body, Date.now()); const response = await got({ method: 'POST', - url: `https://manga.bilibili.com/twirp/comic.v2.Comic/ComicDetail?${query}&ultra_sign=${ultraSign}`, + url: `https://manga.bilibili.com/twirp/comic.v1.Comic/ComicDetail?${query}&ultra_sign=${ultraSign}`, body, headers: { Referer: link, - Cookie: `buvid3=${spi_response.data.data.b_3}; buvid4=${spi_response.data.data.b_4}`, + Cookie: `buvid3=${buvid3}; buvid4=${spi_response.data.data.b_4}`, + 'x-bili-data-sn': '1E74C20E5720FBF3BB351965D7A9DFC1', }, }); - const data = response.data.data; + const data = decryptBytesData(response.data.bytesData, buvid3, comic_id); const author = data.author_name.join(', '); return { diff --git a/lib/routes/bilibili/wasm-exec.ts b/lib/routes/bilibili/wasm-exec.ts deleted file mode 100644 index a14b739bbafd..000000000000 --- a/lib/routes/bilibili/wasm-exec.ts +++ /dev/null @@ -1,661 +0,0 @@ -// @ts-nocheck https://github.com/golang/go/blob/master/lib/wasm/wasm_exec.js -// oxlint-disable unicorn/prefer-math-trunc -// oxlint-disable unicorn-js/no-this-outside-of-class -// oxlint-disable unicorn-js/no-array-from-fill -// oxlint-disable unicorn-js/no-global-object-property-assignment -// oxlint-disable unicorn-js/no-unnecessary-global-this -// oxlint-disable unicorn-js/no-undeclared-class-members -// oxlint-disable unicorn-js/prefer-array-from-map -// oxlint-disable unicorn-js/require-array-sort-compare -// oxlint-disable unicorn-js/prefer-short-arrow-method -// oxlint-disable unicorn-js/prefer-block-statement-over-iife -// oxlint-disable no-unused-vars -// oxlint-disable unicorn/consistent-function-scoping -/* eslint-disable prefer-rest-params */ -/* eslint-disable default-case */ -/* eslint-disable no-console */ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -'use strict'; - -(() => { - const enosys = () => { - const err = new Error('not implemented'); - err.code = 'ENOSYS'; - return err; - }; - - if (!globalThis.fs) { - let outputBuf = ''; - globalThis.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused - writeSync(fd, buf) { - outputBuf += decoder.decode(buf); - const nl = outputBuf.lastIndexOf('\n'); - if (nl !== -1) { - console.log(outputBuf.slice(0, nl)); - outputBuf = outputBuf.slice(nl + 1); - } - return buf.length; - }, - write(fd, buf, offset, length, position, callback) { - if (offset !== 0 || length !== buf.length || position !== null) { - callback(enosys()); - return; - } - const n = this.writeSync(fd, buf); - callback(null, n); - }, - chmod(path, mode, callback) { - callback(enosys()); - }, - chown(path, uid, gid, callback) { - callback(enosys()); - }, - close(fd, callback) { - callback(enosys()); - }, - fchmod(fd, mode, callback) { - callback(enosys()); - }, - fchown(fd, uid, gid, callback) { - callback(enosys()); - }, - fstat(fd, callback) { - callback(enosys()); - }, - fsync(fd, callback) { - callback(null); - }, - ftruncate(fd, length, callback) { - callback(enosys()); - }, - lchown(path, uid, gid, callback) { - callback(enosys()); - }, - link(path, link, callback) { - callback(enosys()); - }, - lstat(path, callback) { - callback(enosys()); - }, - mkdir(path, perm, callback) { - callback(enosys()); - }, - open(path, flags, mode, callback) { - callback(enosys()); - }, - read(fd, buffer, offset, length, position, callback) { - callback(enosys()); - }, - readdir(path, callback) { - callback(enosys()); - }, - readlink(path, callback) { - callback(enosys()); - }, - rename(from, to, callback) { - callback(enosys()); - }, - rmdir(path, callback) { - callback(enosys()); - }, - stat(path, callback) { - callback(enosys()); - }, - symlink(path, link, callback) { - callback(enosys()); - }, - truncate(path, length, callback) { - callback(enosys()); - }, - unlink(path, callback) { - callback(enosys()); - }, - utimes(path, atime, mtime, callback) { - callback(enosys()); - }, - }; - } - - if (!globalThis.process) { - globalThis.process = { - getuid() { - return -1; - }, - getgid() { - return -1; - }, - geteuid() { - return -1; - }, - getegid() { - return -1; - }, - getgroups() { - throw enosys(); - }, - pid: -1, - ppid: -1, - umask() { - throw enosys(); - }, - cwd() { - throw enosys(); - }, - chdir() { - throw enosys(); - }, - }; - } - - if (!globalThis.path) { - globalThis.path = { - resolve(...pathSegments) { - return pathSegments.join('/'); - }, - }; - } - - if (!globalThis.crypto) { - throw new Error('globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)'); - } - - if (!globalThis.performance) { - throw new Error('globalThis.performance is not available, polyfill required (performance.now only)'); - } - - if (!globalThis.TextEncoder) { - throw new Error('globalThis.TextEncoder is not available, polyfill required'); - } - - if (!globalThis.TextDecoder) { - throw new Error('globalThis.TextDecoder is not available, polyfill required'); - } - - const encoder = new TextEncoder(); - const decoder = new TextDecoder('utf-8'); - - globalThis.Go = class { - constructor() { - this.argv = ['js']; - this.env = {}; - this.exit = (code) => { - if (code !== 0) { - console.warn('exit code:', code); - } - }; - this._exitPromise = new Promise((resolve) => { - this._resolveExitPromise = resolve; - }); - this._pendingEvent = null; - this._scheduledTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const setInt64 = (addr, v) => { - this.mem.setUint32(addr + 0, v, true); - this.mem.setUint32(addr + 4, Math.floor(v / 4_294_967_296), true); - }; - - const getInt64 = (addr) => { - const low = this.mem.getUint32(addr + 0, true); - const high = this.mem.getInt32(addr + 4, true); - return low + high * 4_294_967_296; - }; - - const loadValue = (addr) => { - const f = this.mem.getFloat64(addr, true); - if (f === 0) { - return; - } - if (!Number.isNaN(f)) { - return f; - } - - const id = this.mem.getUint32(addr, true); - return this._values[id]; - }; - - const storeValue = (addr, v) => { - const nanHead = 0x7f_f8_00_00; - - if (typeof v === 'number' && v !== 0) { - if (Number.isNaN(v)) { - this.mem.setUint32(addr + 4, nanHead, true); - this.mem.setUint32(addr, 0, true); - return; - } - this.mem.setFloat64(addr, v, true); - return; - } - - if (v === undefined) { - this.mem.setFloat64(addr, 0, true); - return; - } - - let id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = this._values.length; - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 0; - switch (typeof v) { - case 'object': - if (v !== null) { - typeFlag = 1; - } - break; - case 'string': - typeFlag = 2; - break; - case 'symbol': - typeFlag = 3; - break; - case 'function': - typeFlag = 4; - break; - } - this.mem.setUint32(addr + 4, nanHead | typeFlag, true); - this.mem.setUint32(addr, id, true); - }; - - const loadSlice = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - return new Uint8Array(this._inst.exports.mem.buffer, array, len); - }; - - const loadSliceOfValues = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - const a = Array.from({ length: len }); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - }; - - const loadString = (addr) => { - const saddr = getInt64(addr + 0); - const len = getInt64(addr + 8); - return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); - }; - - const testCallExport = (a, b) => { - this._inst.exports.testExport0(); - return this._inst.exports.testExport(a, b); - }; - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - _gotest: { - add: (a, b) => a + b, - callExport: testCallExport, - }, - gojs: { - // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) - // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported - // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). - // This changes the SP, thus we have to update the SP used by the imported function. - - // func wasmExit(code int32) - 'runtime.wasmExit': (sp) => { - sp >>>= 0; - const code = this.mem.getInt32(sp + 8, true); - this.exited = true; - delete this._inst; - delete this._values; - delete this._goRefCounts; - delete this._ids; - delete this._idPool; - this.exit(code); - }, - - // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) - 'runtime.wasmWrite': (sp) => { - sp >>>= 0; - const fd = getInt64(sp + 8); - const p = getInt64(sp + 16); - const n = this.mem.getInt32(sp + 24, true); - globalThis.fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); - }, - - // func resetMemoryDataView() - 'runtime.resetMemoryDataView': (sp) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-useless-assignment - sp >>>= 0; - this.mem = new DataView(this._inst.exports.mem.buffer); - }, - - // func nanotime1() int64 - 'runtime.nanotime1': (sp) => { - sp >>>= 0; - setInt64(sp + 8, (timeOrigin + performance.now()) * 1_000_000); - }, - - // func walltime() (sec int64, nsec int32) - 'runtime.walltime': (sp) => { - sp >>>= 0; - const msec = Date.now(); - setInt64(sp + 8, msec / 1000); - this.mem.setInt32(sp + 16, (msec % 1000) * 1_000_000, true); - }, - - // func scheduleTimeoutEvent(delay int64) int32 - 'runtime.scheduleTimeoutEvent': (sp) => { - sp >>>= 0; - const id = this._nextCallbackTimeoutID; - this._nextCallbackTimeoutID++; - this._scheduledTimeouts.set( - id, - setTimeout( - () => { - this._resume(); - while (this._scheduledTimeouts.has(id)) { - // for some reason Go failed to register the timeout event, log and try again - // (temporary workaround for https://github.com/golang/go/issues/28975) - console.warn('scheduleTimeoutEvent: missed timeout event'); - this._resume(); - } - }, - getInt64(sp + 8) - ) - ); - this.mem.setInt32(sp + 16, id, true); - }, - - // func clearTimeoutEvent(id int32) - 'runtime.clearTimeoutEvent': (sp) => { - sp >>>= 0; - const id = this.mem.getInt32(sp + 8, true); - clearTimeout(this._scheduledTimeouts.get(id)); - this._scheduledTimeouts.delete(id); - }, - - // func getRandomData(r []byte) - 'runtime.getRandomData': (sp) => { - sp >>>= 0; - crypto.getRandomValues(loadSlice(sp + 8)); - }, - - // func finalizeRef(v ref) - 'syscall/js.finalizeRef': (sp) => { - sp >>>= 0; - const id = this.mem.getUint32(sp + 8, true); - this._goRefCounts[id]--; - if (this._goRefCounts[id] !== 0) { - return; - } - - const v = this._values[id]; - this._values[id] = null; - this._ids.delete(v); - this._idPool.push(id); - }, - - // func stringVal(value string) ref - 'syscall/js.stringVal': (sp) => { - sp >>>= 0; - storeValue(sp + 24, loadString(sp + 8)); - }, - - // func valueGet(v ref, p string) ref - 'syscall/js.valueGet': (sp) => { - sp >>>= 0; - const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 32, result); - }, - - // func valueSet(v ref, p string, x ref) - 'syscall/js.valueSet': (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); - }, - - // func valueDelete(v ref, p string) - 'syscall/js.valueDelete': (sp) => { - sp >>>= 0; - Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); - }, - - // func valueIndex(v ref, i int) ref - 'syscall/js.valueIndex': (sp) => { - sp >>>= 0; - storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); - }, - - // valueSetIndex(v ref, i int, x ref) - 'syscall/js.valueSetIndex': (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - 'syscall/js.valueCall': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const m = Reflect.get(v, loadString(sp + 16)); - const args = loadSliceOfValues(sp + 32); - const result = Reflect.apply(m, v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, result); - this.mem.setUint8(sp + 64, 1); - } catch (error) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, error); - this.mem.setUint8(sp + 64, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - 'syscall/js.valueInvoke': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.apply(v, undefined, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (error) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, error); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - 'syscall/js.valueNew': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.construct(v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (error) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, error); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueLength(v ref) int - 'syscall/js.valueLength': (sp) => { - sp >>>= 0; - setInt64(sp + 16, Number.parseInt(loadValue(sp + 8).length)); - }, - - // valuePrepareString(v ref) (ref, int) - 'syscall/js.valuePrepareString': (sp) => { - sp >>>= 0; - const str = encoder.encode(String(loadValue(sp + 8))); - storeValue(sp + 16, str); - setInt64(sp + 24, str.length); - }, - - // valueLoadString(v ref, b []byte) - 'syscall/js.valueLoadString': (sp) => { - sp >>>= 0; - const str = loadValue(sp + 8); - loadSlice(sp + 16).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - 'syscall/js.valueInstanceOf': (sp) => { - sp >>>= 0; - this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16) ? 1 : 0); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - 'syscall/js.copyBytesToGo': (sp) => { - sp >>>= 0; - const dst = loadSlice(sp + 8); - const src = loadValue(sp + 32); - if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - // func copyBytesToJS(dst ref, src []byte) (int, bool) - 'syscall/js.copyBytesToJS': (sp) => { - sp >>>= 0; - const dst = loadValue(sp + 8); - const src = loadSlice(sp + 16); - if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - debug: (value) => { - console.log(value); - }, - }, - }; - } - - async run(instance) { - if (!(instance instanceof WebAssembly.Instance)) { - throw new TypeError('Go.run: WebAssembly.Instance expected'); - } - this._inst = instance; - this.mem = new DataView(this._inst.exports.mem.buffer); - this._values = [ - // JS values that Go currently has references to, indexed by reference id - NaN, - 0, - null, - true, - false, - globalThis, - this, - ]; - this._goRefCounts = Array.from({ length: this._values.length }).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map([ - // mapping from JS values to reference ids - [0, 1], - [null, 2], - [true, 3], - [false, 4], - [globalThis, 5], - [this, 6], - ]); - this._idPool = []; // unused ids that have been garbage collected - this.exited = false; // whether the Go program has exited - - // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. - let offset = 4096; - - const strPtr = (str) => { - const ptr = offset; - const bytes = encoder.encode(str + '\0'); - new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); - offset += bytes.length; - if (offset % 8 !== 0) { - offset += 8 - (offset % 8); - } - return ptr; - }; - - const argc = this.argv.length; - - const argvPtrs: number[] = []; - for (const arg of this.argv) { - argvPtrs.push(strPtr(arg)); - } - argvPtrs.push(0); - - const keys = Object.keys(this.env).toSorted(); - for (const key of keys) { - argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); - } - argvPtrs.push(0); - - const argv = offset; - for (const ptr of argvPtrs) { - this.mem.setUint32(offset, ptr, true); - this.mem.setUint32(offset + 4, 0, true); - offset += 8; - } - - // The linker guarantees global data starts from at least wasmMinDataAddr. - // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. - const wasmMinDataAddr = 4096 + 8192; - if (offset >= wasmMinDataAddr) { - throw new Error('total length of command line and environment variables exceeds limit'); - } - - this._inst.exports.run(argc, argv); - if (this.exited) { - this._resolveExitPromise(); - } - await this._exitPromise; - } - - _resume() { - if (this.exited) { - throw new Error('Go program has already exited'); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - _makeFuncWrapper(id) { - // somehow avoiding aliasing this with an arrow function doesn't work - // oxlint-disable-next-line unicorn/no-this-assignment typescript/no-this-alias - const go = this; - return function () { - const event = { id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } - }; -})(); - -export const Go = globalThis.Go; From 43455d3528af24796b4daabb18f60821eebc5a86 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 26 Sep 2026 02:34:23 +0800 Subject: [PATCH 0926/1190] fix(docker): include oxc-parser asset for raw-transfer deserialization (#23392) --- scripts/docker/minify-docker.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/docker/minify-docker.js b/scripts/docker/minify-docker.js index 5ae4208a6b29..dd05949370a2 100644 --- a/scripts/docker/minify-docker.js +++ b/scripts/docker/minify-docker.js @@ -32,6 +32,18 @@ if (patchrightCoreFile) { console.log('Manually included patchright-core asset:', browsersJson); } } +// oxc-parser loads its raw-transfer deserializers with a template-literal require through createRequire(import.meta.url), +// which @vercel/nft does not expand. +// https://github.com/oxc-project/oxc/blob/crates_v0.151.0/napi/parser/src-js/raw-transfer/eager.js#L75-L77 +const oxcEagerFile = fileList.find((file) => file.endsWith('/oxc-parser/src-js/raw-transfer/eager.js')); +if (oxcEagerFile) { + const deserializeDir = oxcEagerFile.replace(/raw-transfer\/eager\.js$/, 'generated/deserialize'); + if (await fs.pathExists(path.join(projectRoot, deserializeDir))) { + fileList.push(deserializeDir); + console.log('Manually included oxc-parser asset:', deserializeDir); + } +} + console.log('Total files need to be copied (touchable files in node_modules/):', fileList.length); console.log('Start copying files, destination:', resultFolder); try { From 232c0a90fcbbb4ce82901dd72af181bd88d6f079 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 26 Sep 2026 03:32:50 +0800 Subject: [PATCH 0927/1190] fix(route/zhihu): use old api (#23393) --- lib/routes/zhihu/pin/utils.ts | 25 ++---- lib/routes/zhihu/zhuanlan.ts | 145 ++++++++++++++++------------------ 2 files changed, 75 insertions(+), 95 deletions(-) diff --git a/lib/routes/zhihu/pin/utils.ts b/lib/routes/zhihu/pin/utils.ts index c7dc3f7ed4fa..cf08fdd83076 100644 --- a/lib/routes/zhihu/pin/utils.ts +++ b/lib/routes/zhihu/pin/utils.ts @@ -13,21 +13,10 @@ const generateDescription = (target, description = '') => { case 'video': try { - description += `