diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td new file mode 100644 index 000000000000..280c46e6ae89 --- /dev/null +++ b/.github/VOUCHED.td @@ -0,0 +1,17 @@ +# Vouched contributors for this project. +# +# See https://github.com/mitchellh/vouch for details. +# +# Syntax: +# - One handle per line (without @), sorted alphabetically. +# - Optional platform prefix: platform:username (e.g., github:user). +# - Denounce with minus prefix: -username or -platform:username. +# - Optional details after a space following the handle. +-betterandbetterii impersonate as dvorak0, goestav, Kjasn, Loongphy, TonyRL, ttttmr and xbot. https://github.com/DIYgod/RSSHub/commit/9e6f84df79bc9efcfabb0ca0768d7fded6775a1a. +diygod +henryqw +hyoban +neverbehave +pseudoyu +tonyrl +zhenlonghe diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c970e488d7a4..073aae6491d2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -45,6 +45,10 @@ updates: typescript-eslint: patterns: - '@typescript-eslint/*' + vitest: + patterns: + - 'vitest' + - '@vitest/coverage-v8' - package-ecosystem: 'nix' directory: '/' diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 50f8024fd581..c5ba81619275 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -20,9 +20,9 @@ jobs: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - name: Use Node.js Active LTS - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 78371364a547..1b080a235164 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -14,15 +14,15 @@ jobs: if: github.event.sender.login != 'issuehunt-oss[bot]' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' - name: Install dependencies (pnpm) # import remark-parse and unified run: pnpm i - name: Generate feedback - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index c019b02ffb9e..1fa32652d63b 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@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: . tags: ${{ steps.image-name-ordinary.outputs.tags }} @@ -132,7 +132,7 @@ jobs: touch "${{ runner.temp }}/digests/ordinary/${digest#sha256:}" - name: Upload digest (ordinary version) - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: digests-ordinary-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/ordinary/* @@ -160,10 +160,10 @@ jobs: - name: Build and push Docker image (Chromium-bundled version) id: build-and-push-chromium - uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: . - build-args: PUPPETEER_SKIP_DOWNLOAD=0 + build-args: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 tags: ${{ steps.image-name-chromium-bundled.outputs.tags }} labels: ${{ steps.meta-chromium-bundled.outputs.labels }} platforms: ${{ matrix.platform }} @@ -187,7 +187,7 @@ jobs: touch "${{ runner.temp }}/digests/chromium/${digest#sha256:}" - name: Upload digest (Chromium-bundled version) - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: digests-chromium-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/chromium/* diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index cc8c740dbe92..63354d4fb459 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -23,17 +23,13 @@ jobs: GH_TOKEN: ${{ github.token }} # Best practice for scripts is to reference via ENV at runtime. Avoid using the expression syntax in the script content directly: PR_TARGET_REPO: ${{ github.repository }} - # If the PR is from a fork, prefix it with `:`, otherwise only the PR branch name is relevant: - PR_BRANCH: |- - ${{ - (github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login) - && format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) - || github.event.workflow_run.head_branch - }} + # The REST API `head` filter requires `:` format + PR_BRANCH: ${{ format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) }} # Query the PR number by repo + branch, then assign to step output: run: | - gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" \ - --json 'number' --jq '"number=\(.number)"' \ + gh api "repos/${PR_TARGET_REPO}/pulls" \ + --method GET -f head="${PR_BRANCH}" -f state=all \ + --jq '.[0] | "number=\(.number)"' \ >> "${GITHUB_OUTPUT}" - name: Fetch PR data via GitHub API @@ -46,9 +42,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' @@ -58,7 +54,7 @@ jobs: - name: Fetch affected routes id: fetch-route - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: PULL_REQUEST: ${{ steps.pr-data.outputs.data }} with: @@ -74,7 +70,7 @@ jobs: - name: Fetch Docker image if: (env.TEST_CONTINUE) - uses: dawidd6/action-download-artifact@8305c0f1062bb0d184d09ef4493ecb9288447732 # v20 + uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21 with: workflow: ${{ github.event.workflow_run.workflow_id }} run_id: ${{ github.event.workflow_run.id }} @@ -127,7 +123,7 @@ jobs: if: (env.TEST_CONTINUE) id: generate-feedback timeout-minutes: 10 - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: TEST_BASEURL: http://localhost:1200 TEST_ROUTES: ${{ steps.fetch-route.outputs.result }} @@ -171,17 +167,13 @@ jobs: GH_TOKEN: ${{ github.token }} # Best practice for scripts is to reference via ENV at runtime. Avoid using the expression syntax in the script content directly: PR_TARGET_REPO: ${{ github.repository }} - # If the PR is from a fork, prefix it with `:`, otherwise only the PR branch name is relevant: - PR_BRANCH: |- - ${{ - (github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login) - && format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) - || github.event.workflow_run.head_branch - }} + # The REST API `head` filter requires `:` format + PR_BRANCH: ${{ format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) }} # Query the PR number by repo + branch, then assign to step output: run: | - gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" \ - --json 'number' --jq '"number=\(.number)"' \ + gh api "repos/${PR_TARGET_REPO}/pulls" \ + --method GET -f head="${PR_BRANCH}" -f state=all \ + --jq '.[0] | "number=\(.number)\nauthor_association=\(.author_association)"' \ >> "${GITHUB_OUTPUT}" - name: Pull Request Labeler @@ -191,3 +183,11 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ steps.source-run-info.outputs.number }} labels: 'auto: DO NOT merge' + + - name: Close broken PR + if: ${{ steps.source-run-info.outputs.author_association != 'OWNER' && steps.source-run-info.outputs.author_association != 'COLLABORATOR' && steps.source-run-info.outputs.author_association != 'MEMBER' }} + uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0 + with: + actions: 'close-issue' + token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ steps.source-run-info.outputs.number }} diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 4fede1725eac..9c5f8724a06d 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -40,10 +40,10 @@ jobs: flavor: latest=true - name: Build Docker image - uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: . - build-args: PUPPETEER_SKIP_DOWNLOAD=0 # also test bundling Chromium + build-args: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 # also test bundling Chromium load: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -69,7 +69,7 @@ jobs: run: docker save rsshub:latest | zstdmt -o rsshub.tar.zst - name: Upload Docker image - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: docker-image path: rsshub.tar.zst diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 3c7e08335747..582c2161ada6 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -15,12 +15,13 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' - run: pnpm i + - run: npm run format:description - run: npm run format - name: Commit files run: | diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index d98c74af6f49..79bc948acd64 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -36,6 +36,49 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} trigger: '/wip' + vouch-manage: + name: Vouch Manage + if: ${{ startsWith(github.event.comment.body, 'vouch') || startsWith(github.event.comment.body, 'unvouch') || startsWith(github.event.comment.body, 'denounce') }} + runs-on: ubuntu-slim + timeout-minutes: 5 + permissions: + contents: write + issues: write + pull-requests: write + concurrency: + group: vouch-manage + cancel-in-progress: false + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - id: vouch + uses: mitchellh/vouch/action/manage-by-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + with: + issue-id: ${{ github.event.issue.number }} + comment-id: ${{ github.event.comment.id }} + roles: admin,maintain,write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Close PR if denounced + if: ${{ github.event.issue.pull_request && steps.vouch.outputs.status == 'denounced' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const prNumber = context.payload.issue.number; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: 'This pull request has been automatically closed.', + }); + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + state: 'closed', + }); + test-on-demand: name: Test route on demand if: startsWith(github.event.comment.body, '/test') @@ -68,20 +111,20 @@ jobs: ref: ${{ fromJson(steps.pr-data.outputs.data).head.ref }} - name: Install pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - name: Use Node.js Active LTS - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' - name: Install dependencies (pnpm) - run: pnpm i && pnpm rb && pnpx rebrowser-puppeteer browsers install chrome + run: pnpm i && pnpm rb && pnpm exec playwright install chromium - name: Fetch affected routes id: fetch-route - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: EVENT: ${{ toJson(github.event) }} with: @@ -109,7 +152,7 @@ jobs: - name: Generate feedback if: env.TEST_CONTINUE - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: TEST_BASEURL: http://localhost:1200 TEST_ROUTES: ${{ steps.fetch-route.outputs.result }} @@ -129,7 +172,7 @@ jobs: run: cat ${{ github.workspace }}/logs/combined.log - name: Upload Artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: logs path: logs diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b2e41d32d459..f6c519f58379 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,8 +21,8 @@ jobs: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' @@ -30,7 +30,7 @@ jobs: - name: Install oxlint to SARIF converter run: pnpm i -g oxlint-json-to-sarif - name: Lint - run: pnpm exec oxlint --type-aware + run: pnpm exec oxlint --type-aware --config=.oxlintrc.ci.json --format=json | oxlint-json-to-sarif > oxlint-results.sarif continue-on-error: true - name: Upload analysis results to GitHub @@ -39,7 +39,7 @@ jobs: sarif_file: oxlint-results.sarif wait-for-processing: true - name: Upload Artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: path: oxlint-results.sarif @@ -65,6 +65,26 @@ jobs: runs-on: ubuntu-slim timeout-minutes: 5 steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} + + vouch-check-pr: + name: Vouch check PR + if: ${{ github.event_name == 'pull_request_target' && github.event.action == 'opened' && github.repository == 'DIYgod/RSSHub' }} + permissions: + contents: read + issues: write + pull-requests: write + runs-on: ubuntu-slim + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Check if PR author is denounced + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { default: checkPr } = await import('${{ github.workspace }}/scripts/workflow/vouch/check-pr.mjs') + await checkPr({ github, context, core }) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index bfbf5fa31852..f1c95cc35f02 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -18,13 +18,13 @@ jobs: name: npm publish if: github.repository == 'DIYgod/RSSHub' runs-on: ubuntu-slim - timeout-minutes: 5 + timeout-minutes: 10 env: HUSKY: 0 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index 39db57eb3edd..213d6f9f951c 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -31,15 +31,12 @@ jobs: env: GH_TOKEN: ${{ github.token }} PR_TARGET_REPO: ${{ github.repository }} - PR_BRANCH: |- - ${{ - (github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login) - && format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) - || github.event.workflow_run.head_branch - }} + # The REST API `head` filter requires `:` format + PR_BRANCH: ${{ format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch) }} run: | - gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" \ - --json 'number' --jq '"number=\(.number)"' \ + gh api "repos/${PR_TARGET_REPO}/pulls" \ + --method GET -f head="${PR_BRANCH}" -f state=open \ + --jq '.[0] | "number=\(.number)"' \ >> "${GITHUB_OUTPUT}" - name: Check PR author diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index dbb88ca6ff90..33c12c4e41d3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -3,6 +3,7 @@ name: 'Close stale issues and PRs' on: schedule: - cron: '31 23 * * *' + workflow_dispatch: permissions: issues: write @@ -25,6 +26,19 @@ jobs: exempt-issue-labels: 'wait for upstream' exempt-pr-labels: 'wait for upstream' any-of-issue-labels: 'more data required' + + - name: Close Broken PRs + uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + with: + days-before-issue-stale: -1 + days-before-issue-close: -1 + days-before-pr-stale: 1 + days-before-pr-close: 0 + only-pr-labels: 'auto: DO NOT merge' + stale-pr-label: 'Stale' + remove-pr-stale-when-updated: true + close-pr-message: > + This PR is being auto-closed because it has a broken Docker build without any fixes. Push a fix and reopen when ready. lock: runs-on: ubuntu-slim steps: diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml index 6427d4c16c20..b65c471bb2c0 100644 --- a/.github/workflows/test-full-routes.yml +++ b/.github/workflows/test-full-routes.yml @@ -16,9 +16,9 @@ jobs: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install pnpm - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 - name: Use Node.js Active LTS - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 567b235ffdbb..24216edbcee6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,19 +27,20 @@ jobs: strategy: fail-fast: false matrix: - node-version: [latest, lts/*, lts/-1] + # playwright install hangs in node v26.1.0, https://github.com/microsoft/playwright/issues/40724 + node-version: [26.0.0, lts/*, lts/-1] name: Vitest on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - name: Install dependencies (pnpm) run: pnpm i - name: Run postinstall script for dependencies - run: pnpm rb && pnpx rebrowser-puppeteer browsers install chrome + run: pnpm rb && pnpm exec playwright install chromium - name: Build routes run: pnpm build - name: Build worker routes @@ -56,28 +57,28 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos as documented, but seems broken - puppeteer: + playwright: runs-on: ubuntu-latest timeout-minutes: 10 strategy: fail-fast: false matrix: - node-version: [latest, lts/*, lts/-1] + node-version: [26.0.0, lts/*, lts/-1] chromium: - name: bundled Chromium dependency: '' - environment: '{ "PUPPETEER_SKIP_DOWNLOAD": "0" }' + environment: '{ "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": "0" }' - name: Chromium from Ubuntu dependency: chromium-browser - environment: '{ "PUPPETEER_SKIP_DOWNLOAD": "1" }' + environment: '{ "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": "1" }' - name: Chrome from Google dependency: google-chrome-stable - environment: '{ "PUPPETEER_SKIP_DOWNLOAD": "1" }' - name: Vitest puppeteer on Node ${{ matrix.node-version }} with ${{ matrix.chromium.name }} + 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: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -85,11 +86,12 @@ jobs: run: pnpm i env: ${{ fromJSON(matrix.chromium.environment) }} - name: Run postinstall script for dependencies - run: pnpm rb && pnpx rebrowser-puppeteer browsers install chrome - env: ${{ fromJSON(matrix.chromium.environment) }} + run: pnpm rb - name: Build routes run: pnpm build - env: ${{ fromJSON(matrix.chromium.environment) }} + - name: Install bundled Chromium + if: ${{ matrix.chromium.dependency == '' }} + run: pnpm exec playwright install chromium - name: Install Chromium if: ${{ matrix.chromium.dependency != '' }} # 'chromium-browser' from Ubuntu APT repo is a dummy package. Its version (85.0.4183.83) means @@ -104,12 +106,13 @@ jobs: sudo tee /etc/apt/sources.list.d/google-chrome.list > /dev/null sudo apt-get update sudo apt-get install -yq --no-install-recommends ${{ matrix.chromium.dependency }} - - name: Test puppeteer + - name: Test Playwright run: | set -eux - export CHROMIUM_EXECUTABLE_PATH="$(which ${{ matrix.chromium.dependency }})" - pnpm run vitest puppeteer - env: ${{ fromJSON(matrix.chromium.environment) }} + if [ -n "${{ matrix.chromium.dependency }}" ]; then + export CHROMIUM_EXECUTABLE_PATH="$(which ${{ matrix.chromium.dependency }})" + fi + pnpm run vitest playwright all: runs-on: ubuntu-latest @@ -119,12 +122,12 @@ jobs: strategy: fail-fast: false matrix: - node-version: [24, 22, 20] + node-version: [26, 24, 22] name: Build radar and maintainer on Node ${{ matrix.node-version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 - - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + - uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -132,14 +135,14 @@ jobs: - name: Build radar and maintainer run: npm run build - name: Upload assets - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: generated-assets-${{ matrix.node-version }} path: assets/build/ automerge: if: github.triggering_actor == 'dependabot[bot]' && github.event_name == 'pull_request' - needs: [vitest, puppeteer, all] + needs: [vitest, playwright, all] runs-on: ubuntu-slim permissions: pull-requests: write diff --git a/.github/workflows/update-nix-hash.yml b/.github/workflows/update-nix-hash.yml index fb9ba223a0b6..ad058111ae61 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Nix - uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31.10.4 + uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31.10.6 with: nix_path: nixpkgs=channel:nixos-unstable - name: Cache Nix store diff --git a/.oxlintrc.ci.json b/.oxlintrc.ci.json new file mode 100644 index 000000000000..7a7509dc2253 --- /dev/null +++ b/.oxlintrc.ci.json @@ -0,0 +1,9 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "extends": ["./.oxlintrc.json"], + // extends doesn't extend ignorePatterns, oxc-project/oxc#10223, oxc-project/oxc#16079 + "ignorePatterns": ["**/coverage", "**/.vscode", "**/docker-compose.yml", "!.github", "assets/build", "lib/routes-deprecated", "lib/router.js", "dist", "dist-lib", "dist-worker"], + "options": { + "respectEslintDisableDirectives": false + } +} diff --git a/.oxlintrc.json b/.oxlintrc.json index 505d3386edbe..e44803a23c01 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -140,7 +140,6 @@ // "n/no-missing-import": "error", // "n/no-missing-require": "error", // "n/no-process-exit": "error", - "n/no-unpublished-bin": "error", // "n/no-unpublished-import": "error", // "n/no-unpublished-require": "error", "n/no-unsupported-features/es-builtins": "error", @@ -156,7 +155,7 @@ "unicorn/consistent-empty-array-spread": "error", "unicorn/consistent-existence-index-check": "error", // "unicorn/consistent-function-scoping": "error", - "unicorn-js/consistent-template-literal-escape": "error", // use jsPlugins + "unicorn/consistent-template-literal-escape": "error", "unicorn/empty-brace-spaces": "error", "unicorn/error-message": "error", "unicorn/escape-case": "error", @@ -498,6 +497,9 @@ "@stylistic/no-multiple-empty-lines": "error", "@stylistic/no-trailing-spaces": "error", + + "@stylistic/quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": "avoidEscape" }], + "@stylistic/rest-spread-spacing": "error", "@stylistic/semi": "error", "@stylistic/space-before-blocks": "error", diff --git a/.puppeteerrc.cjs b/.puppeteerrc.cjs deleted file mode 100644 index af09a79d0cbd..000000000000 --- a/.puppeteerrc.cjs +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('node:path'); - -/** - * @type {import("puppeteer").Configuration} - */ -module.exports = { - // Changes the cache location for Puppeteer. - cacheDirectory: path.join(__dirname, 'node_modules', '.cache', 'puppeteer'), -}; diff --git a/Dockerfile b/Dockerfile index 248d81f5c267..6683193b7ed6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,10 +21,10 @@ COPY ./patches /app/patches COPY ./pnpm-lock.yaml /app/ COPY ./package.json /app/ -# lazy install Chromium to avoid cache miss, only install production dependencies to minimize the image size +# Lazy install Chromium to avoid cache miss, only install production dependencies to minimize the image size. RUN \ set -ex && \ - export PUPPETEER_SKIP_DOWNLOAD=true && \ + export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true && \ pnpm install --frozen-lockfile && \ pnpm rb @@ -40,7 +40,7 @@ WORKDIR /ver COPY ./package.json /app/ RUN \ set -ex && \ - grep -Po '(?<="rebrowser-puppeteer": ")[^\s"]*(?=")' /app/package.json | tee /ver/.puppeteer_version && \ + grep -Po '(?<="playwright": ")[^\s"]*(?=")' /app/package.json | tee /ver/.playwright_version && \ grep -Po '(?<="@vercel/nft": ")[^\s"]*(?=")' /app/package.json | tee /ver/.nft_version && \ grep -Po '(?<="fs-extra": ")[^\s"]*(?=")' /app/package.json | tee /ver/.fs_extra_version @@ -88,30 +88,29 @@ FROM node:24-bookworm-slim AS chromium-downloader # Yeah, downloading Chromium never needs those dependencies below. WORKDIR /app -COPY ./.puppeteerrc.cjs /app/ -COPY --from=dep-version-parser /ver/.puppeteer_version /app/.puppeteer_version +COPY --from=dep-version-parser /ver/.playwright_version /app/.playwright_version ARG TARGETPLATFORM ARG USE_CHINA_NPM_REGISTRY=0 -ARG PUPPETEER_SKIP_DOWNLOAD=1 -# The official recommended way to use Puppeteer on x86(_64) is to use the bundled Chromium from Puppeteer: -# https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-workwith-chromium-vyyy +ARG PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 +# The official recommended way to use Playwright on x86(_64) is to use the bundled browser. RUN \ set -ex ; \ - if [ "$PUPPETEER_SKIP_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \ + if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \ if [ "$USE_CHINA_NPM_REGISTRY" = 1 ]; then \ npm config set registry https://registry.npmmirror.com && \ yarn config set registry https://registry.npmmirror.com && \ pnpm config set registry https://registry.npmmirror.com ; \ fi; \ echo 'Downloading Chromium...' && \ - unset PUPPETEER_SKIP_DOWNLOAD && \ + unset PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD && \ + export PLAYWRIGHT_BROWSERS_PATH=/app/node_modules/.cache/ms-playwright && \ corepack enable pnpm && \ - pnpm --allow-build=rebrowser-puppeteer add rebrowser-puppeteer@$(cat /app/.puppeteer_version) --save-prod && \ + pnpm --allow-build=playwright add playwright@$(cat /app/.playwright_version) --save-prod && \ pnpm rb && \ - pnpx rebrowser-puppeteer browsers install chrome ; \ + pnpm exec playwright install chromium ; \ else \ - mkdir -p /app/node_modules/.cache/puppeteer ; \ + mkdir -p /app/node_modules/.cache/ms-playwright ; \ fi; # --------------------------------------------------------------------------------------------------------------------- @@ -127,20 +126,17 @@ WORKDIR /app # install deps first to avoid cache miss or disturbing buildkit to build concurrently ARG TARGETPLATFORM -ARG PUPPETEER_SKIP_DOWNLOAD=1 -# https://pptr.dev/troubleshooting#chrome-headless-doesnt-launch-on-unix -# https://github.com/puppeteer/puppeteer/issues/7822 +ARG PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 +# https://playwright.dev/docs/docker#introduction # https://www.debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#noteworthy-obsolete-packages -# The official recommended way to use Puppeteer on arm/arm64 is to install Chromium from the distribution repositories: -# https://github.com/puppeteer/puppeteer/blob/07391bbf5feaf85c191e1aa8aa78138dce84008d/packages/puppeteer-core/src/node/BrowserFetcher.ts#L128-L131 -# Dependencies of puppeteer-real-browser: xvfb, procps +# On arm/arm64, install Chromium from the distribution repositories. RUN \ set -ex && \ apt-get update && \ apt-get install -yq --no-install-recommends \ dumb-init git curl \ ; \ - if [ "$PUPPETEER_SKIP_DOWNLOAD" = 0 ]; then \ + if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ]; then \ if [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \ apt-get install -yq --no-install-recommends \ ca-certificates fonts-liberation wget xdg-utils \ @@ -154,19 +150,16 @@ RUN \ && \ echo "CHROMIUM_EXECUTABLE_PATH=$(which chromium)" | tee /app/.env ; \ fi; \ - apt-get install -yq --no-install-recommends \ - xvfb procps \ - ; \ fi; \ rm -rf /var/lib/apt/lists/* -COPY --from=chromium-downloader /app/node_modules/.cache/puppeteer /app/node_modules/.cache/puppeteer +COPY --from=chromium-downloader /app/node_modules/.cache/ms-playwright /app/node_modules/.cache/ms-playwright RUN \ set -ex && \ - if [ "$PUPPETEER_SKIP_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \ + if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \ echo 'Verifying Chromium installation...' && \ - _chrome_path=$(find /app/node_modules/.cache/puppeteer/chrome/ -name chrome -xtype f -executable | head -n1) && \ + _chrome_path=$(find /app/node_modules/.cache/ms-playwright/ -name chrome -xtype f -executable | head -n1) && \ echo "CHROMIUM_EXECUTABLE_PATH=$_chrome_path" | tee /app/.env && \ if ldd "$_chrome_path" | grep "not found"; then \ echo "!!! Chromium has unmet shared libs !!!" && \ @@ -194,7 +187,7 @@ CMD ["npm", "run", "start"] # apt-file \ # && \ # apt-file update && \ -# ldd $(find /app/node_modules/.cache/puppeteer/ -name chrome -type f) | grep -Po "\S+(?= => not found)" | \ +# ldd $(find /app/node_modules/.cache/ms-playwright/ -name chrome -type f) | grep -Po "\S+(?= => not found)" | \ # sed 's/\./\\./g' | awk '{print $1"$"}' | apt-file search -xlf - | grep ^lib | \ # xargs -d '\n' -- \ # apt-get install -yq --no-install-recommends \ diff --git a/app.json b/app.json index e351d5220292..28cfeb22ec90 100644 --- a/app.json +++ b/app.json @@ -14,7 +14,7 @@ "value": "80", "required": false }, - "PUPPETEER_SKIP_DOWNLOAD": { + "PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD": { "value": "1", "required": false } diff --git a/docker-compose.yml b/docker-compose.yml index 3f118736609a..261d910457b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: rsshub: - # two ways to enable puppeteer: + # two ways to enable Playwright: # * comment out marked lines, then use this image instead: diygod/rsshub:chromium-bundled # * (consumes more disk space and memory) leave everything unchanged image: diygod/rsshub # or ghcr.io/diygod/rsshub @@ -11,7 +11,7 @@ services: NODE_ENV: production CACHE_TYPE: redis REDIS_URL: 'redis://redis:6379/' - PUPPETEER_WS_ENDPOINT: 'ws://browserless:3000' # marked + PLAYWRIGHT_WS_ENDPOINT: 'ws://browserless:3000' # marked healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:1200/healthz'] interval: 30s diff --git a/eslint.config.mjs b/eslint.config.mjs index e1637cb97180..a8f445197a41 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,3 +1,4 @@ +// oxlint-disable simple-import-sort/imports import js from '@eslint/js'; import typescriptEslint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; @@ -55,12 +56,6 @@ export default defineConfig([ // #endregion }, }, - { - files: ['.puppeteerrc.cjs'], - rules: { - '@typescript-eslint/no-require-imports': 'off', - }, - }, { /* files: [SOURCE_FILES_GLOB], diff --git a/flake.lock b/flake.lock index 0724f0a47ad6..794453d5ffcb 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ ] }, "locked": { - "lastModified": 1767714506, - "narHash": "sha256-WaTs0t1CxhgxbIuvQ97OFhDTVUGd1HA+KzLZUZBhe0s=", + "lastModified": 1777487137, + "narHash": "sha256-TuvKVBX60mqyMT6OB5JqVEh1YIWtFMR/igLCaCdC9tw=", "owner": "cachix", "repo": "cachix", - "rev": "894c649f0daaa38bbcfb21de64be47dfa7cd0ec9", + "rev": "a66a440c321d35f7193472c317f42a55ccd1cb93", "type": "github" }, "original": { @@ -156,18 +156,19 @@ "crate2nix": "crate2nix", "flake-compat": "flake-compat_3", "flake-parts": "flake-parts_3", + "ghostty": "ghostty", "git-hooks": "git-hooks_3", "nix": "nix", "nixd": "nixd", - "nixpkgs": "nixpkgs_4", + "nixpkgs": "nixpkgs_6", "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1775673122, - "narHash": "sha256-zVHWCQ0kCkaNIubCVv5fZyrPYfI4EGEPLRFRARwGSl8=", + "lastModified": 1778186459, + "narHash": "sha256-Rv2EMtH1agSQlInQzDBjhdEDPegkdIaV/ViGlx0Dri0=", "owner": "cachix", "repo": "devenv", - "rev": "010a22c855d22b127c6fb007868b2f3fc09bc2c8", + "rev": "f8c5fcd9c049318a1fd15528e61e6eef902e225b", "type": "github" }, "original": { @@ -265,6 +266,22 @@ "type": "github" } }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -318,11 +335,11 @@ ] }, "locked": { - "lastModified": 1772408722, - "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "lastModified": 1777678872, + "narHash": "sha256-EPIFsulyon7Z1vLQq5Fk64GR8L7cQsT+IPhcsukVbgk=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "rev": "5250617bffd85403b14dbf43c3870e7f255d2c16", "type": "github" }, "original": { @@ -333,7 +350,7 @@ }, "flake-utils": { "inputs": { - "systems": "systems" + "systems": "systems_2" }, "locked": { "lastModified": 1731533236, @@ -349,6 +366,29 @@ "type": "github" } }, + "ghostty": { + "inputs": { + "flake-compat": "flake-compat_4", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs_4", + "systems": "systems", + "zig": "zig", + "zon2nix": "zon2nix" + }, + "locked": { + "lastModified": 1777773742, + "narHash": "sha256-dZFc+8az7BUIs8+v45XqNnY5G6oXEwVfVVHZQuATSGQ=", + "owner": "ghostty-org", + "repo": "ghostty", + "rev": "1547dd667ab6d1f4ebcdc7282adc54c95752ee67", + "type": "github" + }, + "original": { + "owner": "ghostty-org", + "repo": "ghostty", + "type": "github" + } + }, "git-hooks": { "inputs": { "flake-compat": [ @@ -424,11 +464,11 @@ ] }, "locked": { - "lastModified": 1772893680, - "narHash": "sha256-JDqZMgxUTCq85ObSaFw0HhE+lvdOre1lx9iI6vYyOEs=", + "lastModified": 1776796298, + "narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "8baab586afc9c9b57645a734c820e4ac0a604af9", + "rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad", "type": "github" }, "original": { @@ -555,6 +595,28 @@ "type": "github" } }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "devenv", + "ghostty", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770586272, + "narHash": "sha256-Ucci8mu8QfxwzyfER2DQDbvW9t1BnTUJhBmY7ybralo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "b1f916ba052341edc1f80d4b2399f1092a4873ca", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "nix": { "inputs": { "flake-compat": [ @@ -581,11 +643,11 @@ ] }, "locked": { - "lastModified": 1775657489, - "narHash": "sha256-v1KwZrIMGpteHPwxXvbapc7o3iduhU61phPUfyrnjM8=", + "lastModified": 1776511668, + "narHash": "sha256-g2KEBuHpc3a56c+jPcg0+w6LSuIj6f+zzdztLCOyIhc=", "owner": "cachix", "repo": "nix", - "rev": "5c0da4397902105a84611c6d49e9d39a618ca025", + "rev": "42d4b7de21c15f28c568410f4383fa06a8458a40", "type": "github" }, "original": { @@ -633,18 +695,15 @@ "devenv", "flake-parts" ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ], + "nixpkgs": "nixpkgs_5", "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1773634079, - "narHash": "sha256-49qb4QNMv77VOeEux+sMd0uBhPvvHgVc0r938Bulvbo=", + "lastModified": 1777345723, + "narHash": "sha256-BhY3D5DhpDnnUcaY+AL/cpyYX+OIjQgnAkbPLZ08C38=", "owner": "nix-community", "repo": "nixd", - "rev": "8ecf93d4d93745e05ea53534e8b94f5e9506e6bd", + "rev": "6bf30951a3dc407a798d30db427e3f96ac9b39f5", "type": "github" }, "original": { @@ -672,11 +731,11 @@ "nixpkgs-src": { "flake": false, "locked": { - "lastModified": 1773597492, - "narHash": "sha256-hQ284SkIeNaeyud+LS0WVLX+WL2rxcVZLFEaK0e03zg=", + "lastModified": 1776329215, + "narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a07d4ce6bee67d7c838a8a5796e75dff9caa21ef", + "rev": "b86751bc4085f48661017fa226dee99fab6c651b", "type": "github" }, "original": { @@ -719,15 +778,41 @@ } }, "nixpkgs_4": { + "locked": { + "lastModified": 1770537093, + "narHash": "sha256-XV30uo8tXuxdzuV8l3sojmlPRLd/8tpMsOp4lNzLGUo=", + "rev": "fef9403a3e4d31b0a23f0bacebbec52c248fbb51", + "type": "tarball", + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre942631.fef9403a3e4d/nixexprs.tar.xz" + }, + "original": { + "type": "tarball", + "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1776877367, + "narHash": "sha256-wMN1gM00sUQ2KC9CNr/XEOGdfOrl67PabIRv9AYayTo=", + "rev": "0726a0ecb6d4e08f6adced58726b95db924cef57", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre985613.0726a0ecb6d4/nixexprs.tar.xz" + }, + "original": { + "type": "tarball", + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + } + }, + "nixpkgs_6": { "inputs": { "nixpkgs-src": "nixpkgs-src" }, "locked": { - "lastModified": 1773704619, - "narHash": "sha256-LKtmit8Sr81z8+N2vpIaN/fyiQJ8f7XJ6tMSKyDVQ9s=", + "lastModified": 1776852779, + "narHash": "sha256-WwO/ITisCXwyiRgtktZgv3iGhAGO+IB5Av4kKCwezR0=", "owner": "cachix", "repo": "devenv-nixpkgs", - "rev": "906534d75b0e2fe74a719559dfb1ad3563485f43", + "rev": "ec3063523dcd911aeadb50faa589f237cdab5853", "type": "github" }, "original": { @@ -737,13 +822,13 @@ "type": "github" } }, - "nixpkgs_5": { + "nixpkgs_7": { "locked": { - "lastModified": 1775423009, - "narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=", + "lastModified": 1777954456, + "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9", + "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "type": "github" }, "original": { @@ -815,7 +900,7 @@ "inputs": { "devenv": "devenv", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_5" + "nixpkgs": "nixpkgs_7" } }, "rust-overlay": { @@ -826,11 +911,11 @@ ] }, "locked": { - "lastModified": 1773630837, - "narHash": "sha256-zJhgAGnbVKeBMJOb9ctZm4BGS/Rnrz+5lfSXTVah4HQ=", + "lastModified": 1777778183, + "narHash": "sha256-Lqv9MZO0XAGcMbXJU+ULBSMD41Pf391uJehylUQKe7Y=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "f600ea449c7b5bb596fa1cf21c871cc5b9e31316", + "rev": "dbba5f888c82ef3ce594c451c33ac2474eb80847", "type": "github" }, "original": { @@ -840,6 +925,22 @@ } }, "systems": { + "flake": false, + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -863,11 +964,11 @@ ] }, "locked": { - "lastModified": 1772660329, - "narHash": "sha256-IjU1FxYqm+VDe5qIOxoW+pISBlGvVApRjiw/Y/ttJzY=", + "lastModified": 1775636079, + "narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "3710e0e1218041bbad640352a0440114b1e10428", + "rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba", "type": "github" }, "original": { @@ -875,6 +976,85 @@ "repo": "treefmt-nix", "type": "github" } + }, + "zig": { + "inputs": { + "flake-compat": [ + "devenv", + "ghostty", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "ghostty", + "nixpkgs" + ], + "systems": [ + "devenv", + "ghostty", + "systems" + ] + }, + "locked": { + "lastModified": 1776789209, + "narHash": "sha256-G6B7Q4TXn7MZ1mB+f9rymjsYF5PLWoSvmbxijb/99bw=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "14fe971844e841297ddd2ce9783d6892b467af39", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + }, + "zig_2": { + "inputs": { + "nixpkgs": [ + "devenv", + "ghostty", + "zon2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1777234348, + "narHash": "sha256-fKw44a4qbUuI5eTG8k0gPbqMV5TOrjYF35PBzsYgd2U=", + "ref": "refs/heads/main", + "rev": "2c781c0609ecda600ab98f98cca417bbd981bd53", + "revCount": 1677, + "type": "git", + "url": "https://codeberg.org/jcollie/zig-overlay.git" + }, + "original": { + "type": "git", + "url": "https://codeberg.org/jcollie/zig-overlay.git" + } + }, + "zon2nix": { + "inputs": { + "nixpkgs": [ + "devenv", + "ghostty", + "nixpkgs" + ], + "zig": "zig_2" + }, + "locked": { + "lastModified": 1777314365, + "narHash": "sha256-eLxQaD0wc96Neqkln8wHS0rNq/chPODifFkhwrwilEU=", + "owner": "jcollie", + "repo": "zon2nix", + "rev": "a5a1d412ad1ab6305511997bbc92b3a9dd6cb784", + "type": "github" + }, + "original": { + "owner": "jcollie", + "ref": "main", + "repo": "zon2nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index d24d5cf617e4..1b14daae8213 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,7 @@ deps = pkgs.fetchPnpmDeps { pname = "rsshub"; src = ./.; - hash = "sha256-bR/jICpR+OpLZzLILkrBpiai0eLXzZWPhnHEPL6oIik="; + hash = "sha256-rEc2nnQlkk8OOzMyqYTmgMGEDsGb/3pLKvlEmgi2bkw="; fetcherVersion = 2; }; in diff --git a/lib/app.worker.tsx b/lib/app.worker.tsx index cd704587579c..6f3a6cff3c6f 100644 --- a/lib/app.worker.tsx +++ b/lib/app.worker.tsx @@ -17,7 +17,7 @@ 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/puppeteer'; +import { setBrowserBinding } from '@/utils/playwright'; // Define Worker environment bindings type Bindings = { diff --git a/lib/bilibili-video-route.test.ts b/lib/bilibili-video-route.test.ts new file mode 100644 index 000000000000..09f601237bd5 --- /dev/null +++ b/lib/bilibili-video-route.test.ts @@ -0,0 +1,186 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const cacheMock = { + getCookie: vi.fn(), + getConfiguredCookie: vi.fn(), + getUsernameAndFaceFromUID: vi.fn(), + getVideoSubtitleAttachment: vi.fn(), +}; + +const destroy = vi.fn(); +const getPlaywrightPage = vi.fn(); +const goto = vi.fn(); +const on = vi.fn(); +const setCookie = vi.fn(); +const setRequestInterception = vi.fn(); +const waitForResponse = vi.fn(); + +const page = { + goto, + on, + setCookie, + setRequestInterception, + waitForResponse, +}; + +vi.mock('@/routes/bilibili/cache', () => ({ + default: cacheMock, +})); + +vi.mock('@/utils/playwright', () => ({ + getPlaywrightPage, +})); + +vi.mock('@/utils/logger', () => ({ + default: { + error: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + }, +})); + +const createContext = () => + ({ + req: { + param: (name: string) => { + if (name === 'uid') { + return '646730844'; + } + }, + query: (name: string) => { + if (name === 'format') { + return 'json'; + } + }, + }, + }) as any; + +describe('/bilibili/user/video/:uid', () => { + beforeEach(() => { + vi.resetModules(); + cacheMock.getCookie.mockReset(); + cacheMock.getConfiguredCookie.mockReset(); + cacheMock.getUsernameAndFaceFromUID.mockReset(); + cacheMock.getVideoSubtitleAttachment.mockReset(); + destroy.mockReset(); + getPlaywrightPage.mockReset(); + goto.mockReset(); + on.mockReset(); + setCookie.mockReset(); + setRequestInterception.mockReset(); + waitForResponse.mockReset(); + }); + + it('falls back to browser mode by opening the video page and reading a single video list response', async () => { + cacheMock.getCookie.mockRejectedValueOnce(new Error('cookie unavailable')); + cacheMock.getConfiguredCookie.mockReturnValue(undefined); + cacheMock.getUsernameAndFaceFromUID.mockResolvedValue(['雷军', 'https://example.com/face.jpg']); + cacheMock.getVideoSubtitleAttachment.mockResolvedValue([]); + waitForResponse.mockResolvedValue({ + headers: () => ({ 'content-type': 'application/json; charset=utf-8' }), + json: () => ({ + code: 0, + data: { + list: { + vlist: [ + { + aid: 1, + author: '雷军', + bvid: 'BV1xx411c7mD', + comment: 10, + created: 1_700_000_000, + description: 'video description', + length: '01:02', + pic: 'https://example.com/cover.jpg', + title: 'Video title', + }, + ], + }, + }, + }), + request: () => ({ + method: () => 'GET', + resourceType: () => 'fetch', + }), + status: () => 200, + url: () => 'https://api.bilibili.com/x/space/wbi/arc/search', + }); + goto.mockResolvedValue(undefined); + getPlaywrightPage.mockImplementation(async (_url, options) => { + await options.onBeforeLoad?.(page); + return { + destroy, + page, + }; + }); + + const { route } = await import('@/routes/bilibili/video'); + const data = await route.handler(createContext()); + + expect(getPlaywrightPage).toHaveBeenCalledWith( + 'https://space.bilibili.com/646730844/video', + expect.objectContaining({ + closeTimeout: 90000, + gotoConfig: { waitUntil: 'domcontentloaded' }, + noGoto: true, + }) + ); + expect(waitForResponse.mock.invocationCallOrder[0]).toBeLessThan(goto.mock.invocationCallOrder[0]); + expect(waitForResponse).toHaveBeenCalledWith(expect.any(Function), { timeout: 45000 }); + const isVideoListResponse = waitForResponse.mock.calls[0][0]; + expect( + isVideoListResponse({ + headers: () => ({ 'content-type': 'application/json; charset=utf-8' }), + request: () => ({ + method: () => 'GET', + resourceType: () => 'fetch', + }), + url: () => 'https://api.bilibili.com/x/space/wbi/arc/search', + }) + ).toBe(true); + expect( + isVideoListResponse({ + headers: () => ({ 'content-type': 'text/html' }), + request: () => ({ + method: () => 'GET', + resourceType: () => 'fetch', + }), + url: () => 'https://api.bilibili.com/x/space/wbi/arc/search', + }) + ).toBe(true); + expect(goto).toHaveBeenCalledTimes(1); + expect(goto).toHaveBeenCalledWith('https://space.bilibili.com/646730844/video', { timeout: 45000, waitUntil: 'domcontentloaded' }); + expect(cacheMock.getCookie).toHaveBeenCalledTimes(1); + expect(data.item).toHaveLength(1); + expect(data.item[0].title).toBe('Video title'); + }); + + it('throws when the single video list response returns 412', async () => { + cacheMock.getCookie.mockRejectedValueOnce(new Error('cookie unavailable')); + cacheMock.getConfiguredCookie.mockReturnValue(undefined); + waitForResponse.mockResolvedValue({ + headers: () => ({ 'content-type': 'text/html' }), + request: () => ({ + method: () => 'GET', + resourceType: () => 'fetch', + }), + status: () => 412, + url: () => 'https://api.bilibili.com/x/space/wbi/arc/search', + }); + goto.mockResolvedValue(undefined); + getPlaywrightPage.mockImplementation(async (_url, options) => { + await options.onBeforeLoad?.(page); + return { + destroy, + page, + }; + }); + + const { route } = await import('@/routes/bilibili/video'); + + await expect(route.handler(createContext())).rejects.toThrow('Bilibili browser mode returned unexpected video list API status 412'); + expect(goto).toHaveBeenCalledTimes(1); + expect(waitForResponse).toHaveBeenCalledTimes(1); + expect(destroy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/lib/config.test.ts b/lib/config.test.ts index 02010cb5d417..472f1aefe470 100644 --- a/lib/config.test.ts +++ b/lib/config.test.ts @@ -91,6 +91,20 @@ describe('config', () => { expect(config.isDefaultUA).toBe(true); }); + it('http cache config', async () => { + process.env.CACHE_HTTP_URL = 'https://cache.example.com'; + process.env.CACHE_HTTP_TOKEN = 'token'; + + const { config } = await import('./config'); + expect(config.httpCache).toMatchObject({ + url: 'https://cache.example.com', + token: 'token', + }); + + delete process.env.CACHE_HTTP_URL; + delete process.env.CACHE_HTTP_TOKEN; + }); + it('remote config', async () => { process.env.REMOTE_CONFIG = 'http://rsshub.test/config'; diff --git a/lib/config.ts b/lib/config.ts index 9ba567508c9e..a460f33723b0 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -8,12 +8,13 @@ type ConfigEnvKeys = | 'ENABLE_CLUSTER' | 'IS_PACKAGE' | 'NODE_NAME' - | 'PUPPETEER_REAL_BROWSER_SERVICE' + | 'PLAYWRIGHT_WS_ENDPOINT' | 'PUPPETEER_WS_ENDPOINT' | 'CHROMIUM_EXECUTABLE_PATH' // Network | 'PORT' | 'LISTEN_INADDR_ANY' + | 'DISABLE_IPV6' | 'REQUEST_RETRY' | 'REQUEST_TIMEOUT' | 'UA' @@ -26,6 +27,8 @@ type ConfigEnvKeys = | 'CACHE_CONTENT_EXPIRE' | 'MEMORY_MAX' | 'REDIS_URL' + | 'CACHE_HTTP_URL' + | 'CACHE_HTTP_TOKEN' // Proxy | 'PROXY_URI' | 'PROXY_URIS' @@ -238,6 +241,7 @@ type ConfigEnvKeys = | 'YOUTUBE_CLIENT_SECRET' | 'YOUTUBE_REFRESH_TOKEN' | 'YOUTUBE_VIDEO_EMBED_URL' + | 'ZAIMANHUA_TOKEN' | 'ZHIHU_COOKIES' | 'ZODGAME_COOKIE' | 'ZSXQ_ACCESS_TOKEN' @@ -255,14 +259,14 @@ export type Config = { enableCluster?: string; isPackage: boolean; nodeName?: string; - puppeteerRealBrowserService?: string; - puppeteerWSEndpoint?: string; + playwrightWSEndpoint?: string; chromiumExecutablePath?: string; // network connect: { port: number; }; listenInaddrAny: boolean; + disableIPv6: boolean; requestRetry: number; requestTimeout: number; ua: string; @@ -282,6 +286,10 @@ export type Config = { redis: { url: string; }; + httpCache: { + url?: string; + token?: string; + }; // proxy proxyUri?: string; proxyUris?: string[]; @@ -684,6 +692,9 @@ export type Config = { refreshToken?: string; videoEmbedUrl?: string; }; + zaimanhua: { + token?: string; + }; zhihu: { cookies?: string; }; @@ -744,14 +755,14 @@ const calculateValue = () => { enableCluster: toBoolean(envs.ENABLE_CLUSTER, false), isPackage: !!envs.IS_PACKAGE, nodeName: envs.NODE_NAME, - puppeteerRealBrowserService: envs.PUPPETEER_REAL_BROWSER_SERVICE, - puppeteerWSEndpoint: envs.PUPPETEER_WS_ENDPOINT, + playwrightWSEndpoint: envs.PLAYWRIGHT_WS_ENDPOINT ?? envs.PUPPETEER_WS_ENDPOINT, chromiumExecutablePath: envs.CHROMIUM_EXECUTABLE_PATH, // network connect: { port: toInt(envs.PORT, 1200), // 监听端口 }, listenInaddrAny: toBoolean(envs.LISTEN_INADDR_ANY, true), // 是否允许公网连接,取值 0 1 + 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'), @@ -760,7 +771,7 @@ const calculateValue = () => { allowOrigin: envs.ALLOW_ORIGIN, // cache cache: { - type: envs.CACHE_TYPE || (envs.CACHE_TYPE === '' ? '' : 'memory'), // 缓存类型,支持 'memory' 和 'redis',设为空可以禁止缓存 + type: envs.CACHE_TYPE || (envs.CACHE_TYPE === '' ? '' : 'memory'), // Cache type; supports 'memory', 'redis', and 'http'. Set to empty string to disable cache. requestTimeout: toInt(envs.CACHE_REQUEST_TIMEOUT, 60), routeExpire: toInt(envs.CACHE_EXPIRE, 5 * 60), // 路由缓存时间,单位为秒 contentExpire: toInt(envs.CACHE_CONTENT_EXPIRE, 1 * 60 * 60), // 不变内容缓存时间,单位为秒 @@ -772,6 +783,10 @@ const calculateValue = () => { redis: { url: envs.REDIS_URL || 'redis://localhost:6379/', }, + httpCache: { + url: envs.CACHE_HTTP_URL, + token: envs.CACHE_HTTP_TOKEN, + }, // proxy proxyUri: envs.PROXY_URI, proxyUris: envs.PROXY_URIS @@ -1179,6 +1194,9 @@ const calculateValue = () => { refreshToken: envs.YOUTUBE_REFRESH_TOKEN, videoEmbedUrl: envs.YOUTUBE_VIDEO_EMBED_URL || 'https://www.youtube-nocookie.com/embed/', }, + zaimanhua: { + token: envs.ZAIMANHUA_TOKEN, + }, zhihu: { cookies: envs.ZHIHU_COOKIES, }, @@ -1198,7 +1216,6 @@ const calculateValue = () => { } }; calculateValue(); - (async () => { if (envs.REMOTE_CONFIG) { const { default: logger } = await import('@/utils/logger'); diff --git a/lib/errors/index.test.ts b/lib/errors/index.test.ts index 2dae632b489e..9c102b973600 100644 --- a/lib/errors/index.test.ts +++ b/lib/errors/index.test.ts @@ -5,7 +5,7 @@ import app from '@/app'; import { config } from '@/config'; describe('error', () => { - it(`error`, async () => { + it('error', async () => { const response = await app.request('/test/error'); expect(response.status).toBe(503); const text = await response.text(); @@ -14,20 +14,20 @@ describe('error', () => { }); describe('httperror', () => { - it(`httperror`, async () => { + it('httperror', async () => { const response = await app.request('/test/httperror'); expect(response.status).toBe(503); const text = await response.text(); - expect(text).toContain('FetchError: [GET] "https://httpbingo.org/status/404": 404 Not Found'); + expect(text).toContain('FetchError: [GET] "https://httpbingo.org/status/404": 404'); }, 20000); }); describe('RequestInProgressError', () => { - it(`RequestInProgressError with retry`, async () => { + it('RequestInProgressError with retry', async () => { const responses = await Promise.all([app.request('/test/slow'), app.request('/test/slow')]); expect(new Set(responses.map((r) => r.status))).toEqual(new Set([200, 200])); }); - it(`RequestInProgressError`, async () => { + it('RequestInProgressError', async () => { const responses = await Promise.all([app.request('/test/slow4'), app.request('/test/slow4')]); expect(new Set(responses.map((r) => r.status))).toEqual(new Set([200, 503])); expect(new Set(responses.map((r) => r.headers.get('cache-control')))).toEqual(new Set([`public, max-age=${config.cache.routeExpire}`, `public, max-age=${config.requestTimeout / 1000}`])); @@ -37,7 +37,7 @@ describe('RequestInProgressError', () => { }); describe('config-not-found-error', () => { - it(`config-not-found-error`, async () => { + it('config-not-found-error', async () => { const response = await app.request('/test/config-not-found-error'); expect(response.status).toBe(503); const text = await response.text(); @@ -46,7 +46,7 @@ describe('config-not-found-error', () => { }); describe('invalid-parameter-error', () => { - it(`invalid-parameter-error`, async () => { + it('invalid-parameter-error', async () => { const response = await app.request('/test/invalid-parameter-error'); expect(response.status).toBe(503); const text = await response.text(); @@ -55,7 +55,7 @@ describe('invalid-parameter-error', () => { }); describe('captcha-error', () => { - it(`captcha-error`, async () => { + it('captcha-error', async () => { const response = await app.request('/test/captcha-error'); expect(response.status).toBe(503); const text = await response.text(); diff --git a/lib/index.ts b/lib/index.ts index f70d71b343a2..d21bac888c48 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -34,7 +34,7 @@ if (config.enableCluster) { logger.info(`Worker ${process.pid} is running`); serve({ fetch: app.fetch, - hostname: config.listenInaddrAny ? '::' : '127.0.0.1', + hostname: config.listenInaddrAny ? (config.disableIPv6 ? '0.0.0.0' : '::') : '127.0.0.1', port, serverOptions: { maxHeaderSize: 1024 * 32, @@ -52,7 +52,7 @@ if (config.enableCluster) { server = serve({ fetch: app.fetch, - hostname: config.listenInaddrAny ? '::' : '127.0.0.1', + hostname: config.listenInaddrAny ? (config.disableIPv6 ? '0.0.0.0' : '::') : '127.0.0.1', port, serverOptions: { maxHeaderSize: 1024 * 32, diff --git a/lib/middleware/access-control.test.ts b/lib/middleware/access-control.test.ts index 40674096e695..ec6c11f6b6c2 100644 --- a/lib/middleware/access-control.test.ts +++ b/lib/middleware/access-control.test.ts @@ -15,7 +15,7 @@ afterEach(() => { }); describe('access-control', () => { - it(`access key`, async () => { + it('access key', async () => { const key = '1L0veRSSHub'; const code = md5('/test/2' + key); process.env.ACCESS_KEY = key; diff --git a/lib/middleware/filter-engine.test.ts b/lib/middleware/filter-engine.test.ts index e68e60c1e957..e0c2158b8bf2 100644 --- a/lib/middleware/filter-engine.test.ts +++ b/lib/middleware/filter-engine.test.ts @@ -10,7 +10,7 @@ afterEach(() => { }); describe('filter-engine', () => { - it(`filter RE2 engine ReDoS attack`, async () => { + it('filter RE2 engine ReDoS attack', async () => { const app = (await import('@/app')).default; const response = await app.request('/test/1?filter=abc(%3F%3Ddef)'); @@ -18,7 +18,7 @@ describe('filter-engine', () => { expect(await response.text()).toMatch(/RE2JSSyntaxException/); }); - it(`filter Regexp engine backward compatibility`, async () => { + it('filter Regexp engine backward compatibility', async () => { process.env.FILTER_REGEX_ENGINE = 'regexp'; const app = (await import('@/app')).default; @@ -27,7 +27,7 @@ describe('filter-engine', () => { expect(response.status).toBe(200); }); - it(`filter Regexp engine test config`, async () => { + it('filter Regexp engine test config', async () => { process.env.FILTER_REGEX_ENGINE = 'somethingelse'; const app = (await import('@/app')).default; diff --git a/lib/middleware/header.test.ts b/lib/middleware/header.test.ts index 71f18dbbdc6b..4578d8da22c2 100644 --- a/lib/middleware/header.test.ts +++ b/lib/middleware/header.test.ts @@ -17,7 +17,7 @@ afterAll(() => { }); describe('header', () => { - it(`header`, async () => { + it('header', async () => { const app = (await import('@/app')).default; const { config } = await import('@/config'); const response = await app.request('/test/1'); @@ -32,7 +32,7 @@ describe('header', () => { expect(response.headers.get('x-rsshub-route')).toBe('/test/:id/:params?'); }); - it(`etag`, async () => { + it('etag', async () => { const app = (await import('@/app')).default; const response = await app.request('/test/1', { headers: { diff --git a/lib/middleware/parameter.test.ts b/lib/middleware/parameter.test.ts index 69f3000e3336..c602a901dea5 100644 --- a/lib/middleware/parameter.test.ts +++ b/lib/middleware/parameter.test.ts @@ -11,7 +11,7 @@ const { default: app } = await import('@/app'); const parser = new Parser(); describe('filter', () => { - it(`filter`, async () => { + it('filter', async () => { const response = await app.request('/test/1?filter=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -19,13 +19,13 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Title5'); }); - it(`filter filter_case_sensitive default`, async () => { + it('filter filter_case_sensitive default', async () => { const response = await app.request('/test/1?filter=description4|title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(0); }); - it(`filter filter_case_sensitive=false`, async () => { + it('filter filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filter=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -33,35 +33,35 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Title5'); }); - it(`filter_title`, async () => { + it('filter_title', async () => { const response = await app.request('/test/1?filter_title=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Title5'); }); - it(`filter_title filter_case_sensitive=false`, async () => { + it('filter_title filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filter_title=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Title5'); }); - it(`filter_description`, async () => { + it('filter_description', async () => { const response = await app.request('/test/1?filter_description=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Title4'); }); - it(`filter_description filter_case_sensitive=false`, async () => { + it('filter_description filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filter_description=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Title4'); }); - it(`filter_author`, async () => { + it('filter_author', async () => { const response = await app.request('/test/1?filter_author=DIYgod4|DIYgod5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -69,13 +69,13 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Title5'); }); - it(`filter_author filter_case_sensitive default`, async () => { + it('filter_author filter_case_sensitive default', async () => { const response = await app.request('/test/1?filter_author=diygod4|diygod5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(0); }); - it(`filter_author filter_case_sensitive=false`, async () => { + it('filter_author filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filter_author=diygod4|diygod5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -83,7 +83,7 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Title5'); }); - it(`filter_category`, async () => { + it('filter_category', async () => { const response = await app.request('/test/filter?filter_category=Category0|Category1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -91,13 +91,13 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Filter Title2'); }); - it(`filter_category filter_case_sensitive default`, async () => { + it('filter_category filter_case_sensitive default', async () => { const response = await app.request('/test/filter?filter_category=category0|category1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(0); }); - it(`filter_category filter_case_sensitive=false`, async () => { + it('filter_category filter_case_sensitive=false', async () => { const response = await app.request('/test/filter?filter_category=category0|category1&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -105,14 +105,14 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Filter Title2'); }); - it(`filter_category filter_case_sensitive=false category string`, async () => { + it('filter_category filter_case_sensitive=false category string', async () => { const response = await app.request('/test/filter?filter_category=category3&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Filter Title3'); }); - it(`filter_category illegal_category`, async () => { + it('filter_category illegal_category', async () => { const response = await app.request('/test/filter-illegal-category?filter_category=CategoryIllegal'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); @@ -120,7 +120,7 @@ describe('filter', () => { expect(parsed.items[0].categories?.[0]).toBe('CategoryIllegal'); }); - it(`filter_time`, async () => { + it('filter_time', async () => { const response = await app.request('/test/current_time?filter_time=25'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); @@ -128,7 +128,7 @@ describe('filter', () => { expect(parsed.items[1].title).toBe('Title2'); }); - it(`filterout`, async () => { + it('filterout', async () => { const response = await app.request('/test/1?filterout=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(3); @@ -137,7 +137,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout filter_case_sensitive default`, async () => { + it('filterout filter_case_sensitive default', async () => { const response = await app.request('/test/1?filterout=description4|title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(5); @@ -146,7 +146,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout filter_case_sensitive=false`, async () => { + it('filterout filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filterout=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(3); @@ -155,7 +155,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout_title`, async () => { + it('filterout_title', async () => { const response = await app.request('/test/1?filterout_title=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(4); @@ -165,7 +165,7 @@ describe('filter', () => { expect(parsed.items[3].title).toBe('Title4'); }); - it(`filterout_title filter_case_sensitive=false`, async () => { + it('filterout_title filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filterout_title=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(4); @@ -175,7 +175,7 @@ describe('filter', () => { expect(parsed.items[3].title).toBe('Title4'); }); - it(`filterout_description`, async () => { + it('filterout_description', async () => { const response = await app.request('/test/1?filterout_description=Description4|Title5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(4); @@ -185,7 +185,7 @@ describe('filter', () => { expect(parsed.items[3].title).toBe('Title5'); }); - it(`filterout_description filter_case_sensitive=false`, async () => { + it('filterout_description filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filterout_description=description4|title5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(4); @@ -195,7 +195,7 @@ describe('filter', () => { expect(parsed.items[3].title).toBe('Title5'); }); - it(`filterout_author`, async () => { + it('filterout_author', async () => { const response = await app.request('/test/1?filterout_author=DIYgod4|DIYgod5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(3); @@ -204,7 +204,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout_author filter_case_sensitive default`, async () => { + it('filterout_author filter_case_sensitive default', async () => { const response = await app.request('/test/1?filterout_author=diygod4|diygod5'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(5); @@ -213,7 +213,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout_author filter_case_sensitive=false`, async () => { + it('filterout_author filter_case_sensitive=false', async () => { const response = await app.request('/test/1?filterout_author=diygod4|diygod5&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(3); @@ -222,7 +222,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title3'); }); - it(`filterout_category`, async () => { + it('filterout_category', async () => { const response = await app.request('/test/filter?filterout_category=Category0|Category1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(6); @@ -231,7 +231,7 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title2'); }); - it(`filterout_category filter_case_sensitive default`, async () => { + it('filterout_category filter_case_sensitive default', async () => { const response = await app.request('/test/filter?filterout_category=category0|category1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(8); @@ -241,7 +241,7 @@ describe('filter', () => { expect(parsed.items[3].title).toBe('Title1'); }); - it(`filterout_category filter_case_sensitive=false`, async () => { + it('filterout_category filter_case_sensitive=false', async () => { const response = await app.request('/test/filter?filterout_category=category0|category1&filter_case_sensitive=false'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(6); @@ -250,14 +250,14 @@ describe('filter', () => { expect(parsed.items[2].title).toBe('Title2'); }); - it(`filter combination`, async () => { + it('filter combination', async () => { const response = await app.request('/test/filter?filter_title=Filter&filter_description=Description1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(1); expect(parsed.items[0].title).toBe('Filter Title1'); }); - it(`filterout combination`, async () => { + it('filterout combination', async () => { const response = await app.request('/test/filter?filterout_title=Filter&filterout_description=Description1'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(4); @@ -266,7 +266,7 @@ describe('filter', () => { }); describe('limit', () => { - it(`limit`, async () => { + it('limit', async () => { const response = await app.request('/test/1?limit=3'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(3); @@ -289,16 +289,16 @@ describe('sorted', () => { }); describe('tgiv', () => { - it(`tgiv`, async () => { + it('tgiv', async () => { const response = await app.request('/test/1?tgiv=test'); const parsed = await parser.parseString(await response.text()); - expect(parsed.items[0].link).toBe(`https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F1&rhash=test`); - expect(parsed.items[1].link).toBe(`https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F2&rhash=test`); + expect(parsed.items[0].link).toBe('https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F1&rhash=test'); + expect(parsed.items[1].link).toBe('https://t.me/iv?url=https%3A%2F%2Fgithub.com%2FDIYgod%2FRSSHub%2Fissues%2F2&rhash=test'); }); }); describe('empty', () => { - it(`empty`, async () => { + it('empty', async () => { const response1 = await app.request('/test/empty'); expect(response1.status).toBe(503); expect(await response1.text()).toMatch(/Error: this route is empty/); @@ -311,7 +311,7 @@ describe('empty', () => { }); describe('allow_empty', () => { - it(`allow_empty`, async () => { + it('allow_empty', async () => { const response = await app.request('/test/allow_empty'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -320,7 +320,7 @@ describe('allow_empty', () => { }); describe('wrong_path', () => { - it(`wrong_path`, async () => { + it('wrong_path', async () => { const response = await app.request('/wrong'); expect(response.status).toBe(404); expect(response.headers.get('cache-control')).toBe(`public, max-age=${config.cache.routeExpire}`); @@ -329,7 +329,7 @@ describe('wrong_path', () => { }); describe('fulltext_mode', () => { - it(`fulltext`, async () => { + it('fulltext', async () => { const response = await app.request('/test/1?mode=fulltext'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -338,7 +338,7 @@ describe('fulltext_mode', () => { }); describe('complicated_description', () => { - it(`complicated_description`, async () => { + it('complicated_description', async () => { const response = await app.request('/test/complicated'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -359,7 +359,7 @@ describe('complicated_description', () => { }); describe('multimedia_description', () => { - it(`multimedia_description`, async () => { + it('multimedia_description', async () => { const response = await app.request('/test/multimedia'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -375,7 +375,7 @@ describe('multimedia_description', () => { }); describe('sort', () => { - it(`sort`, async () => { + it('sort', async () => { const response = await app.request('/test/sort'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -387,7 +387,7 @@ describe('sort', () => { }); describe('mess parameter', () => { - it(`date`, async () => { + it('date', async () => { const response = await app.request('/test/mess'); expect(response.status).toBe(200); const parsed = await parser.parseString(await response.text()); @@ -397,7 +397,7 @@ describe('mess parameter', () => { }); describe('opencc', () => { - it(`opencc`, async () => { + it('opencc', async () => { const response = await app.request('/test/opencc?opencc=t2s'); const parsed = await parser.parseString(await response.text()); expect(parsed.items[0].title).toBe('小可爱'); @@ -406,7 +406,7 @@ describe('opencc', () => { }); describe('brief', () => { - it(`brief`, async () => { + it('brief', async () => { const response = await app.request('/test/brief?brief=100'); const parsed = await parser.parseString(await response.text()); expect(parsed.items[0].title).toBe('小可愛'); @@ -417,7 +417,7 @@ describe('brief', () => { }); describe('multi parameter', () => { - it(`filter before limit`, async () => { + it('filter before limit', async () => { const response = await app.request('/test/filter-limit?filterout_title=2&limit=2'); const parsed = await parser.parseString(await response.text()); expect(parsed.items.length).toBe(2); diff --git a/lib/middleware/parameter.ts b/lib/middleware/parameter.ts index d3b31692c94f..07e70e098005 100644 --- a/lib/middleware/parameter.ts +++ b/lib/middleware/parameter.ts @@ -414,7 +414,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => { } } } else { - throw new Error(`Invalid parameter brief. Please check the doc https://docs.rsshub.app/guide/parameters#shu-chu-jian-xun`); + throw new Error('Invalid parameter brief. Please check the doc https://docs.rsshub.app/guide/parameters#shu-chu-jian-xun'); } } // some parameters are processed in `anti-hotlink.js` diff --git a/lib/registry.test.ts b/lib/registry.test.ts index a66edacf6c00..dfbdde572c8d 100644 --- a/lib/registry.test.ts +++ b/lib/registry.test.ts @@ -5,7 +5,7 @@ import { config } from '@/config'; describe('registry', () => { // root - it(`/`, async () => { + it('/', async () => { const response = await app.request('/'); expect(response.status).toBe(200); expect(response.headers.get('content-type')).toBe('text/html; charset=UTF-8'); diff --git a/lib/routes/005/index.tsx b/lib/routes/005/index.tsx index 9694c07eef3b..43c2be4b5b92 100644 --- a/lib/routes/005/index.tsx +++ b/lib/routes/005/index.tsx @@ -103,11 +103,9 @@ export const route: Route = { handler, example: '/005/zx', parameters: { category: '分类,可在对应分类页 URL 中找到,默认为二次元资讯' }, - description: ` -| 二次元资讯 | 慢慢说 | 道听途说 | 展会资讯 | + description: `| 二次元资讯 | 慢慢说 | 道听途说 | 展会资讯 | | ---------- | ------ | -------- | -------- | -| zx | zwh | dtts | zh | - `, +| zx | zwh | dtts | zh |`, categories: ['anime'], features: { diff --git a/lib/routes/0xxx/index.ts b/lib/routes/0xxx/index.ts index f8d319b236b8..78ff90a68380 100644 --- a/lib/routes/0xxx/index.ts +++ b/lib/routes/0xxx/index.ts @@ -134,10 +134,9 @@ export const route: Route = { description: 'Filter', }, }, - description: `:::tip + description: `::: tip To subscribe to [Movie HD 1080p](https://0xxx.ws?category=Movie-HD-1080p), where the source URL is \`https://0xxx.ws?category=Movie-HD-1080p\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/0xxx/category=Movie-HD-1080p\`](https://rsshub.app/0xxx/category=Movie-HD-1080p). -::: -`, +:::`, categories: ['multimedia'], features: { requireConfig: false, diff --git a/lib/routes/10000link/info.ts b/lib/routes/10000link/info.ts index bdffd3c32e1f..3b7adf2caf75 100644 --- a/lib/routes/10000link/info.ts +++ b/lib/routes/10000link/info.ts @@ -214,8 +214,7 @@ export const route: Route = { | 政策 | 规划 | 案例 | 职场 | 供应链票据 | | ------------- | ------------- | -------------- | ------------ | ---------- | -| newslists/A03 | newslists/A04 | newslists/GL03 | newslists/ZC | newsBill | -`, +| newslists/A03 | newslists/A04 | newslists/GL03 | newslists/ZC | newsBill |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/10jqka/realtimenews.ts b/lib/routes/10jqka/realtimenews.ts index 25a3182f73e2..684217bfd7fb 100644 --- a/lib/routes/10jqka/realtimenews.ts +++ b/lib/routes/10jqka/realtimenews.ts @@ -79,14 +79,13 @@ export const route: Route = { example: '/10jqka/realtimenews', parameters: { tag: '标签,默认为全部' }, description: `::: tip - 若订阅 [7×24小时要闻直播](https://news.10jqka.com.cn/realtimenews.html) 的 \`公告\` 标签。将 \`公告\` 作为标签参数填入,此时路由为 [\`/10jqka/realtimenews/公告\`](https://rsshub.app/10jqka/realtimenews/公告)。 - - 若订阅 [7×24小时要闻直播](https://news.10jqka.com.cn/realtimenews.html) 的 \`公告\` 和 \`A股\` 标签。将 \`公告,A股\` 作为标签参数填入,此时路由为 [\`/10jqka/realtimenews/公告,A股\`](https://rsshub.app/10jqka/realtimenews/公告,A股)。 +若订阅 [7×24 小时要闻直播](https://news.10jqka.com.cn/realtimenews.html) 的 \`公告\` 标签。将 \`公告\` 作为标签参数填入,此时路由为 [\`/10jqka/realtimenews/公告\`](https://rsshub.app/10jqka/realtimenews/公告)。 + +若订阅 [7×24 小时要闻直播](https://news.10jqka.com.cn/realtimenews.html) 的 \`公告\` 和 \`A股\` 标签。将 \`公告,A股\` 作为标签参数填入,此时路由为 [\`/10jqka/realtimenews/公告,A股\`](https://rsshub.app/10jqka/realtimenews/公告,A股)。 ::: -| 全部 | 重要 | A股 | 港股 | 美股 | 机会 | 异动 | 公告 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | - `, +| 全部 | 重要 | A 股 | 港股 | 美股 | 机会 | 异动 | 公告 | +| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |`, categories: ['finance'], features: { diff --git a/lib/routes/141jav/index.tsx b/lib/routes/141jav/index.tsx index f8693934e28d..350aa2b25cce 100644 --- a/lib/routes/141jav/index.tsx +++ b/lib/routes/141jav/index.tsx @@ -27,25 +27,25 @@ export const route: Route = { **示例说明** -- \`/141jav/new\` +- \`/141jav/new\` - 仅当类型为 \`new\` \`popular\` 或 \`random\` 时关键词为 **空** + 仅当类型为 \`new\` \`popular\` 或 \`random\` 时关键词为 **空** -- \`/141jav/popular/30\` +- \`/141jav/popular/30\` - \`popular\` \`random\` 类型的关键词可填写 \`7\` \`30\` 或 \`60\` 三个 **日期范围** 之一,分别对应 **7 天**、**30 天** 或 **60 天内** + \`popular\` \`random\` 类型的关键词可填写 \`7\` \`30\` 或 \`60\` 三个 **日期范围** 之一,分别对应 **7 天**、**30 天** 或 **60 天内** -- \`/141jav/actress/Yua%20Mikami\` +- \`/141jav/actress/Yua%20Mikami\` - \`actress\` 类型的关键词必须填写 **演员名** ,可在 [此处](https://141jav.com/actress/) 演员单页链接中获取 + \`actress\` 类型的关键词必须填写 **演员名** ,可在 [此处](https://141jav.com/actress/) 演员单页链接中获取 -- \`/141jav/tag/Adult%20Awards\` +- \`/141jav/tag/Adult%20Awards\` - \`tag\` 类型的关键词必须填写 **标签名** 且标签中的 \`/\` 必须替换为 \`%2F\` ,可在 [此处](https://141jav.com/tag/) 标签单页链接中获取 + \`tag\` 类型的关键词必须填写 **标签名** 且标签中的 \`/\` 必须替换为 \`%2F\` ,可在 [此处](https://141jav.com/tag/) 标签单页链接中获取 -- \`/141jav/date/2020/07/30\` +- \`/141jav/date/2020/07/30\` - \`date\` 类型的关键词必须填写 **日期(年/月/日)**`, + \`date\` 类型的关键词必须填写 **日期 (年 / 月 / 日)**`, features: { nsfw: true, }, diff --git a/lib/routes/141jav/namespace.ts b/lib/routes/141jav/namespace.ts index e114d671eb28..023fa07903b8 100644 --- a/lib/routes/141jav/namespace.ts +++ b/lib/routes/141jav/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: '141JAV', url: '141jav.com', description: `::: tip -官方提供的订阅源不支持 BT 下载订阅,地址为 [https://141jav.com/feeds/](https://141jav.com/feeds/) +官方提供的订阅源不支持 BT 下载订阅,地址为 :::`, lang: 'en', }; diff --git a/lib/routes/141ppv/index.tsx b/lib/routes/141ppv/index.tsx index 8a360f917c53..4d27b39b20c3 100644 --- a/lib/routes/141ppv/index.tsx +++ b/lib/routes/141ppv/index.tsx @@ -27,25 +27,25 @@ export const route: Route = { **示例说明** -- \`/141ppv/new\` +- \`/141ppv/new\` - 仅当类型为 \`new\` \`popular\` 或 \`random\` 时关键词为 **空** + 仅当类型为 \`new\` \`popular\` 或 \`random\` 时关键词为 **空** -- \`/141ppv/popular/30\` +- \`/141ppv/popular/30\` - \`popular\` \`random\` 类型的关键词可填写 \`7\` \`30\` 或 \`60\` 三个 **日期范围** 之一,分别对应 **7 天**、**30 天** 或 **60 天内** + \`popular\` \`random\` 类型的关键词可填写 \`7\` \`30\` 或 \`60\` 三个 **日期范围** 之一,分别对应 **7 天**、**30 天** 或 **60 天内** -- \`/141ppv/actress/Yua%20Mikami\` +- \`/141ppv/actress/Yua%20Mikami\` - \`actress\` 类型的关键词必须填写 **演员名** ,可在 [此处](https://141ppv.com/actress/) 演员单页链接中获取 + \`actress\` 类型的关键词必须填写 **演员名** ,可在 [此处](https://141ppv.com/actress/) 演员单页链接中获取 -- \`/141ppv/tag/Adult%20Awards\` +- \`/141ppv/tag/Adult%20Awards\` - \`tag\` 类型的关键词必须填写 **标签名** 且标签中的 \`/\` 必须替换为 \`%2F\` ,可在 [此处](https://141ppv.com/tag/) 标签单页链接中获取 + \`tag\` 类型的关键词必须填写 **标签名** 且标签中的 \`/\` 必须替换为 \`%2F\` ,可在 [此处](https://141ppv.com/tag/) 标签单页链接中获取 -- \`/141ppv/date/2020/07/30\` +- \`/141ppv/date/2020/07/30\` - \`date\` 类型的关键词必须填写 **日期(年/月/日)**`, + \`date\` 类型的关键词必须填写 **日期 (年 / 月 / 日)**`, features: { nsfw: true, }, diff --git a/lib/routes/141ppv/namespace.ts b/lib/routes/141ppv/namespace.ts index 2147332b3b6f..f093b0178388 100644 --- a/lib/routes/141ppv/namespace.ts +++ b/lib/routes/141ppv/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: '141PPV', url: '141ppv.com', description: `::: tip -官方提供的订阅源不支持 BT 下载订阅,地址为 [https://141ppv.com/feeds/](https://141ppv.com/feeds/) +官方提供的订阅源不支持 BT 下载订阅,地址为 :::`, lang: 'en', }; diff --git a/lib/routes/163/dy.ts b/lib/routes/163/dy.ts index 5f2a29f8c784..8b77df942391 100644 --- a/lib/routes/163/dy.ts +++ b/lib/routes/163/dy.ts @@ -22,9 +22,9 @@ export const route: Route = { name: '更新', maintainers: ['HendricksZheng'], handler, - description: `1. 在[网易号搜索页面](https://dy.163.com/v2/media/tosearch.html) 搜索想要订阅的网易号。 - 2. 打开网易号的任意文章。 - 3. 查看源代码,搜索 \`data-wemediaid\`,查看紧随其后的引号内的属性值(类似 \`W1966190042455428950\`)即为网易号 ID。`, + description: `1. 在[网易号搜索页面](https://dy.163.com/v2/media/tosearch.html) 搜索想要订阅的网易号。 +2. 打开网易号的任意文章。 +3. 查看源代码,搜索 \`data-wemediaid\`,查看紧随其后的引号内的属性值(类似 \`W1966190042455428950\`)即为网易号 ID。`, }; async function handler(ctx) { diff --git a/lib/routes/163/music/artist-songs.ts b/lib/routes/163/music/artist-songs.ts index d60905d3388c..3ebf5cb07b47 100644 --- a/lib/routes/163/music/artist-songs.ts +++ b/lib/routes/163/music/artist-songs.ts @@ -24,7 +24,7 @@ export const route: Route = { async function handler(ctx) { const id = ctx.req.param('id'); - const { data } = await got(`https://music.163.com/api/v1/artist/songs`, { + const { data } = await got('https://music.163.com/api/v1/artist/songs', { headers: { Referer: 'https://music.163.com/', }, diff --git a/lib/routes/163/news/rank.ts b/lib/routes/163/news/rank.ts index aee0db375f9d..3436f581bc3a 100644 --- a/lib/routes/163/news/rank.ts +++ b/lib/routes/163/news/rank.ts @@ -100,14 +100,14 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 全站新闻 **点击榜** 的统计时间仅包含 “24 小时”、“本周”、“本月”,不包含 “1 小时”。即可用的\`time\`参数为\`day\`、\`week\`、\`month\`。 +全站新闻 **点击榜** 的统计时间仅包含 “24 小时”、“本周”、“本月”,不包含 “1 小时”。即可用的\`time\`参数为\`day\`、\`week\`、\`month\`。 - 其他分类 **点击榜** 的统计时间仅包含 “1 小时”、“24 小时”、“本周”。即可用的\`time\`参数为\`hour\`、\`day\`、\`week\`。 +其他分类 **点击榜** 的统计时间仅包含 “1 小时”、“24 小时”、“本周”。即可用的\`time\`参数为\`hour\`、\`day\`、\`week\`。 - 而所有分类(包括全站)的 **跟贴榜** 的统计时间皆仅包含 “24 小时”、“本周”、“本月”。即可用的\`time\`参数为\`day\`、\`week\`、\`month\`。 +而所有分类(包括全站)的 **跟贴榜** 的统计时间皆仅包含 “24 小时”、“本周”、“本月”。即可用的\`time\`参数为\`day\`、\`week\`、\`month\`。 ::: - 新闻分类: +新闻分类: | 全站 | 新闻 | 娱乐 | 体育 | 财经 | 科技 | 汽车 | 女人 | 房产 | 游戏 | 旅游 | 教育 | | ----- | ---- | ------------- | ------ | ----- | ---- | ---- | ---- | ----- | ---- | ------ | ---- | diff --git a/lib/routes/163/news/special.ts b/lib/routes/163/news/special.ts index 70b969f8fdba..ea7d1948c278 100644 --- a/lib/routes/163/news/special.ts +++ b/lib/routes/163/news/special.ts @@ -52,49 +52,49 @@ async function handler(ctx) { let type; switch (selectedType) { case 1: - type = `BD21K0DLwangning`; // 轻松一刻 + type = 'BD21K0DLwangning'; // 轻松一刻 break; case 2: - type = `CICMICLUwangning`; // 槽值 + type = 'CICMICLUwangning'; // 槽值 break; case 3: - type = `CICMOMBLwangning`; // 人间 + type = 'CICMOMBLwangning'; // 人间 break; case 4: - type = `CICMPVC5wangning`; // 大国小民 + type = 'CICMPVC5wangning'; // 大国小民 break; case 5: - type = `CICMLCOUwangning`; // 三三有梗 + type = 'CICMLCOUwangning'; // 三三有梗 break; case 6: - type = `D551V75Cwangning`; // 数读 + type = 'D551V75Cwangning'; // 数读 break; case 7: - type = `D55253RHwangning`; // 看客 + type = 'D55253RHwangning'; // 看客 break; case 8: - type = `D553A53Lwangning`; // 下划线 + type = 'D553A53Lwangning'; // 下划线 break; case 9: - type = `D553PGHQwangning`; // 谈心社 + type = 'D553PGHQwangning'; // 谈心社 break; case 10: - type = `CICMS5BIwangning`; // 哒哒 + type = 'CICMS5BIwangning'; // 哒哒 break; case 11: - type = `CQ9UDVKOwangning`; // 胖编怪聊 + type = 'CQ9UDVKOwangning'; // 胖编怪聊 break; case 12: - type = `CQ9UJIJNwangning`; // 曲一刀 + type = 'CQ9UJIJNwangning'; // 曲一刀 break; case 13: - type = `BD284UM8wangning`; // 今日之声 + type = 'BD284UM8wangning'; // 今日之声 break; case 14: - type = `CICMMGBHwangning`; // 浪潮 + type = 'CICMMGBHwangning'; // 浪潮 break; case 15: - type = `D5543R68wangning`; // 沸点 + type = 'D5543R68wangning'; // 沸点 break; default: break; diff --git a/lib/routes/163/today.ts b/lib/routes/163/today.ts index 5d34784b9e56..d259642cefb1 100644 --- a/lib/routes/163/today.ts +++ b/lib/routes/163/today.ts @@ -30,7 +30,7 @@ export const route: Route = { handler, url: 'wp.m.163.com/163/html/newsapp/todayFocus/index.html', description: `::: tip - 参数 **需要获取全文** 设置为 \`true\` \`yes\` \`t\` \`y\` 等值后,RSS 会携带该新闻条目的对应全文。 +参数 **需要获取全文** 设置为 \`true\` \`yes\` \`t\` \`y\` 等值后,RSS 会携带该新闻条目的对应全文。 :::`, }; diff --git a/lib/routes/18comic/album.ts b/lib/routes/18comic/album.ts index e7d423b7db85..c7a5f9a0dab6 100644 --- a/lib/routes/18comic/album.ts +++ b/lib/routes/18comic/album.ts @@ -28,7 +28,7 @@ export const route: Route = { handler, url: 'jmcomic.group/', description: `::: tip - 专辑 id 不包括 URL 中标题的部分。 +专辑 id 不包括 URL 中标题的部分。 :::`, }; diff --git a/lib/routes/18comic/index.ts b/lib/routes/18comic/index.ts index 433be226d3c8..1e6881ef35eb 100644 --- a/lib/routes/18comic/index.ts +++ b/lib/routes/18comic/index.ts @@ -31,19 +31,19 @@ export const route: Route = { | ---- | -------- | ------ | ------ | ------ | ----- | ------ | | all | another | doujin | hanman | meiman | short | single | - 时间范围 +时间范围 | 全部 | 今天 | 这周 | 本月 | | ---- | ---- | ---- | ---- | | a | t | w | m | - 排列顺序 +排列顺序 | 最新 | 最多点阅的 | 最多图片 | 最高评分 | 最多评论 | 最多爱心 | | ---- | ---------- | -------- | -------- | -------- | -------- | | mr | mv | mp | tr | md | tf | - 关键字(供参考) +关键字(供参考) | YAOI | 女性向 | NTR | 非 H | 3D | 獵奇 | | ---- | ------ | --- | ---- | -- | ---- |`, diff --git a/lib/routes/18comic/search.ts b/lib/routes/18comic/search.ts index 09ff46b5a1c5..ffd8324b8070 100644 --- a/lib/routes/18comic/search.ts +++ b/lib/routes/18comic/search.ts @@ -36,7 +36,7 @@ export const route: Route = { handler, url: 'jmcomic.group/', description: `::: tip - 关键字必须超过两个字,这是来自网站的限制。 +关键字必须超过两个字,这是来自网站的限制。 :::`, }; diff --git a/lib/routes/199it/index.tsx b/lib/routes/199it/index.tsx index de4747a76d23..39d540fd3a32 100644 --- a/lib/routes/199it/index.tsx +++ b/lib/routes/199it/index.tsx @@ -220,23 +220,22 @@ export const route: Route = {
更多分类 -| 分类 | ID | -| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| [报告](http://www.199it.com/archives/category/report) | [archives/category/report](https://rsshub.app/199it/archives/category/report) | -| [新兴产业](http://www.199it.com/archives/category/emerging) | [archives/category/emerging](https://rsshub.app/199it/archives/category/emerging) | -| [金融科技](http://www.199it.com/archives/category/fintech) | [archives/category/fintech](https://rsshub.app/199it/archives/category/fintech) | -| [共享经济](http://www.199it.com/archives/category/sharingeconomy) | [archives/category/sharingeconomy](https://rsshub.app/199it/archives/category/sharingeconomy) | -| [移动互联网](http://www.199it.com/archives/category/mobile-internet) | [archives/category/mobile-internet](https://rsshub.app/199it/archives/category/mobile-internet) | -| [电子商务](http://www.199it.com/archives/category/electronic-commerce) | [archives/category/electronic-commerce](https://rsshub.app/199it/archives/category/electronic-commerce) | -| [社交网络](http://www.199it.com/archives/category/social-network) | [archives/category/social-network](https://rsshub.app/199it/archives/category/social-network) | -| [网络广告](http://www.199it.com/archives/category/advertising) | [archives/category/advertising](https://rsshub.app/199it/archives/category/advertising) | -| [投资&经济,互联网金融](http://www.199it.com/archives/category/economic-data) | [archives/category/economic-data](https://rsshub.app/199it/archives/category/economic-data) | -| [服务](http://www.199it.com/archives/category/service) | [archives/category/service](https://rsshub.app/199it/archives/category/service) | -| [网络服务行业](http://www.199it.com/archives/category/dataindustry) | [archives/category/dataindustry](https://rsshub.app/199it/archives/category/dataindustry) | -| [用户研究](http://www.199it.com/archives/category/internet-users) | [archives/category/internet-users](https://rsshub.app/199it/archives/category/internet-users) | +| 分类 | ID | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| [报告](http://www.199it.com/archives/category/report) | [archives/category/report](https://rsshub.app/199it/archives/category/report) | +| [新兴产业](http://www.199it.com/archives/category/emerging) | [archives/category/emerging](https://rsshub.app/199it/archives/category/emerging) | +| [金融科技](http://www.199it.com/archives/category/fintech) | [archives/category/fintech](https://rsshub.app/199it/archives/category/fintech) | +| [共享经济](http://www.199it.com/archives/category/sharingeconomy) | [archives/category/sharingeconomy](https://rsshub.app/199it/archives/category/sharingeconomy) | +| [移动互联网](http://www.199it.com/archives/category/mobile-internet) | [archives/category/mobile-internet](https://rsshub.app/199it/archives/category/mobile-internet) | +| [电子商务](http://www.199it.com/archives/category/electronic-commerce) | [archives/category/electronic-commerce](https://rsshub.app/199it/archives/category/electronic-commerce) | +| [社交网络](http://www.199it.com/archives/category/social-network) | [archives/category/social-network](https://rsshub.app/199it/archives/category/social-network) | +| [网络广告](http://www.199it.com/archives/category/advertising) | [archives/category/advertising](https://rsshub.app/199it/archives/category/advertising) | +| [投资 & 经济,互联网金融](http://www.199it.com/archives/category/economic-data) | [archives/category/economic-data](https://rsshub.app/199it/archives/category/economic-data) | +| [服务](http://www.199it.com/archives/category/service) | [archives/category/service](https://rsshub.app/199it/archives/category/service) | +| [网络服务行业](http://www.199it.com/archives/category/dataindustry) | [archives/category/dataindustry](https://rsshub.app/199it/archives/category/dataindustry) | +| [用户研究](http://www.199it.com/archives/category/internet-users) | [archives/category/internet-users](https://rsshub.app/199it/archives/category/internet-users) | -
-`, +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/1lou/index.ts b/lib/routes/1lou/index.ts index 26768f6af4a2..88bfc90e82a5 100644 --- a/lib/routes/1lou/index.ts +++ b/lib/routes/1lou/index.ts @@ -115,13 +115,13 @@ export const route: Route = { example: '/1lou/forum-2-1', parameters: { params: '路径参数,可以在对应页面的 URL 中找到' }, description: `::: tip - \`1lou.me/\` 后的内容填入 params 参数,以下是几个例子: +\`1lou.me/\` 后的内容填入 params 参数,以下是几个例子: - 若订阅 [大陆电视剧](https://www.1lou.me/forum-2-1.htm?tagids=0_97_0_0),网址为 \`https://www.1lou.me/forum-2-1.htm?tagids=0_97_0_0\`。截取 \`https://www.1lou.me/\` 到末尾 \`.htm\` 的部分 \`forum-2-1\` 作为参数,并补充 \`tagids\`,此时路由为 [\`/1lou/forum-2-1?tagids=0_97_0_0\`](https://rsshub.app/1lou/forum-2-1?tagids=0_97_0_0)。 - - 若订阅 [最新发帖电视剧](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)。 +若订阅 [大陆电视剧](https://www.1lou.me/forum-2-1.htm?tagids=0_97_0_0),网址为 \`https://www.1lou.me/forum-2-1.htm?tagids=0_97_0_0\`。截取 \`https://www.1lou.me/\` 到末尾 \`.htm\` 的部分 \`forum-2-1\` 作为参数,并补充 \`tagids\`,此时路由为 [\`/1lou/forum-2-1?tagids=0_97_0_0\`](https://rsshub.app/1lou/forum-2-1?tagids=0_97_0_0)。 + +若订阅 [最新发帖电视剧](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/1point3acres/category.ts b/lib/routes/1point3acres/category.ts index 8e7914684495..70e51ad01668 100644 --- a/lib/routes/1point3acres/category.ts +++ b/lib/routes/1point3acres/category.ts @@ -25,16 +25,16 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 更多标签可以在 [标签列表](https://instant.1point3acres.com/tags) 中找到。 +更多标签可以在 [标签列表](https://instant.1point3acres.com/tags) 中找到。 ::: - 分类 +分类 | 热门帖子 | 最新帖子 | | -------- | -------- | | hot | new | - 排序方式 +排序方式 | 最新回复 | 最新发布 | | -------- | -------- | diff --git a/lib/routes/1point3acres/offer.tsx b/lib/routes/1point3acres/offer.tsx index ebfc7315ceee..a7f1797c2da9 100644 --- a/lib/routes/1point3acres/offer.tsx +++ b/lib/routes/1point3acres/offer.tsx @@ -28,12 +28,14 @@ export const route: Route = { handler, url: 'offer.1point3acres.com/', description: `::: tip 三个 id 获取方式 - 1. 打开 [https://offer.1point3acres.com](https://offer.1point3acres.com) - 2. 打开控制台 - 3. 切换到 Network 面板 - 4. 点击 搜索 按钮 - 5. 点击 results?ps=15&pg=1 POST 请求 - 6. 找到 Request Payload 请求参数,例如 \`filters: {planyr: "13", planmajor: "1", outname_w: "ACADIAU"}\` ,则三个 id 分别为: 13,1,ACADIAU + +1. 打开 +2. 打开控制台 +3. 切换到 Network 面板 +4. 点击 搜索 按钮 +5. 点击 results?ps=15\\&pg=1 POST 请求 +6. 找到 Request Payload 请求参数,例如 \`filters: {planyr: "13", planmajor: "1", outname_w: "ACADIAU"}\` ,则三个 id 分别为: 13,1,ACADIAU + :::`, }; diff --git a/lib/routes/1point3acres/section.ts b/lib/routes/1point3acres/section.ts index 97109dfc552a..e9a304b49439 100644 --- a/lib/routes/1point3acres/section.ts +++ b/lib/routes/1point3acres/section.ts @@ -48,13 +48,13 @@ export const route: Route = { | 海外求职 | 38 | | 签证移民 | 265 | - 分类 +分类 | 热门帖子 | 最新帖子 | | -------- | -------- | | hot | new | - 排序方式 +排序方式 | 最新回复 | 最新发布 | | -------- | -------- | diff --git a/lib/routes/1point3acres/thread.ts b/lib/routes/1point3acres/thread.ts index 063d062c7811..624f4550ab92 100644 --- a/lib/routes/1point3acres/thread.ts +++ b/lib/routes/1point3acres/thread.ts @@ -18,7 +18,7 @@ export const route: Route = { | -------- | -------- | | hot | new | - 排序方式 +排序方式 | 最新回复 | 最新发布 | | -------- | -------- | diff --git a/lib/routes/2023game/index.ts b/lib/routes/2023game/index.ts index 2c10cfb5b745..13ad92163cba 100644 --- a/lib/routes/2023game/index.ts +++ b/lib/routes/2023game/index.ts @@ -27,9 +27,9 @@ export const route: Route = { url: 'www.2023game.com/', description: `分类 -| PS4游戏 | switch游戏 | 3DS游戏 | PSV游戏 | Xbox360 | PS3游戏 | 世嘉MD/SS | PSP游戏 | PC周边 | 怀旧掌机 | 怀旧主机 | PS4教程 | PS4金手指 | switch金手指 | switch教程 | switch补丁 | switch主题 | switch存档 | -| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -| ps4 | sgame | 3ds | psv | jiaocheng | ps3yx | zhuji.md | zhangji.psp | pcgame | zhangji | zhuji | ps4.psjc | ps41.ps4pkg | nsaita.cundang | nsaita.pojie | nsaita.buding | nsaita.zhutie | nsaita.zhuti |`, +| PS4 游戏 | switch 游戏 | 3DS 游戏 | PSV 游戏 | Xbox360 | PS3 游戏 | 世嘉 MD/SS | PSP 游戏 | PC 周边 | 怀旧掌机 | 怀旧主机 | PS4 教程 | PS4 金手指 | switch 金手指 | switch 教程 | switch 补丁 | switch 主题 | switch 存档 | +| -------- | ----------- | -------- | -------- | --------- | -------- | ---------- | ----------- | ------- | -------- | -------- | -------- | ----------- | -------------- | ------------ | ------------- | ------------- | ------------ | +| ps4 | sgame | 3ds | psv | jiaocheng | ps3yx | zhuji.md | zhangji.psp | pcgame | zhangji | zhuji | ps4.psjc | ps41.ps4pkg | nsaita.cundang | nsaita.pojie | nsaita.buding | nsaita.zhutie | nsaita.zhuti |`, }; async function handler(ctx: Context): Promise { @@ -41,7 +41,7 @@ async function handler(ctx: Context): Promise { const response = await got(currentUrl); const $ = load(response.data as any); - let selector = `.news`; + let selector = '.news'; if (tab !== 'all') { selector = `#${tab} > ${selector}`; } diff --git a/lib/routes/21caijing/channel.ts b/lib/routes/21caijing/channel.ts index 1678d0a89f60..0ec8a3dc6a2d 100644 --- a/lib/routes/21caijing/channel.ts +++ b/lib/routes/21caijing/channel.ts @@ -197,61 +197,61 @@ export const route: Route = { #### [投资通](https://m.21jingji.com/#/channel/investment) -| [推荐](https://m.21jingji.com/#/channel/investment) | [盘前情报](https://m.21jingji.com/#/channel/premkt) | [公司洞察](https://m.21jingji.com/#/channel/gsdc) | [南财研选](https://m.21jingji.com/#/channel/ncyx) | [龙虎榜](https://m.21jingji.com/#/channel/lhb) | [公告精选](https://m.21jingji.com/#/channel/notice) | [牛熊透视](https://m.21jingji.com/#/channel/bullbear) | [一周前瞻](https://m.21jingji.com/#/channel/dailyfx) | [财经日历](https://m.21jingji.com/#/) | [风口掘金](https://m.21jingji.com/#/channel/windgap) | [实时解盘](https://m.21jingji.com/#/channel/marketanalysis) | [调研内参](https://m.21jingji.com/#/channel/research) | [趋势前瞻](https://m.21jingji.com/#/channel/tendency) | [硬核选基](https://m.21jingji.com/#/channel/yhxj) | [3 分钟理财](https://m.21jingji.com/#/channel/sfzlc) | [AI 智讯](https://m.21jingji.com/#/channel/aizx) | [北向资金](https://m.21jingji.com/#/channel/northmoney) | -| --------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------- | -| [投资通/推荐](https://rsshub.app/21caijing/channel/投资通/推荐) | [投资通/盘前情报](https://rsshub.app/21caijing/channel/投资通/盘前情报) | [投资通/公司洞察](https://rsshub.app/21caijing/channel/投资通/公司洞察) | [投资通/南财研选](https://rsshub.app/21caijing/channel/投资通/南财研选) | [投资通/龙虎榜](https://rsshub.app/21caijing/channel/投资通/龙虎榜) | [投资通/公告精选](https://rsshub.app/21caijing/channel/投资通/公告精选) | [投资通/牛熊透视](https://rsshub.app/21caijing/channel/投资通/牛熊透视) | [投资通/一周前瞻](https://rsshub.app/21caijing/channel/投资通/一周前瞻) | [投资通/财经日历](https://rsshub.app/21caijing/channel/投资通/财经日历) | [投资通/风口掘金](https://rsshub.app/21caijing/channel/投资通/风口掘金) | [投资通/实时解盘](https://rsshub.app/21caijing/channel/投资通/实时解盘) | [投资通/调研内参](https://rsshub.app/21caijing/channel/投资通/调研内参) | [投资通/趋势前瞻](https://rsshub.app/21caijing/channel/投资通/趋势前瞻) | [投资通/硬核选基](https://rsshub.app/21caijing/channel/投资通/硬核选基) | [投资通/3 分钟理财](https://rsshub.app/21caijing/channel/投资通/3分钟理财) | [投资通/AI 智讯](https://rsshub.app/21caijing/channel/投资通/AI智讯) | [投资通/北向资金](https://rsshub.app/21caijing/channel/投资通/北向资金) | +| [推荐](https://m.21jingji.com/#/channel/investment) | [盘前情报](https://m.21jingji.com/#/channel/premkt) | [公司洞察](https://m.21jingji.com/#/channel/gsdc) | [南财研选](https://m.21jingji.com/#/channel/ncyx) | [龙虎榜](https://m.21jingji.com/#/channel/lhb) | [公告精选](https://m.21jingji.com/#/channel/notice) | [牛熊透视](https://m.21jingji.com/#/channel/bullbear) | [一周前瞻](https://m.21jingji.com/#/channel/dailyfx) | [财经日历](https://m.21jingji.com/#/) | [风口掘金](https://m.21jingji.com/#/channel/windgap) | [实时解盘](https://m.21jingji.com/#/channel/marketanalysis) | [调研内参](https://m.21jingji.com/#/channel/research) | [趋势前瞻](https://m.21jingji.com/#/channel/tendency) | [硬核选基](https://m.21jingji.com/#/channel/yhxj) | [3 分钟理财](https://m.21jingji.com/#/channel/sfzlc) | [AI 智讯](https://m.21jingji.com/#/channel/aizx) | [北向资金](https://m.21jingji.com/#/channel/northmoney) | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| [投资通 / 推荐](https://rsshub.app/21caijing/channel/投资通/推荐) | [投资通 / 盘前情报](https://rsshub.app/21caijing/channel/投资通/盘前情报) | [投资通 / 公司洞察](https://rsshub.app/21caijing/channel/投资通/公司洞察) | [投资通 / 南财研选](https://rsshub.app/21caijing/channel/投资通/南财研选) | [投资通 / 龙虎榜](https://rsshub.app/21caijing/channel/投资通/龙虎榜) | [投资通 / 公告精选](https://rsshub.app/21caijing/channel/投资通/公告精选) | [投资通 / 牛熊透视](https://rsshub.app/21caijing/channel/投资通/牛熊透视) | [投资通 / 一周前瞻](https://rsshub.app/21caijing/channel/投资通/一周前瞻) | [投资通 / 财经日历](https://rsshub.app/21caijing/channel/投资通/财经日历) | [投资通 / 风口掘金](https://rsshub.app/21caijing/channel/投资通/风口掘金) | [投资通 / 实时解盘](https://rsshub.app/21caijing/channel/投资通/实时解盘) | [投资通 / 调研内参](https://rsshub.app/21caijing/channel/投资通/调研内参) | [投资通 / 趋势前瞻](https://rsshub.app/21caijing/channel/投资通/趋势前瞻) | [投资通 / 硬核选基](https://rsshub.app/21caijing/channel/投资通/硬核选基) | [投资通 / 3 分钟理财](https://rsshub.app/21caijing/channel/投资通/3分钟理财) | [投资通 / AI 智讯](https://rsshub.app/21caijing/channel/投资通/AI智讯) | [投资通 / 北向资金](https://rsshub.app/21caijing/channel/投资通/北向资金) | #### [金融](https://m.21jingji.com/#/channel/finance) -| [动态](https://m.21jingji.com/#/channel/finance) | [最保险](https://m.21jingji.com/#/channel/Insurance) | [资管](https://m.21jingji.com/#/channel/21zg) | [数字金融](https://m.21jingji.com/#/channel/szjr) | [私人银行](https://m.21jingji.com/#/channel/sryh) | [普惠](https://m.21jingji.com/#/channel/puhui) | [观债](https://m.21jingji.com/#/channel/21gz) | [金融研究](https://m.21jingji.com/#/channel/jryj) | [投教基地](https://m.21jingji.com/#/channel/tjjd) | [银行](https://m.21jingji.com/#/channel/bank) | [非银金融](https://m.21jingji.com/#/channel/nonbank) | [金融人事](https://m.21jingji.com/#/channel/jrrs) | -| ----------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [金融/动态](https://rsshub.app/21caijing/channel/金融/动态) | [金融/最保险](https://rsshub.app/21caijing/channel/金融/最保险) | [金融/资管](https://rsshub.app/21caijing/channel/金融/资管) | [金融/数字金融](https://rsshub.app/21caijing/channel/金融/数字金融) | [金融/私人银行](https://rsshub.app/21caijing/channel/金融/私人银行) | [金融/普惠](https://rsshub.app/21caijing/channel/金融/普惠) | [金融/观债](https://rsshub.app/21caijing/channel/金融/观债) | [金融/金融研究](https://rsshub.app/21caijing/channel/金融/金融研究) | [金融/投教基地](https://rsshub.app/21caijing/channel/金融/投教基地) | [金融/银行](https://rsshub.app/21caijing/channel/金融/银行) | [金融/非银金融](https://rsshub.app/21caijing/channel/金融/非银金融) | [金融/金融人事](https://rsshub.app/21caijing/channel/金融/金融人事) | +| [动态](https://m.21jingji.com/#/channel/finance) | [最保险](https://m.21jingji.com/#/channel/Insurance) | [资管](https://m.21jingji.com/#/channel/21zg) | [数字金融](https://m.21jingji.com/#/channel/szjr) | [私人银行](https://m.21jingji.com/#/channel/sryh) | [普惠](https://m.21jingji.com/#/channel/puhui) | [观债](https://m.21jingji.com/#/channel/21gz) | [金融研究](https://m.21jingji.com/#/channel/jryj) | [投教基地](https://m.21jingji.com/#/channel/tjjd) | [银行](https://m.21jingji.com/#/channel/bank) | [非银金融](https://m.21jingji.com/#/channel/nonbank) | [金融人事](https://m.21jingji.com/#/channel/jrrs) | +| ------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [金融 / 动态](https://rsshub.app/21caijing/channel/金融/动态) | [金融 / 最保险](https://rsshub.app/21caijing/channel/金融/最保险) | [金融 / 资管](https://rsshub.app/21caijing/channel/金融/资管) | [金融 / 数字金融](https://rsshub.app/21caijing/channel/金融/数字金融) | [金融 / 私人银行](https://rsshub.app/21caijing/channel/金融/私人银行) | [金融 / 普惠](https://rsshub.app/21caijing/channel/金融/普惠) | [金融 / 观债](https://rsshub.app/21caijing/channel/金融/观债) | [金融 / 金融研究](https://rsshub.app/21caijing/channel/金融/金融研究) | [金融 / 投教基地](https://rsshub.app/21caijing/channel/金融/投教基地) | [金融 / 银行](https://rsshub.app/21caijing/channel/金融/银行) | [金融 / 非银金融](https://rsshub.app/21caijing/channel/金融/非银金融) | [金融 / 金融人事](https://rsshub.app/21caijing/channel/金融/金融人事) | #### [宏观](https://m.21jingji.com/#/channel/politics) #### [学习经济](https://m.21jingji.com/#/jujiao/xxjjIndexV3) -| [经济思想](https://m.21jingji.com/#/https://m.21jingji.com/news/xxjj) | [学习经济卡片](https://m.21jingji.com/#/channel/mrjj) | [高质量发展](https://m.21jingji.com/#/channel/gzlfz) | [经济政策](https://m.21jingji.com/#/channel/jjzc) | [广东在行动](https://m.21jingji.com/#/channel/gdzxd) | [数说经济](https://m.21jingji.com/#/channel/ssjj) | [学习视频](https://m.21jingji.com/#/channel/xxsp) | [学习党史](https://m.21jingji.com/#/) | -| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| [学习经济/经济思想](https://rsshub.app/21caijing/channel/学习经济/经济思想) | [学习经济/学习经济卡片](https://rsshub.app/21caijing/channel/学习经济/学习经济卡片) | [学习经济/高质量发展](https://rsshub.app/21caijing/channel/学习经济/高质量发展) | [学习经济/经济政策](https://rsshub.app/21caijing/channel/学习经济/经济政策) | [学习经济/广东在行动](https://rsshub.app/21caijing/channel/学习经济/广东在行动) | [学习经济/数说经济](https://rsshub.app/21caijing/channel/学习经济/数说经济) | [学习经济/学习视频](https://rsshub.app/21caijing/channel/学习经济/学习视频) | [学习经济/学习党史](https://rsshub.app/21caijing/channel/学习经济/学习党史) | +| [经济思想](https://m.21jingji.com/#/https://m.21jingji.com/news/xxjj) | [学习经济卡片](https://m.21jingji.com/#/channel/mrjj) | [高质量发展](https://m.21jingji.com/#/channel/gzlfz) | [经济政策](https://m.21jingji.com/#/channel/jjzc) | [广东在行动](https://m.21jingji.com/#/channel/gdzxd) | [数说经济](https://m.21jingji.com/#/channel/ssjj) | [学习视频](https://m.21jingji.com/#/channel/xxsp) | [学习党史](https://m.21jingji.com/#/) | +| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| [学习经济 / 经济思想](https://rsshub.app/21caijing/channel/学习经济/经济思想) | [学习经济 / 学习经济卡片](https://rsshub.app/21caijing/channel/学习经济/学习经济卡片) | [学习经济 / 高质量发展](https://rsshub.app/21caijing/channel/学习经济/高质量发展) | [学习经济 / 经济政策](https://rsshub.app/21caijing/channel/学习经济/经济政策) | [学习经济 / 广东在行动](https://rsshub.app/21caijing/channel/学习经济/广东在行动) | [学习经济 / 数说经济](https://rsshub.app/21caijing/channel/学习经济/数说经济) | [学习经济 / 学习视频](https://rsshub.app/21caijing/channel/学习经济/学习视频) | [学习经济 / 学习党史](https://rsshub.app/21caijing/channel/学习经济/学习党史) | #### [大湾区](https://m.21jingji.com/#/channel/GHM_GreaterBay) -| [动态](https://m.21jingji.com/#/channel/GHM_GreaterBay) | [湾区金融](https://m.21jingji.com/#/channel/wqjr) | [大湾区直播室](https://m.21jingji.com/#/channel/dwqzbs) | [高成长企业](https://m.21jingji.com/#/channel/gczqy) | [产业地理](https://m.21jingji.com/#/channel/cydl) | [数智湾区](https://m.21jingji.com/#/channel/szwq) | [湾区金融大咖会](https://m.21jingji.com/#/channel/wqjrdkh) | [“港”创科 25 人](https://m.21jingji.com/#/channel/gck) | [湾区论坛](https://m.21jingji.com/#/channel/wqlt) | -| --------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -| [大湾区/动态](https://rsshub.app/21caijing/channel/大湾区/动态) | [大湾区/湾区金融](https://rsshub.app/21caijing/channel/大湾区/湾区金融) | [大湾区/大湾区直播室](https://rsshub.app/21caijing/channel/大湾区/大湾区直播室) | [大湾区/高成长企业](https://rsshub.app/21caijing/channel/大湾区/高成长企业) | [大湾区/产业地理](https://rsshub.app/21caijing/channel/大湾区/产业地理) | [大湾区/数智湾区](https://rsshub.app/21caijing/channel/大湾区/数智湾区) | [大湾区/湾区金融大咖会](https://rsshub.app/21caijing/channel/大湾区/湾区金融大咖会) | [大湾区/“港”创科 25 人](https://rsshub.app/21caijing/channel/大湾区/“港”创科25人) | [大湾区/湾区论坛](https://rsshub.app/21caijing/channel/大湾区/湾区论坛) | +| [动态](https://m.21jingji.com/#/channel/GHM_GreaterBay) | [湾区金融](https://m.21jingji.com/#/channel/wqjr) | [大湾区直播室](https://m.21jingji.com/#/channel/dwqzbs) | [高成长企业](https://m.21jingji.com/#/channel/gczqy) | [产业地理](https://m.21jingji.com/#/channel/cydl) | [数智湾区](https://m.21jingji.com/#/channel/szwq) | [湾区金融大咖会](https://m.21jingji.com/#/channel/wqjrdkh) | [“港” 创科 25 人](https://m.21jingji.com/#/channel/gck) | [湾区论坛](https://m.21jingji.com/#/channel/wqlt) | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| [大湾区 / 动态](https://rsshub.app/21caijing/channel/大湾区/动态) | [大湾区 / 湾区金融](https://rsshub.app/21caijing/channel/大湾区/湾区金融) | [大湾区 / 大湾区直播室](https://rsshub.app/21caijing/channel/大湾区/大湾区直播室) | [大湾区 / 高成长企业](https://rsshub.app/21caijing/channel/大湾区/高成长企业) | [大湾区 / 产业地理](https://rsshub.app/21caijing/channel/大湾区/产业地理) | [大湾区 / 数智湾区](https://rsshub.app/21caijing/channel/大湾区/数智湾区) | [大湾区 / 湾区金融大咖会](https://rsshub.app/21caijing/channel/大湾区/湾区金融大咖会) | [大湾区 /“港” 创科 25 人](https://rsshub.app/21caijing/channel/大湾区/“港”创科25人) | [大湾区 / 湾区论坛](https://rsshub.app/21caijing/channel/大湾区/湾区论坛) | #### [证券](https://m.21jingji.com/#/channel/capital) -| [动态](https://m.21jingji.com/#/channel/capital) | [赢基金](https://m.21jingji.com/#/channel/funds) | [券业观察](https://m.21jingji.com/#/channel/securities) | [期市一线](https://m.21jingji.com/#/channel/qsyx) | [ETF](https://m.21jingji.com/#/channel/govern) | -| ----------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- | -| [证券/动态](https://rsshub.app/21caijing/channel/证券/动态) | [证券/赢基金](https://rsshub.app/21caijing/channel/证券/赢基金) | [证券/券业观察](https://rsshub.app/21caijing/channel/证券/券业观察) | [证券/期市一线](https://rsshub.app/21caijing/channel/证券/期市一线) | [证券/ETF](https://rsshub.app/21caijing/channel/证券/ETF) | +| [动态](https://m.21jingji.com/#/channel/capital) | [赢基金](https://m.21jingji.com/#/channel/funds) | [券业观察](https://m.21jingji.com/#/channel/securities) | [期市一线](https://m.21jingji.com/#/channel/qsyx) | [ETF](https://m.21jingji.com/#/channel/govern) | +| ------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------- | +| [证券 / 动态](https://rsshub.app/21caijing/channel/证券/动态) | [证券 / 赢基金](https://rsshub.app/21caijing/channel/证券/赢基金) | [证券 / 券业观察](https://rsshub.app/21caijing/channel/证券/券业观察) | [证券 / 期市一线](https://rsshub.app/21caijing/channel/证券/期市一线) | [证券 / ETF](https://rsshub.app/21caijing/channel/证券/ETF) | #### [汽车](https://m.21jingji.com/#/channel/auto) -| [热闻](https://m.21jingji.com/#/channel/autofocus) | [新汽车](https://m.21jingji.com/#/channel/newauto) | [车访间](https://m.21jingji.com/#/channel/autointerview) | [财说车](https://m.21jingji.com/#/channel/autofortune) | [汽车人](https://m.21jingji.com/#/channel/autopeople) | [汽车商业地理](https://m.21jingji.com/#/channel/autogeo) | [汽车金融](https://m.21jingji.com/#/channel/autofinance) | [行业报告](https://m.21jingji.com/#/channel/autoreport) | [聚焦](https://m.21jingji.com/#/channel/autospotlight) | -| ----------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- | -| [汽车/热闻](https://rsshub.app/21caijing/channel/汽车/热闻) | [汽车/新汽车](https://rsshub.app/21caijing/channel/汽车/新汽车) | [汽车/车访间](https://rsshub.app/21caijing/channel/汽车/车访间) | [汽车/财说车](https://rsshub.app/21caijing/channel/汽车/财说车) | [汽车/汽车人](https://rsshub.app/21caijing/channel/汽车/汽车人) | [汽车/汽车商业地理](https://rsshub.app/21caijing/channel/汽车/汽车商业地理) | [汽车/汽车金融](https://rsshub.app/21caijing/channel/汽车/汽车金融) | [汽车/行业报告](https://rsshub.app/21caijing/channel/汽车/行业报告) | [汽车/聚焦](https://rsshub.app/21caijing/channel/汽车/聚焦) | +| [热闻](https://m.21jingji.com/#/channel/autofocus) | [新汽车](https://m.21jingji.com/#/channel/newauto) | [车访间](https://m.21jingji.com/#/channel/autointerview) | [财说车](https://m.21jingji.com/#/channel/autofortune) | [汽车人](https://m.21jingji.com/#/channel/autopeople) | [汽车商业地理](https://m.21jingji.com/#/channel/autogeo) | [汽车金融](https://m.21jingji.com/#/channel/autofinance) | [行业报告](https://m.21jingji.com/#/channel/autoreport) | [聚焦](https://m.21jingji.com/#/channel/autospotlight) | +| ------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- | +| [汽车 / 热闻](https://rsshub.app/21caijing/channel/汽车/热闻) | [汽车 / 新汽车](https://rsshub.app/21caijing/channel/汽车/新汽车) | [汽车 / 车访间](https://rsshub.app/21caijing/channel/汽车/车访间) | [汽车 / 财说车](https://rsshub.app/21caijing/channel/汽车/财说车) | [汽车 / 汽车人](https://rsshub.app/21caijing/channel/汽车/汽车人) | [汽车 / 汽车商业地理](https://rsshub.app/21caijing/channel/汽车/汽车商业地理) | [汽车 / 汽车金融](https://rsshub.app/21caijing/channel/汽车/汽车金融) | [汽车 / 行业报告](https://rsshub.app/21caijing/channel/汽车/行业报告) | [汽车 / 聚焦](https://rsshub.app/21caijing/channel/汽车/聚焦) | #### [观点](https://m.21jingji.com/#/channel/opinion) #### [新健康](https://m.21jingji.com/#/channel/healthnews) -| [动态](https://m.21jingji.com/#/channel/healthdt) | [21 健讯 Daily](https://m.21jingji.com/#/channel/healthinfo) | [21CC](https://m.21jingji.com/#/channel/21cc) | [21 健谈](https://m.21jingji.com/#/channel/healthtalk) | [名医说](https://m.21jingji.com/#/channel/doctorssay) | [数字医疗](https://m.21jingji.com/#/channel/digitalhealth) | [21H 院长对话](https://m.21jingji.com/#/channel/talkwithdean) | [医健 IPO 解码](https://m.21jingji.com/#/channel/medicalIPO) | [研究报告](https://m.21jingji.com/#/channel/yjbg) | [21 科普](https://m.21jingji.com/#/channel/healthkp) | -| --------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------- | -| [新健康/动态](https://rsshub.app/21caijing/channel/新健康/动态) | [新健康/21 健讯 Daily](https://rsshub.app/21caijing/channel/新健康/21健讯Daily) | [新健康/21CC](https://rsshub.app/21caijing/channel/新健康/21CC) | [新健康/21 健谈](https://rsshub.app/21caijing/channel/新健康/21健谈) | [新健康/名医说](https://rsshub.app/21caijing/channel/新健康/名医说) | [新健康/数字医疗](https://rsshub.app/21caijing/channel/新健康/数字医疗) | [新健康/21H 院长对话](https://rsshub.app/21caijing/channel/新健康/21H院长对话) | [新健康/医健 IPO 解码](https://rsshub.app/21caijing/channel/新健康/医健IPO解码) | [新健康/研究报告](https://rsshub.app/21caijing/channel/新健康/研究报告) | [新健康/21 科普](https://rsshub.app/21caijing/channel/新健康/21科普) | +| [动态](https://m.21jingji.com/#/channel/healthdt) | [21 健讯 Daily](https://m.21jingji.com/#/channel/healthinfo) | [21CC](https://m.21jingji.com/#/channel/21cc) | [21 健谈](https://m.21jingji.com/#/channel/healthtalk) | [名医说](https://m.21jingji.com/#/channel/doctorssay) | [数字医疗](https://m.21jingji.com/#/channel/digitalhealth) | [21H 院长对话](https://m.21jingji.com/#/channel/talkwithdean) | [医健 IPO 解码](https://m.21jingji.com/#/channel/medicalIPO) | [研究报告](https://m.21jingji.com/#/channel/yjbg) | [21 科普](https://m.21jingji.com/#/channel/healthkp) | +| ----------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| [新健康 / 动态](https://rsshub.app/21caijing/channel/新健康/动态) | [新健康 / 21 健讯 Daily](https://rsshub.app/21caijing/channel/新健康/21健讯Daily) | [新健康 / 21CC](https://rsshub.app/21caijing/channel/新健康/21CC) | [新健康 / 21 健谈](https://rsshub.app/21caijing/channel/新健康/21健谈) | [新健康 / 名医说](https://rsshub.app/21caijing/channel/新健康/名医说) | [新健康 / 数字医疗](https://rsshub.app/21caijing/channel/新健康/数字医疗) | [新健康 / 21H 院长对话](https://rsshub.app/21caijing/channel/新健康/21H院长对话) | [新健康 / 医健 IPO 解码](https://rsshub.app/21caijing/channel/新健康/医健IPO解码) | [新健康 / 研究报告](https://rsshub.app/21caijing/channel/新健康/研究报告) | [新健康 / 21 科普](https://rsshub.app/21caijing/channel/新健康/21科普) | #### [ESG](https://m.21jingji.com/#/channel/esg) -| [ESG 发布厅](https://m.21jingji.com/#/channel/esg) | [绿色公司](https://m.21jingji.com/#/channel/lsgs) | [绿色金融](https://m.21jingji.com/#/channel/lsjr) | [净零碳城市](https://m.21jingji.com/#/channel/jltcs) | [碳市场](https://m.21jingji.com/#/channel/) | [生物多样性](https://m.21jingji.com/#/channel/swdyx) | [行业周报](https://m.21jingji.com/#/channel/hyzb) | [研究报告](https://m.21jingji.com/#/) | -| -------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | -| [ESG/ESG 发布厅](https://rsshub.app/21caijing/channel/ESG/ESG发布厅) | [ESG/绿色公司](https://rsshub.app/21caijing/channel/ESG/绿色公司) | [ESG/绿色金融](https://rsshub.app/21caijing/channel/ESG/绿色金融) | [ESG/净零碳城市](https://rsshub.app/21caijing/channel/ESG/净零碳城市) | [ESG/碳市场](https://rsshub.app/21caijing/channel/ESG/碳市场) | [ESG/生物多样性](https://rsshub.app/21caijing/channel/ESG/生物多样性) | [ESG/行业周报](https://rsshub.app/21caijing/channel/ESG/行业周报) | [ESG/研究报告](https://rsshub.app/21caijing/channel/ESG/研究报告) | +| [ESG 发布厅](https://m.21jingji.com/#/channel/esg) | [绿色公司](https://m.21jingji.com/#/channel/lsgs) | [绿色金融](https://m.21jingji.com/#/channel/lsjr) | [净零碳城市](https://m.21jingji.com/#/channel/jltcs) | [碳市场](https://m.21jingji.com/#/channel/) | [生物多样性](https://m.21jingji.com/#/channel/swdyx) | [行业周报](https://m.21jingji.com/#/channel/hyzb) | [研究报告](https://m.21jingji.com/#/) | +| -------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | +| [ESG/ESG 发布厅](https://rsshub.app/21caijing/channel/ESG/ESG发布厅) | [ESG / 绿色公司](https://rsshub.app/21caijing/channel/ESG/绿色公司) | [ESG / 绿色金融](https://rsshub.app/21caijing/channel/ESG/绿色金融) | [ESG / 净零碳城市](https://rsshub.app/21caijing/channel/ESG/净零碳城市) | [ESG / 碳市场](https://rsshub.app/21caijing/channel/ESG/碳市场) | [ESG / 生物多样性](https://rsshub.app/21caijing/channel/ESG/生物多样性) | [ESG / 行业周报](https://rsshub.app/21caijing/channel/ESG/行业周报) | [ESG / 研究报告](https://rsshub.app/21caijing/channel/ESG/研究报告) | #### [全球市场](https://m.21jingji.com/#/channel/global) -| [动态](https://m.21jingji.com/#/channel/global) | [全球财经连线](https://m.21jingji.com/#/channel/globaleconomics) | [直击华尔街](https://m.21jingji.com/#/channel/wallstreet) | [百家跨国公司看中国](https://m.21jingji.com/#/channel/mnc) | [全球央行观察](https://m.21jingji.com/#/channel/globalcentralbanks) | [全球能源观察](https://m.21jingji.com/#/channel/globalenergy) | [美股一线](https://m.21jingji.com/#/channel/USstock) | [港股一线](https://m.21jingji.com/#/channel/HKstock) | [全球金融观察](https://m.21jingji.com/#/channel/globalfinance) | [联合国现场](https://m.21jingji.com/#/channel/unitednations) | [全球央行月报](https://m.21jingji.com/#/channel/centralbankreport) | [全球商品观察](https://m.21jingji.com/#/channel/globalcommodities) | -| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -| [全球市场/动态](https://rsshub.app/21caijing/channel/全球市场/动态) | [全球市场/全球财经连线](https://rsshub.app/21caijing/channel/全球市场/全球财经连线) | [全球市场/直击华尔街](https://rsshub.app/21caijing/channel/全球市场/直击华尔街) | [全球市场/百家跨国公司看中国](https://rsshub.app/21caijing/channel/全球市场/百家跨国公司看中国) | [全球市场/全球央行观察](https://rsshub.app/21caijing/channel/全球市场/全球央行观察) | [全球市场/全球能源观察](https://rsshub.app/21caijing/channel/全球市场/全球能源观察) | [全球市场/美股一线](https://rsshub.app/21caijing/channel/全球市场/美股一线) | [全球市场/港股一线](https://rsshub.app/21caijing/channel/全球市场/港股一线) | [全球市场/全球金融观察](https://rsshub.app/21caijing/channel/全球市场/全球金融观察) | [全球市场/联合国现场](https://rsshub.app/21caijing/channel/全球市场/联合国现场) | [全球市场/全球央行月报](https://rsshub.app/21caijing/channel/全球市场/全球央行月报) | [全球市场/全球商品观察](https://rsshub.app/21caijing/channel/全球市场/全球商品观察) | +| [动态](https://m.21jingji.com/#/channel/global) | [全球财经连线](https://m.21jingji.com/#/channel/globaleconomics) | [直击华尔街](https://m.21jingji.com/#/channel/wallstreet) | [百家跨国公司看中国](https://m.21jingji.com/#/channel/mnc) | [全球央行观察](https://m.21jingji.com/#/channel/globalcentralbanks) | [全球能源观察](https://m.21jingji.com/#/channel/globalenergy) | [美股一线](https://m.21jingji.com/#/channel/USstock) | [港股一线](https://m.21jingji.com/#/channel/HKstock) | [全球金融观察](https://m.21jingji.com/#/channel/globalfinance) | [联合国现场](https://m.21jingji.com/#/channel/unitednations) | [全球央行月报](https://m.21jingji.com/#/channel/centralbankreport) | [全球商品观察](https://m.21jingji.com/#/channel/globalcommodities) | +| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| [全球市场 / 动态](https://rsshub.app/21caijing/channel/全球市场/动态) | [全球市场 / 全球财经连线](https://rsshub.app/21caijing/channel/全球市场/全球财经连线) | [全球市场 / 直击华尔街](https://rsshub.app/21caijing/channel/全球市场/直击华尔街) | [全球市场 / 百家跨国公司看中国](https://rsshub.app/21caijing/channel/全球市场/百家跨国公司看中国) | [全球市场 / 全球央行观察](https://rsshub.app/21caijing/channel/全球市场/全球央行观察) | [全球市场 / 全球能源观察](https://rsshub.app/21caijing/channel/全球市场/全球能源观察) | [全球市场 / 美股一线](https://rsshub.app/21caijing/channel/全球市场/美股一线) | [全球市场 / 港股一线](https://rsshub.app/21caijing/channel/全球市场/港股一线) | [全球市场 / 全球金融观察](https://rsshub.app/21caijing/channel/全球市场/全球金融观察) | [全球市场 / 联合国现场](https://rsshub.app/21caijing/channel/全球市场/联合国现场) | [全球市场 / 全球央行月报](https://rsshub.app/21caijing/channel/全球市场/全球央行月报) | [全球市场 / 全球商品观察](https://rsshub.app/21caijing/channel/全球市场/全球商品观察) | #### [一带一路](https://m.21jingji.com/#/channel/BandR) @@ -259,9 +259,9 @@ export const route: Route = { #### [理财通](https://m.21jingji.com/#/channel/financing) -| [动态](https://m.21jingji.com/#/channel/licaidongtai) | [数据库](https://m.21jingji.com/#/channel/sjk) | [研报](https://m.21jingji.com/#/channel/yanbao) | [投教](https://m.21jingji.com/#/channel/tj) | [政策](https://m.21jingji.com/#/channel/zhengce) | [固收+](https://m.21jingji.com/#/channel/gushou) | [纯固收](https://m.21jingji.com/#/channel/chungushou) | [现金](https://m.21jingji.com/#/channel/xianjin) | [混合](https://m.21jingji.com/#/channel/hunhe) | [权益](https://m.21jingji.com/#/channel/quanyi) | -| --------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | -| [理财通/动态](https://rsshub.app/21caijing/channel/理财通/动态) | [理财通/数据库](https://rsshub.app/21caijing/channel/理财通/数据库) | [理财通/研报](https://rsshub.app/21caijing/channel/理财通/研报) | [理财通/投教](https://rsshub.app/21caijing/channel/理财通/投教) | [理财通/政策](https://rsshub.app/21caijing/channel/理财通/政策) | [理财通/固收+](https://rsshub.app/21caijing/channel/理财通/固收+) | [理财通/纯固收](https://rsshub.app/21caijing/channel/理财通/纯固收) | [理财通/现金](https://rsshub.app/21caijing/channel/理财通/现金) | [理财通/混合](https://rsshub.app/21caijing/channel/理财通/混合) | [理财通/权益](https://rsshub.app/21caijing/channel/理财通/权益) | +| [动态](https://m.21jingji.com/#/channel/licaidongtai) | [数据库](https://m.21jingji.com/#/channel/sjk) | [研报](https://m.21jingji.com/#/channel/yanbao) | [投教](https://m.21jingji.com/#/channel/tj) | [政策](https://m.21jingji.com/#/channel/zhengce) | [固收 +](https://m.21jingji.com/#/channel/gushou) | [纯固收](https://m.21jingji.com/#/channel/chungushou) | [现金](https://m.21jingji.com/#/channel/xianjin) | [混合](https://m.21jingji.com/#/channel/hunhe) | [权益](https://m.21jingji.com/#/channel/quanyi) | +| ----------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | +| [理财通 / 动态](https://rsshub.app/21caijing/channel/理财通/动态) | [理财通 / 数据库](https://rsshub.app/21caijing/channel/理财通/数据库) | [理财通 / 研报](https://rsshub.app/21caijing/channel/理财通/研报) | [理财通 / 投教](https://rsshub.app/21caijing/channel/理财通/投教) | [理财通 / 政策](https://rsshub.app/21caijing/channel/理财通/政策) | [理财通 / 固收 +](https://rsshub.app/21caijing/channel/理财通/固收+) | [理财通 / 纯固收](https://rsshub.app/21caijing/channel/理财通/纯固收) | [理财通 / 现金](https://rsshub.app/21caijing/channel/理财通/现金) | [理财通 / 混合](https://rsshub.app/21caijing/channel/理财通/混合) | [理财通 / 权益](https://rsshub.app/21caijing/channel/理财通/权益) | #### [直播](https://m.21jingji.com/#/channel/live) @@ -273,9 +273,9 @@ export const route: Route = { #### [投教](https://m.21jingji.com/#/channel/tjzjy) -| [动态](https://m.21jingji.com/#/channel/tjzjy) | [投教知识](https://m.21jingji.com/#/channel/tjzs) | [公益活动](https://m.21jingji.com/#/channel/gyhd) | -| ----------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [投教/动态](https://rsshub.app/21caijing/channel/投教/动态) | [投教/投教知识](https://rsshub.app/21caijing/channel/投教/投教知识) | [投教/公益活动](https://rsshub.app/21caijing/channel/投教/公益活动) | +| [动态](https://m.21jingji.com/#/channel/tjzjy) | [投教知识](https://m.21jingji.com/#/channel/tjzs) | [公益活动](https://m.21jingji.com/#/channel/gyhd) | +| ------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [投教 / 动态](https://rsshub.app/21caijing/channel/投教/动态) | [投教 / 投教知识](https://rsshub.app/21caijing/channel/投教/投教知识) | [投教 / 公益活动](https://rsshub.app/21caijing/channel/投教/公益活动) | #### [海洋经济](https://m.21jingji.com/#/channel/oceaneconomy) @@ -283,28 +283,27 @@ export const route: Route = { #### [公司](https://m.21jingji.com/#/channel/company) -| [动态](https://m.21jingji.com/#/channel/company) | [电子通信](https://m.21jingji.com/#/channel/electrocommunication) | [互联网](https://m.21jingji.com/#/channel/internet) | [高端制造](https://m.21jingji.com/#/channel/highend) | [新能源](https://m.21jingji.com/#/channel/newenergy) | [消费](https://m.21jingji.com/#/channel/consumption) | [地产基建](https://m.21jingji.com/#/channel/infrastructure) | [IPO](https://m.21jingji.com/#/channel/IPO) | [文旅](https://m.21jingji.com/#/channel/culturetravel) | -| ----------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------- | -| [公司/动态](https://rsshub.app/21caijing/channel/公司/动态) | [公司/电子通信](https://rsshub.app/21caijing/channel/公司/电子通信) | [公司/互联网](https://rsshub.app/21caijing/channel/公司/互联网) | [公司/高端制造](https://rsshub.app/21caijing/channel/公司/高端制造) | [公司/新能源](https://rsshub.app/21caijing/channel/公司/新能源) | [公司/消费](https://rsshub.app/21caijing/channel/公司/消费) | [公司/地产基建](https://rsshub.app/21caijing/channel/公司/地产基建) | [公司/IPO](https://rsshub.app/21caijing/channel/公司/IPO) | [公司/文旅](https://rsshub.app/21caijing/channel/公司/文旅) | +| [动态](https://m.21jingji.com/#/channel/company) | [电子通信](https://m.21jingji.com/#/channel/electrocommunication) | [互联网](https://m.21jingji.com/#/channel/internet) | [高端制造](https://m.21jingji.com/#/channel/highend) | [新能源](https://m.21jingji.com/#/channel/newenergy) | [消费](https://m.21jingji.com/#/channel/consumption) | [地产基建](https://m.21jingji.com/#/channel/infrastructure) | [IPO](https://m.21jingji.com/#/channel/IPO) | [文旅](https://m.21jingji.com/#/channel/culturetravel) | +| ------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------- | +| [公司 / 动态](https://rsshub.app/21caijing/channel/公司/动态) | [公司 / 电子通信](https://rsshub.app/21caijing/channel/公司/电子通信) | [公司 / 互联网](https://rsshub.app/21caijing/channel/公司/互联网) | [公司 / 高端制造](https://rsshub.app/21caijing/channel/公司/高端制造) | [公司 / 新能源](https://rsshub.app/21caijing/channel/公司/新能源) | [公司 / 消费](https://rsshub.app/21caijing/channel/公司/消费) | [公司 / 地产基建](https://rsshub.app/21caijing/channel/公司/地产基建) | [公司 / IPO](https://rsshub.app/21caijing/channel/公司/IPO) | [公司 / 文旅](https://rsshub.app/21caijing/channel/公司/文旅) | #### [人文](https://m.21jingji.com/#/channel/life) #### [SFC Global](https://m.21jingji.com/#/channel/SFCGlobal) -| [News](https://m.21jingji.com/#/channel/SFCGlobal) | [SFC Markets and Finance](https://m.21jingji.com/#/channel/ SFCMarketsandFinance) | [SFC Market Talk](https://m.21jingji.com/#/channel/ SFCMarketTalk) | [CBN](https://m.21jingji.com/#/channel/CBN) | [Multinationals on China](https://m.21jingji.com/#/channel/MultinationalsonChina) | [Companies in the GBA](https://m.21jingji.com/#/channel/CompaniesintheGBA) | -| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| [SFC Global/News](https://rsshub.app/21caijing/channel/SFC Global/News) | [SFC Global/SFC Markets and Finance](https://rsshub.app/21caijing/channel/SFC Global/SFC Markets and Finance) | [SFC Global/SFC Market Talk](https://rsshub.app/21caijing/channel/SFC Global/SFC Market Talk) | [SFC Global/CBN](https://rsshub.app/21caijing/channel/SFC Global/CBN) | [SFC Global/Multinationals on China](https://rsshub.app/21caijing/channel/SFC Global/Multinationals on China) | [SFC Global/Companies in the GBA](https://rsshub.app/21caijing/channel/SFC Global/Companies in the GBA) | +| [News](https://m.21jingji.com/#/channel/SFCGlobal) | \\[SFC Markets and Finance]\\( SFCMarketsandFinance) | \\[SFC Market Talk]\\( SFCMarketTalk) | [CBN](https://m.21jingji.com/#/channel/CBN) | [Multinationals on China](https://m.21jingji.com/#/channel/MultinationalsonChina) | [Companies in the GBA](https://m.21jingji.com/#/channel/CompaniesintheGBA) | +| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| \\[SFC Global/News]\\( Global/News) | \\[SFC Global/SFC Markets and Finance]\\( Global/SFC Markets and Finance) | \\[SFC Global/SFC Market Talk]\\( Global/SFC Market Talk) | \\[SFC Global/CBN]\\( Global/CBN) | \\[SFC Global/Multinationals on China]\\( Global/Multinationals on China) | \\[SFC Global/Companies in the GBA]\\( Global/Companies in the GBA) | #### [南方财经报道](https://m.21jingji.com/#/channel/nfcjbd) #### [链上预制菜](https://m.21jingji.com/#/channel/precookedfood) -| [动态](https://m.21jingji.com/#/channel/precookedfood) | [活动](https://m.21jingji.com/#/channel/foodevent) | [报道](https://m.21jingji.com/#/channel/foodnews) | [智库/课题](https://m.21jingji.com/#/channel/foodtopic) | [数据/创新案例](https://m.21jingji.com/#/channel/foodcase) | [链接平台](https://m.21jingji.com/#/channel/foodlink) | -| ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| [链上预制菜/动态](https://rsshub.app/21caijing/channel/链上预制菜/动态) | [链上预制菜/活动](https://rsshub.app/21caijing/channel/链上预制菜/活动) | [链上预制菜/报道](https://rsshub.app/21caijing/channel/链上预制菜/报道) | [链上预制菜/智库/课题](https://rsshub.app/21caijing/channel/链上预制菜/智库/课题) | [链上预制菜/数据/创新案例](https://rsshub.app/21caijing/channel/链上预制菜/数据/创新案例) | [链上预制菜/链接平台](https://rsshub.app/21caijing/channel/链上预制菜/链接平台) | +| [动态](https://m.21jingji.com/#/channel/precookedfood) | [活动](https://m.21jingji.com/#/channel/foodevent) | [报道](https://m.21jingji.com/#/channel/foodnews) | [智库 / 课题](https://m.21jingji.com/#/channel/foodtopic) | [数据 / 创新案例](https://m.21jingji.com/#/channel/foodcase) | [链接平台](https://m.21jingji.com/#/channel/foodlink) | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| [链上预制菜 / 动态](https://rsshub.app/21caijing/channel/链上预制菜/动态) | [链上预制菜 / 活动](https://rsshub.app/21caijing/channel/链上预制菜/活动) | [链上预制菜 / 报道](https://rsshub.app/21caijing/channel/链上预制菜/报道) | [链上预制菜 / 智库 / 课题](https://rsshub.app/21caijing/channel/链上预制菜/智库/课题) | [链上预制菜 / 数据 / 创新案例](https://rsshub.app/21caijing/channel/链上预制菜/数据/创新案例) | [链上预制菜 / 链接平台](https://rsshub.app/21caijing/channel/链上预制菜/链接平台) | - -`, +`, categories: ['finance'], features: { requireConfig: false, diff --git a/lib/routes/36kr/index.ts b/lib/routes/36kr/index.ts index 1898c12effae..6c24a1d83726 100644 --- a/lib/routes/36kr/index.ts +++ b/lib/routes/36kr/index.ts @@ -28,9 +28,9 @@ export const route: Route = { }, name: '资讯, 快讯, 用户文章, 主题文章, 专题文章, 搜索文章, 搜索快讯', maintainers: ['nczitzk', 'fashioncj'], - description: `| 最新资讯频道 | 快讯 | 推荐资讯 | 生活 | 房产 | 职场 | 搜索文章 | 搜索快讯 | -| ------- | -------- | -------- | -------- | -------- | --------| -------- | -------- | -| news | newsflashes | recommend | life | estate | workplace | search/articles/关键词 | search/articles/关键词 |`, + description: `| 最新资讯频道 | 快讯 | 推荐资讯 | 生活 | 房产 | 职场 | 搜索文章 | 搜索快讯 | +| ------------ | ----------- | --------- | ---- | ------ | --------- | ----------------------- | ----------------------- | +| news | newsflashes | recommend | life | estate | workplace | search/articles/ 关键词 | search/articles/ 关键词 |`, handler, }; diff --git a/lib/routes/3dmgame/news-center.ts b/lib/routes/3dmgame/news-center.ts index 716fddc7704b..d51e0c760928 100644 --- a/lib/routes/3dmgame/news-center.ts +++ b/lib/routes/3dmgame/news-center.ts @@ -31,7 +31,7 @@ export const route: Route = { handler, description: `| 新闻推荐 | 游戏新闻 | 动漫影视 | 智能数码 | 时事焦点 | | -------- | -------- | -------- | -------- | ----------- | -| | game | acg | next | news_36_1 |`, +| | game | acg | next | news\\_36\\_1 |`, }; async function handler(ctx) { diff --git a/lib/routes/3kns/index.tsx b/lib/routes/3kns/index.tsx index b2f9eb7fafdc..7cab6030e233 100644 --- a/lib/routes/3kns/index.tsx +++ b/lib/routes/3kns/index.tsx @@ -26,31 +26,31 @@ export const route: Route = { maintainers: ['xzzpig'], handler, url: 'www.3kns.com/', - description: `游戏类型(category) + description: `游戏类型 (category) | 不限 | 角色扮演 | 动作冒险 | 策略游戏 | 模拟经营 | 即时战略 | 格斗类 | 射击游戏 | 休闲益智 | 体育运动 | 街机格斗 | 无双类 | 其他游戏 | 赛车竞速 | | ---- | -------- | -------- | -------- | -------- | -------- | ------ | -------- | -------- | -------- | -------- | ------ | -------- | -------- | | all | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | - 游戏语言(language) +游戏语言 (language) | 不限 | 中文 | 英语 | 日语 | 其他 | 中文汉化 | 德语 | | ---- | ---- | ---- | ---- | ---- | -------- | ---- | | all | 1 | 2 | 3 | 4 | 5 | 6 | - 游戏标签(tag) +游戏标签 (tag) | 不限 | 热门 | 多人聚会 | 僵尸 | 体感 | 大作 | 音乐 | 三国 | RPG | 格斗 | 闯关 | 横版 | 科幻 | 棋牌 | 运输 | 无双 | 卡通动漫 | 日系 | 养成 | 恐怖 | 运动 | 乙女 | 街机 | 飞行模拟 | 解谜 | 海战 | 战争 | 跑酷 | 即时策略 | 射击 | 经营 | 益智 | 沙盒 | 模拟 | 冒险 | 竞速 | 休闲 | 动作 | 生存 | 独立 | 拼图 | 魔改 xci | 卡牌 | 塔防 | | ---- | ---- | -------- | ---- | ---- | ---- | ---- | ---- | --- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | -------- | ---- | ---- | ---- | ---- | ---- | ---- | -------- | ---- | ---- | ---- | ---- | -------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | -------- | ---- | ---- | | all | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | - 发售时间(pubDate) +发售时间 (pubDate) | 不限 | 2017 年 | 2018 年 | 2019 年 | 2020 年 | 2021 年 | 2022 年 | 2023 年 | 2024 年 | | ---- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | | all | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | - 游戏集合(collection) +游戏集合 (collection) | 不限 | 舞力全开 | 马里奥 | 生化危机 | 炼金工房 | 最终幻想 | 塞尔达 | 宝可梦 | 勇者斗恶龙 | 模拟器 | 秋之回忆 | 第一方 | 体感健身 | 开放世界 | 儿童乐园 | | ---- | -------- | ------ | -------- | -------- | -------- | ------ | ------ | ---------- | ------ | -------- | ------ | -------- | -------- | -------- | @@ -82,7 +82,7 @@ async function handler(ctx: Context): Promise { const response = await got(currentUrl); const $ = load(response.data as any); - const selector = `form .newItem`; + const selector = 'form .newItem'; const items: DataItem[] = $(selector) .toArray() .map((item) => { diff --git a/lib/routes/423down/index.ts b/lib/routes/423down/index.ts index 810d2740892e..c5fb36e757ac 100644 --- a/lib/routes/423down/index.ts +++ b/lib/routes/423down/index.ts @@ -122,7 +122,7 @@ export const route: Route = { example: '/423down', parameters: { category: '分类,默认为首页,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [Android - 423Down](https://www.423down.com/apk),网址为 \`https://www.423down.com/apk\`。截取 \`https://www.423down.com/\` 到末尾的部分 \`apk\` 作为参数填入,此时路由为 [\`/423down/apk\`](https://rsshub.app/423down/apk)。 +若订阅 [Android - 423Down](https://www.423down.com/apk),网址为 \`https://www.423down.com/apk\`。截取 \`https://www.423down.com/\` 到末尾的部分 \`apk\` 作为参数填入,此时路由为 [\`/423down/apk\`](https://rsshub.app/423down/apk)。 ::: #### [安卓软件](https://www.423down.com/apk) @@ -149,8 +149,7 @@ export const route: Route = { | [Windows 11](https://www.423down.com/win11) | [Windows 10](https://www.423down.com/win10) | [Windows 7](https://www.423down.com/win7) | [Windows XP](https://www.423down.com/win7/winxp) | [WinPE](https://www.423down.com/pe-system) | | ------------------------------------------- | ------------------------------------------- | ----------------------------------------- | --------------------------------------------------- | ------------------------------------------------- | -| [win11](https://rsshub.app/423down/win11) | [win10](https://rsshub.app/423down/win10) | [win7](https://rsshub.app/423down/win7) | [win7/winxp](https://rsshub.app/423down/win7/winxp) | [pe-system](https://rsshub.app/423down/pe-system) | - `, +| [win11](https://rsshub.app/423down/win11) | [win10](https://rsshub.app/423down/win10) | [win7](https://rsshub.app/423down/win7) | [win7/winxp](https://rsshub.app/423down/win7/winxp) | [pe-system](https://rsshub.app/423down/pe-system) |`, categories: ['program-update'], features: { diff --git a/lib/routes/4chan/catalog.tsx b/lib/routes/4chan/catalog.tsx index ba04087e76fe..06faea5c9f1e 100644 --- a/lib/routes/4chan/catalog.tsx +++ b/lib/routes/4chan/catalog.tsx @@ -42,10 +42,11 @@ export const route: Route = { }, ], description: `Specify options (in the format of query string) in parameter \`routeParams\` to control some extra features for Tweets -| Key | Description | Accepts | Defaults to | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | ----------------------------------------- | -| \`showReplyCount\` | Show number of replies of each thread in catalog | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` | -| \`showLastReplies\` | Show last 5 replies of each thread | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` | -| \`revealSpoilers\` | Don't wrap images tagged as spoilers | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` |`, + +| Key | Description | Accepts | Defaults to | +| ----------------- | ------------------------------------------------ | ---------------------- | ----------- | +| \`showReplyCount\` | Show number of replies of each thread in catalog | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` | +| \`showLastReplies\` | Show last 5 replies of each thread | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` | +| \`revealSpoilers\` | Don't wrap images tagged as spoilers | \`0\`/\`1\`/\`true\`/\`false\` | \`false\` |`, handler, }; diff --git a/lib/routes/4gamers/tag.ts b/lib/routes/4gamers/tag.ts index c9dba110e6ef..c10e916cea2d 100644 --- a/lib/routes/4gamers/tag.ts +++ b/lib/routes/4gamers/tag.ts @@ -32,7 +32,7 @@ async function handler(ctx) { const tag = ctx.req.param('tag'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; - const { data: response } = await got(`https://www.4gamers.com.tw/site/api/news/by-tag`, { + const { data: response } = await got('https://www.4gamers.com.tw/site/api/news/by-tag', { searchParams: { tag, pageSize: limit, diff --git a/lib/routes/4ksj/forum.tsx b/lib/routes/4ksj/forum.tsx index b4cf2228edb8..4762c3e87bb8 100644 --- a/lib/routes/4ksj/forum.tsx +++ b/lib/routes/4ksj/forum.tsx @@ -60,9 +60,9 @@ export const route: Route = { example: '/4ksj/4k-uhd-1', parameters: { id: '分类 id,默认为最新4K电影' }, description: `::: tip - 若订阅 [最新 4K 电影](https://www.4ksj.com/4k-uhd-1.html),网址为 \`https://www.4ksj.com/4k-uhd-1.html\`。截取 \`https://www.4ksj.com/\` 到末尾 \`.html\` 的部分 \`4k-uhd-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-1\`](https://rsshub.app/4ksj/4k-uhd-1)。 +若订阅 [最新 4K 电影](https://www.4ksj.com/4k-uhd-1.html),网址为 \`https://www.4ksj.com/4k-uhd-1.html\`。截取 \`https://www.4ksj.com/\` 到末尾 \`.html\` 的部分 \`4k-uhd-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-1\`](https://rsshub.app/4ksj/4k-uhd-1)。 - 若订阅子分类 [Dolby Vision 动作 4K 电影](https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html),网址为 \`https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html\`。截取 \`https://www.4ksj.com/forum-\` 到末尾 \`.html\` 的部分 \`4kdianying-s7-dianyingbiaozhun-3-dytypes-9-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-s7-display-3-dytypes-1-1\`](https://rsshub.app/4ksj/4k-uhd-s7-display-3-dytypes-1-1)。 +若订阅子分类 [Dolby Vision 动作 4K 电影](https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html),网址为 \`https://www.4ksj.com/4k-uhd-s7-display-3-dytypes-1-1.html\`。截取 \`https://www.4ksj.com/forum-\` 到末尾 \`.html\` 的部分 \`4kdianying-s7-dianyingbiaozhun-3-dytypes-9-1\` 作为参数,此时路由为 [\`/4ksj/4k-uhd-s7-display-3-dytypes-1-1\`](https://rsshub.app/4ksj/4k-uhd-s7-display-3-dytypes-1-1)。 :::`, categories: ['multimedia'], }; diff --git a/lib/routes/4kup/popular.ts b/lib/routes/4kup/popular.ts index b55d89e13d32..4f1e710180fd 100644 --- a/lib/routes/4kup/popular.ts +++ b/lib/routes/4kup/popular.ts @@ -49,7 +49,7 @@ function getPeriodConfig(period) { } return { url: `${SUB_URL}most-view/`, - range: `all`, + range: 'all', title: `${SUB_NAME_PREFIX} - Most views`, }; } diff --git a/lib/routes/50forum/zhuanjia.ts b/lib/routes/50forum/zhuanjia.ts index 580a506836ef..c5687eb29bb4 100644 --- a/lib/routes/50forum/zhuanjia.ts +++ b/lib/routes/50forum/zhuanjia.ts @@ -64,7 +64,7 @@ async function handler() { ) ); return { - title: `中国经济50人论坛专家文章`, + title: '中国经济50人论坛专家文章', link: 'https://www.50forum.org.cn/portal/list/index.html?id=6', description: '中国经济50人论坛专家文章', item: out, diff --git a/lib/routes/52hrtt/index.ts b/lib/routes/52hrtt/index.ts index 69860c718a12..76cc9a6b3a4f 100644 --- a/lib/routes/52hrtt/index.ts +++ b/lib/routes/52hrtt/index.ts @@ -24,7 +24,7 @@ export const route: Route = { handler, description: `地区和分类皆可在浏览器地址栏中找到,下面是一个例子。 - 访问华人头条全球站的国际分类,会跳转到 \`https://www.52hrtt.com/global/n/w?infoTypeId=A1459145516533\`。其中 \`global\` 即为 **全球** 对应的地区代码,\`A1459145516533\` 即为 **国际** 对应的分类代码。`, +访问华人头条全球站的国际分类,会跳转到 \`https://www.52hrtt.com/global/n/w?infoTypeId=A1459145516533\`。其中 \`global\` 即为 **全球** 对应的地区代码,\`A1459145516533\` 即为 **国际** 对应的分类代码。`, }; async function handler(ctx) { diff --git a/lib/routes/52hrtt/symposium.ts b/lib/routes/52hrtt/symposium.ts index b01ea7dbada1..b8c568479ce8 100644 --- a/lib/routes/52hrtt/symposium.ts +++ b/lib/routes/52hrtt/symposium.ts @@ -30,10 +30,10 @@ export const route: Route = { handler, description: `专题 id 和 子分类 id 皆可在浏览器地址栏中找到,下面是一个例子。 - 访问 “邱毅看平潭” 专题,会跳转到 \`https://www.52hrtt.com/global/n/w/symposium/F1626082387819\`。其中 \`F1626082387819\` 即为 **专题 id** 对应的地区代码。 +访问 “邱毅看平潭” 专题,会跳转到 \`https://www.52hrtt.com/global/n/w/symposium/F1626082387819\`。其中 \`F1626082387819\` 即为 **专题 id** 对应的地区代码。 ::: tip - 更多的专题可以点击 [这里](https://www.52hrtt.com/global/n/w/symposium) +更多的专题可以点击 [这里](https://www.52hrtt.com/global/n/w/symposium) :::`, }; diff --git a/lib/routes/56kog/class.ts b/lib/routes/56kog/class.ts index cf003c08c6c2..5c4394639680 100644 --- a/lib/routes/56kog/class.ts +++ b/lib/routes/56kog/class.ts @@ -21,11 +21,11 @@ export const route: Route = { handler, description: `| [玄幻魔法](https://www.56kog.com/class/1_1.html) | [武侠修真](https://www.56kog.com/class/2_1.html) | [历史军事](https://www.56kog.com/class/4_1.html) | [侦探推理](https://www.56kog.com/class/5_1.html) | [网游动漫](https://www.56kog.com/class/6_1.html) | | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | -| 1_1 | 2_1 | 4_1 | 5_1 | 6_1 | +| 1\\_1 | 2\\_1 | 4\\_1 | 5\\_1 | 6\\_1 | | [恐怖灵异](https://www.56kog.com/class/8_1.html) | [都市言情](https://www.56kog.com/class/3_1.html) | [科幻](https://www.56kog.com/class/7_1.html) | [女生小说](https://www.56kog.com/class/9_1.html) | [其他](https://www.56kog.com/class/10_1.html) | | ------------------------------------------------ | ------------------------------------------------ | -------------------------------------------- | ------------------------------------------------ | --------------------------------------------- | -| 8_1 | 3_1 | 7_1 | 9_1 | 10_1 |`, +| 8\\_1 | 3\\_1 | 7\\_1 | 9\\_1 | 10\\_1 |`, }; async function handler(ctx) { diff --git a/lib/routes/591/list.tsx b/lib/routes/591/list.tsx index 746d37ef28e3..76bbe7c99af1 100644 --- a/lib/routes/591/list.tsx +++ b/lib/routes/591/list.tsx @@ -173,7 +173,7 @@ export const route: Route = { maintainers: ['Yukaii'], handler, description: `::: tip - Copy the URL of the 591 filter housing page and remove the front part \`https://rent.591.com.tw/?\`, you will get the query parameters. +Copy the URL of the 591 filter housing page and remove the front part \`https://rent.591.com.tw/?\`, you will get the query parameters. :::`, }; diff --git a/lib/routes/5music/index.ts b/lib/routes/5music/index.ts index 8b250948986d..bcfd1c3af748 100644 --- a/lib/routes/5music/index.ts +++ b/lib/routes/5music/index.ts @@ -27,9 +27,10 @@ export const route: Route = { maintainers: ['gideonsenku'], handler, description: `Categories: + | 華語 | 西洋 | 東洋 | 韓語 | 古典 | | ---- | ---- | ---- | ---- | ---- | -| A | B | F | M | D |`, +| A | B | F | M | D |`, url: 'www.5music.com.tw/New_releases.asp', }; diff --git a/lib/routes/5music/namespace.ts b/lib/routes/5music/namespace.ts index a553165dcc82..93b1fd0c8808 100644 --- a/lib/routes/5music/namespace.ts +++ b/lib/routes/5music/namespace.ts @@ -5,5 +5,5 @@ export const namespace: Namespace = { url: '5music.com.tw', lang: 'zh-TW', categories: ['shopping'], - description: '五大唱片是台湾五大唱片股份有限公司的简称,成立于1990年,是台湾最大的唱片公司之一。', + description: '五大唱片是台湾五大唱片股份有限公司的简称,成立于 1990 年,是台湾最大的唱片公司之一。', }; diff --git a/lib/routes/6park/index.ts b/lib/routes/6park/index.ts index 42009e9e9ac5..4e2015f42835 100644 --- a/lib/routes/6park/index.ts +++ b/lib/routes/6park/index.ts @@ -20,7 +20,7 @@ export const route: Route = { }, ], description: `| 婚姻家庭 | 魅力时尚 | 女性频道 | 生活百态 | 美食厨房 | 非常影音 | 车迷沙龙 | 游戏天地 | 卡通漫画 | 体坛纵横 | 运动健身 | 电脑前线 | 数码家电 | 旅游风向 | 摄影部落 | 奇珍异宝 | 笑口常开 | 娱乐八卦 | 吃喝玩乐 | 文化长廊 | 军事纵横 | 百家论坛 | 科技频道 | 爱子情怀 | 健康人生 | 博论天下 | 史海钩沉 | 网际谈兵 | 经济观察 | 谈股论金 | 杂论闲侃 | 唯美乐园 | 学习园地 | 命理玄机 | 宠物情缘 | 网络歌坛 | 音乐殿堂 | 情感世界 | -|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------|----------| +| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | | life9 | life1 | chan10 | life2 | life6 | fr | enter7 | enter3 | enter6 | enter5 | sport | know1 | chan6 | life7 | chan8 | page | enter1 | enter8 | netstar | life10 | nz | other | chan2 | chan5 | life5 | bolun | chan1 | military | finance | chan4 | pk | gz1 | gz2 | gz3 | life8 | chan7 | enter4 | life3 |`, }; diff --git a/lib/routes/6v123/index.ts b/lib/routes/6v123/index.ts index ef0a69c95dff..be6dfa62c9aa 100644 --- a/lib/routes/6v123/index.ts +++ b/lib/routes/6v123/index.ts @@ -309,8 +309,7 @@ export const route: Route = { | [国产电影](https://www.hao6v.me/s/guochandianying/) | [s/guochandianying](https://rsshub.app/6v123/s/guochandianying) | | [欧洲电影](https://www.hao6v.me/s/xijudianying/) | [s/xijudianying](https://rsshub.app/6v123/s/xijudianying) | - -`, +`, categories: ['multimedia'], features: { requireConfig: false, diff --git a/lib/routes/78dm/index.ts b/lib/routes/78dm/index.ts index 30c03dd88385..7a7be12bbae0 100644 --- a/lib/routes/78dm/index.ts +++ b/lib/routes/78dm/index.ts @@ -141,9 +141,9 @@ export const route: Route = { example: '/78dm/news', parameters: { category: '分类,默认为 `news`,即新品速递,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [新品速递](https://www.78dm.net/news),网址为 \`https://www.78dm.net/news\`。截取 \`https://www.78dm.net/\` 到末尾的部分 \`news\` 作为参数填入,此时路由为 [\`/78dm/news\`](https://rsshub.app/78dm/news)。 +若订阅 [新品速递](https://www.78dm.net/news),网址为 \`https://www.78dm.net/news\`。截取 \`https://www.78dm.net/\` 到末尾的部分 \`news\` 作为参数填入,此时路由为 [\`/78dm/news\`](https://rsshub.app/78dm/news)。 - 若订阅 [精彩评测 - 变形金刚](https://www.78dm.net/eval_list/109/0/0/1.html),网址为 \`https://www.78dm.net/eval_list/109/0/0/1.html\`。截取 \`https://www.78dm.net/\` 到末尾 \`.html\` 的部分 \`eval_list/109/0/0/1\` 作为参数填入,此时路由为 [\`/78dm/eval_list/109/0/0/1\`](https://rsshub.app/78dm/eval_list/109/0/0/1)。 +若订阅 [精彩评测 - 变形金刚](https://www.78dm.net/eval_list/109/0/0/1.html),网址为 \`https://www.78dm.net/eval_list/109/0/0/1.html\`。截取 \`https://www.78dm.net/\` 到末尾 \`.html\` 的部分 \`eval_list/109/0/0/1\` 作为参数填入,此时路由为 [\`/78dm/eval_list/109/0/0/1\`](https://rsshub.app/78dm/eval_list/109/0/0/1)。 :::
@@ -174,44 +174,44 @@ export const route: Route = { #### [精彩评测](https://www.78dm.net/eval_list) -| 分类 | ID | -| --------------------------------------------------------- | ------------------------------------------------------------------ | -| [全部](https://www.78dm.net/eval_list/0/0/0/1.html) | [eval_list/0/0/0/1](https://rsshub.app/78dm/eval_list/0/0/0/1) | -| [变形金刚](https://www.78dm.net/eval_list/109/0/0/1.html) | [eval_list/109/0/0/1](https://rsshub.app/78dm/eval_list/109/0/0/1) | -| [高达](https://www.78dm.net/eval_list/110/0/0/1.html) | [eval_list/110/0/0/1](https://rsshub.app/78dm/eval_list/110/0/0/1) | -| [圣斗士](https://www.78dm.net/eval_list/111/0/0/1.html) | [eval_list/111/0/0/1](https://rsshub.app/78dm/eval_list/111/0/0/1) | -| [海贼王](https://www.78dm.net/eval_list/112/0/0/1.html) | [eval_list/112/0/0/1](https://rsshub.app/78dm/eval_list/112/0/0/1) | -| [PVC 手办](https://www.78dm.net/eval_list/115/0/0/1.html) | [eval_list/115/0/0/1](https://rsshub.app/78dm/eval_list/115/0/0/1) | -| [拼装模型](https://www.78dm.net/eval_list/113/0/0/1.html) | [eval_list/113/0/0/1](https://rsshub.app/78dm/eval_list/113/0/0/1) | -| [机甲成品](https://www.78dm.net/eval_list/114/0/0/1.html) | [eval_list/114/0/0/1](https://rsshub.app/78dm/eval_list/114/0/0/1) | -| [特摄](https://www.78dm.net/eval_list/116/0/0/1.html) | [eval_list/116/0/0/1](https://rsshub.app/78dm/eval_list/116/0/0/1) | -| [美系](https://www.78dm.net/eval_list/117/0/0/1.html) | [eval_list/117/0/0/1](https://rsshub.app/78dm/eval_list/117/0/0/1) | -| [GK](https://www.78dm.net/eval_list/118/0/0/1.html) | [eval_list/118/0/0/1](https://rsshub.app/78dm/eval_list/118/0/0/1) | -| [综合](https://www.78dm.net/eval_list/120/0/0/1.html) | [eval_list/120/0/0/1](https://rsshub.app/78dm/eval_list/120/0/0/1) | +| 分类 | ID | +| --------------------------------------------------------- | ------------------------------------------------------------------- | +| [全部](https://www.78dm.net/eval_list/0/0/0/1.html) | [eval\\_list/0/0/0/1](https://rsshub.app/78dm/eval_list/0/0/0/1) | +| [变形金刚](https://www.78dm.net/eval_list/109/0/0/1.html) | [eval\\_list/109/0/0/1](https://rsshub.app/78dm/eval_list/109/0/0/1) | +| [高达](https://www.78dm.net/eval_list/110/0/0/1.html) | [eval\\_list/110/0/0/1](https://rsshub.app/78dm/eval_list/110/0/0/1) | +| [圣斗士](https://www.78dm.net/eval_list/111/0/0/1.html) | [eval\\_list/111/0/0/1](https://rsshub.app/78dm/eval_list/111/0/0/1) | +| [海贼王](https://www.78dm.net/eval_list/112/0/0/1.html) | [eval\\_list/112/0/0/1](https://rsshub.app/78dm/eval_list/112/0/0/1) | +| [PVC 手办](https://www.78dm.net/eval_list/115/0/0/1.html) | [eval\\_list/115/0/0/1](https://rsshub.app/78dm/eval_list/115/0/0/1) | +| [拼装模型](https://www.78dm.net/eval_list/113/0/0/1.html) | [eval\\_list/113/0/0/1](https://rsshub.app/78dm/eval_list/113/0/0/1) | +| [机甲成品](https://www.78dm.net/eval_list/114/0/0/1.html) | [eval\\_list/114/0/0/1](https://rsshub.app/78dm/eval_list/114/0/0/1) | +| [特摄](https://www.78dm.net/eval_list/116/0/0/1.html) | [eval\\_list/116/0/0/1](https://rsshub.app/78dm/eval_list/116/0/0/1) | +| [美系](https://www.78dm.net/eval_list/117/0/0/1.html) | [eval\\_list/117/0/0/1](https://rsshub.app/78dm/eval_list/117/0/0/1) | +| [GK](https://www.78dm.net/eval_list/118/0/0/1.html) | [eval\\_list/118/0/0/1](https://rsshub.app/78dm/eval_list/118/0/0/1) | +| [综合](https://www.78dm.net/eval_list/120/0/0/1.html) | [eval\\_list/120/0/0/1](https://rsshub.app/78dm/eval_list/120/0/0/1) | #### [好贴推荐](https://www.78dm.net/ht_list) -| 分类 | ID | -| ------------------------------------------------------- | -------------------------------------------------------------- | -| [全部](https://www.78dm.net/ht_list/0/0/0/1.html) | [ht_list/0/0/0/1](https://rsshub.app/78dm/ht_list/0/0/0/1) | -| [变形金刚](https://www.78dm.net/ht_list/95/0/0/1.html) | [ht_list/95/0/0/1](https://rsshub.app/78dm/ht_list/95/0/0/1) | -| [高达](https://www.78dm.net/ht_list/96/0/0/1.html) | [ht_list/96/0/0/1](https://rsshub.app/78dm/ht_list/96/0/0/1) | -| [圣斗士](https://www.78dm.net/ht_list/98/0/0/1.html) | [ht_list/98/0/0/1](https://rsshub.app/78dm/ht_list/98/0/0/1) | -| [海贼王](https://www.78dm.net/ht_list/99/0/0/1.html) | [ht_list/99/0/0/1](https://rsshub.app/78dm/ht_list/99/0/0/1) | -| [PVC 手办](https://www.78dm.net/ht_list/100/0/0/1.html) | [ht_list/100/0/0/1](https://rsshub.app/78dm/ht_list/100/0/0/1) | -| [拼装模型](https://www.78dm.net/ht_list/101/0/0/1.html) | [ht_list/101/0/0/1](https://rsshub.app/78dm/ht_list/101/0/0/1) | -| [机甲成品](https://www.78dm.net/ht_list/102/0/0/1.html) | [ht_list/102/0/0/1](https://rsshub.app/78dm/ht_list/102/0/0/1) | -| [特摄](https://www.78dm.net/ht_list/103/0/0/1.html) | [ht_list/103/0/0/1](https://rsshub.app/78dm/ht_list/103/0/0/1) | -| [美系](https://www.78dm.net/ht_list/104/0/0/1.html) | [ht_list/104/0/0/1](https://rsshub.app/78dm/ht_list/104/0/0/1) | -| [GK](https://www.78dm.net/ht_list/105/0/0/1.html) | [ht_list/105/0/0/1](https://rsshub.app/78dm/ht_list/105/0/0/1) | -| [综合](https://www.78dm.net/ht_list/107/0/0/1.html) | [ht_list/107/0/0/1](https://rsshub.app/78dm/ht_list/107/0/0/1) | -| [装甲战车](https://www.78dm.net/ht_list/131/0/0/1.html) | [ht_list/131/0/0/1](https://rsshub.app/78dm/ht_list/131/0/0/1) | -| [舰船模型](https://www.78dm.net/ht_list/132/0/0/1.html) | [ht_list/132/0/0/1](https://rsshub.app/78dm/ht_list/132/0/0/1) | -| [飞机模型](https://www.78dm.net/ht_list/133/0/0/1.html) | [ht_list/133/0/0/1](https://rsshub.app/78dm/ht_list/133/0/0/1) | -| [民用模型](https://www.78dm.net/ht_list/134/0/0/1.html) | [ht_list/134/0/0/1](https://rsshub.app/78dm/ht_list/134/0/0/1) | -| [兵人模型](https://www.78dm.net/ht_list/135/0/0/1.html) | [ht_list/135/0/0/1](https://rsshub.app/78dm/ht_list/135/0/0/1) | -
- `, +| 分类 | ID | +| ------------------------------------------------------- | --------------------------------------------------------------- | +| [全部](https://www.78dm.net/ht_list/0/0/0/1.html) | [ht\\_list/0/0/0/1](https://rsshub.app/78dm/ht_list/0/0/0/1) | +| [变形金刚](https://www.78dm.net/ht_list/95/0/0/1.html) | [ht\\_list/95/0/0/1](https://rsshub.app/78dm/ht_list/95/0/0/1) | +| [高达](https://www.78dm.net/ht_list/96/0/0/1.html) | [ht\\_list/96/0/0/1](https://rsshub.app/78dm/ht_list/96/0/0/1) | +| [圣斗士](https://www.78dm.net/ht_list/98/0/0/1.html) | [ht\\_list/98/0/0/1](https://rsshub.app/78dm/ht_list/98/0/0/1) | +| [海贼王](https://www.78dm.net/ht_list/99/0/0/1.html) | [ht\\_list/99/0/0/1](https://rsshub.app/78dm/ht_list/99/0/0/1) | +| [PVC 手办](https://www.78dm.net/ht_list/100/0/0/1.html) | [ht\\_list/100/0/0/1](https://rsshub.app/78dm/ht_list/100/0/0/1) | +| [拼装模型](https://www.78dm.net/ht_list/101/0/0/1.html) | [ht\\_list/101/0/0/1](https://rsshub.app/78dm/ht_list/101/0/0/1) | +| [机甲成品](https://www.78dm.net/ht_list/102/0/0/1.html) | [ht\\_list/102/0/0/1](https://rsshub.app/78dm/ht_list/102/0/0/1) | +| [特摄](https://www.78dm.net/ht_list/103/0/0/1.html) | [ht\\_list/103/0/0/1](https://rsshub.app/78dm/ht_list/103/0/0/1) | +| [美系](https://www.78dm.net/ht_list/104/0/0/1.html) | [ht\\_list/104/0/0/1](https://rsshub.app/78dm/ht_list/104/0/0/1) | +| [GK](https://www.78dm.net/ht_list/105/0/0/1.html) | [ht\\_list/105/0/0/1](https://rsshub.app/78dm/ht_list/105/0/0/1) | +| [综合](https://www.78dm.net/ht_list/107/0/0/1.html) | [ht\\_list/107/0/0/1](https://rsshub.app/78dm/ht_list/107/0/0/1) | +| [装甲战车](https://www.78dm.net/ht_list/131/0/0/1.html) | [ht\\_list/131/0/0/1](https://rsshub.app/78dm/ht_list/131/0/0/1) | +| [舰船模型](https://www.78dm.net/ht_list/132/0/0/1.html) | [ht\\_list/132/0/0/1](https://rsshub.app/78dm/ht_list/132/0/0/1) | +| [飞机模型](https://www.78dm.net/ht_list/133/0/0/1.html) | [ht\\_list/133/0/0/1](https://rsshub.app/78dm/ht_list/133/0/0/1) | +| [民用模型](https://www.78dm.net/ht_list/134/0/0/1.html) | [ht\\_list/134/0/0/1](https://rsshub.app/78dm/ht_list/134/0/0/1) | +| [兵人模型](https://www.78dm.net/ht_list/135/0/0/1.html) | [ht\\_list/135/0/0/1](https://rsshub.app/78dm/ht_list/135/0/0/1) | + +`, categories: ['new-media'], features: { diff --git a/lib/routes/7mmtv/index.tsx b/lib/routes/7mmtv/index.tsx index 0024f279c9ac..4104cf70921c 100644 --- a/lib/routes/7mmtv/index.tsx +++ b/lib/routes/7mmtv/index.tsx @@ -30,17 +30,17 @@ export const route: Route = { | ------- | ------ | ------ | ---- | | en | ja | ko | zh | - **Category** +**Category** | Chinese subtitles AV | Censored | Amateur | Uncensored | Asian self-timer | H comics | | -------------------- | -------------- | ---------------- | ---------------- | ---------------- | ------------ | -| chinese_list | censored_list | amateurjav_list | uncensored_list | amateur_list | hcomic_list | +| chinese\\_list | censored\\_list | amateurjav\\_list | uncensored\\_list | amateur\\_list | hcomic\\_list | | Chinese subtitles AV random | Censored random | Amateur random | Uncensored random | Asian self-timer random | H comics random | | --------------------------- | ---------------- | ------------------ | ------------------ | ----------------------- | --------------- | -| chinese_random | censored_random | amateurjav_random | uncensored_random | amateur_random | hcomic_random | +| chinese\\_random | censored\\_random | amateurjav\\_random | uncensored\\_random | amateur\\_random | hcomic\\_random | - **Server** +**Server** | All Server | fembed(Full DL) | streamsb(Full DL) | doodstream | streamtape(Full DL) | avgle | embedgram | videovard(Full DL) | | ---------- | --------------- | ----------------- | ---------- | ------------------- | ----- | --------- | ------------------ | diff --git a/lib/routes/81/81rc/index.ts b/lib/routes/81/81rc/index.ts index ca8b8767292a..e3687a4e0708 100644 --- a/lib/routes/81/81rc/index.ts +++ b/lib/routes/81/81rc/index.ts @@ -81,9 +81,8 @@ export const route: Route = { example: '/81/81rc/sy/gzdt_210283', parameters: { category: '分类,默认为 `sy/gzdt_210283`,即工作动态,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [工作动态](https://81rc.81.cn/sy/gzdt_210283),网址为 \`https://81rc.81.cn/sy/gzdt_210283\`。截取 \`https://81rc.81.cn/\` 到末尾的部分 \`sy/gzdt_210283\` 作为参数填入,此时路由为 [\`/81/81rc/sy/gzdt_210283\`](https://rsshub.app/81/81rc/sy/gzdt_210283)。 -::: - `, +若订阅 [工作动态](https://81rc.81.cn/sy/gzdt_210283),网址为 \`https://81rc.81.cn/sy/gzdt_210283\`。截取 \`https://81rc.81.cn/\` 到末尾的部分 \`sy/gzdt_210283\` 作为参数填入,此时路由为 [\`/81/81rc/sy/gzdt_210283\`](https://rsshub.app/81/81rc/sy/gzdt_210283)。 +:::`, categories: ['government'], features: { diff --git a/lib/routes/8264/list.tsx b/lib/routes/8264/list.tsx index 4c27b4a8d833..1dc72d45f511 100644 --- a/lib/routes/8264/list.tsx +++ b/lib/routes/8264/list.tsx @@ -41,11 +41,11 @@ export const route: Route = { | 徒步 | 露营 | 安全急救 | 领队 | 登雪山 | | ---- | ---- | -------- | ---- | ------ | -| 242 | 950 | 931 | 920 | 915 | +| 242 | 950 | 931 | 920 | 915 | | 攀岩 | 骑行 | 跑步 | 滑雪 | 水上运动 | | ---- | ---- | ---- | ---- | -------- | -| 916 | 917 | 918 | 919 | 921 | +| 916 | 917 | 918 | 919 | 921 | | 钓鱼 | 潜水 | 攀冰 | 冲浪 | 网球 | | ---- | ---- | ---- | ---- | ---- | @@ -53,7 +53,7 @@ export const route: Route = { | 绳索知识 | 高尔夫 | 马术 | 户外摄影 | 羽毛球 | | -------- | ------ | ---- | -------- | ------ | -| 968 | 969 | 970 | 973 | 971 | +| 968 | 969 | 970 | 973 | 971 | | 游泳 | 溯溪 | 健身 | 瑜伽 | | ---- | ---- | ---- | ---- | @@ -63,15 +63,15 @@ export const route: Route = { | 服装 | 冲锋衣 | 抓绒衣 | 皮肤衣 | 速干衣 | | ---- | ------ | ------ | ------ | ------ | -| 209 | 923 | 924 | 925 | 926 | +| 209 | 923 | 924 | 925 | 926 | | 羽绒服 | 软壳 | 户外鞋 | 登山鞋 | 徒步鞋 | | ------ | ---- | ------ | ------ | ------ | -| 927 | 929 | 211 | 928 | 930 | +| 927 | 929 | 211 | 928 | 930 | | 越野跑鞋 | 溯溪鞋 | 登山杖 | 帐篷 | 睡袋 | | -------- | ------ | ------ | ---- | ---- | -| 933 | 932 | 220 | 208 | 212 | +| 933 | 932 | 220 | 208 | 212 | | 炉具 | 灯具 | 水具 | 面料 | 背包 | | ---- | ---- | ---- | ---- | ---- | @@ -79,7 +79,8 @@ export const route: Route = { | 防潮垫 | 电子导航 | 冰岩绳索 | 综合装备 | | ------ | -------- | -------- | -------- | -| 214 | 216 | 215 | 223 | +| 214 | 216 | 215 | 223 | + `, }; diff --git a/lib/routes/8kcos/utils.ts b/lib/routes/8kcos/utils.ts index 237317eaec81..ee3f03fedc8c 100644 --- a/lib/routes/8kcos/utils.ts +++ b/lib/routes/8kcos/utils.ts @@ -7,7 +7,7 @@ export const SUB_NAME_PREFIX = '8KCosplay'; export const SUB_URL = 'https://www.8kcosplay.com'; export const getPosts = async (limit: number, options?: { categories?: number; tags?: number }) => { - const data = await ofetch(`https://www.8kcosplay.com/wp-json/wp/v2/posts`, { + const data = await ofetch('https://www.8kcosplay.com/wp-json/wp/v2/posts', { query: { per_page: limit, _embed: '', @@ -26,7 +26,7 @@ export const getPosts = async (limit: number, options?: { categories?: number; t export const getCategoryInfo = (category: string) => cache.tryGet(`8kcosplay:category:${category}`, async () => { - const data = await ofetch(`https://www.8kcosplay.com/wp-json/wp/v2/categories`, { + const data = await ofetch('https://www.8kcosplay.com/wp-json/wp/v2/categories', { query: { slug: category, }, @@ -45,7 +45,7 @@ export const getCategoryInfo = (category: string) => export const getTagInfo = (tag: string) => cache.tryGet(`8kcosplay:tag:${tag}`, async () => { - const data = await ofetch(`https://www.8kcosplay.com/wp-json/wp/v2/tags`, { + const data = await ofetch('https://www.8kcosplay.com/wp-json/wp/v2/tags', { query: { slug: tag, }, diff --git a/lib/routes/91porn/index.ts b/lib/routes/91porn/index.ts index 184864e2df03..d5998e5d127a 100644 --- a/lib/routes/91porn/index.ts +++ b/lib/routes/91porn/index.ts @@ -34,7 +34,7 @@ export const route: Route = { url: '91porn.com/index.php', description: `| English | 简体中文 | 繁體中文 | | ------- | -------- | -------- | -| en_US | cn_CN | zh_ZH |`, +| en\\_US | cn\\_CN | zh\\_ZH |`, }; async function handler(ctx) { diff --git a/lib/routes/a9vg/index.ts b/lib/routes/a9vg/index.ts index 5d3525c8bf6f..2cbda0659faa 100644 --- a/lib/routes/a9vg/index.ts +++ b/lib/routes/a9vg/index.ts @@ -128,7 +128,7 @@ export const route: Route = { example: '/a9vg/news', parameters: { category: '分类,默认为 ,可在对应分类页 URL 中找到, Category, by default' }, description: `::: tip - 若订阅 [PS4](http://www.a9vg.com/list/news/PS4),网址为 \`http://www.a9vg.com/list/news/PS4\`。截取 \`http://www.a9vg.com/list\` 到末尾的部分 \`news/PS4\` 作为参数填入,此时路由为 [\`/a9vg/news/PS4\`](https://rsshub.app/a9vg/news/PS4)。 +若订阅 [PS4](http://www.a9vg.com/list/news/PS4),网址为 \`http://www.a9vg.com/list/news/PS4\`。截取 \`http://www.a9vg.com/list\` 到末尾的部分 \`news/PS4\` 作为参数填入,此时路由为 [\`/a9vg/news/PS4\`](https://rsshub.app/a9vg/news/PS4)。 ::: | 分类 | ID | @@ -141,8 +141,7 @@ export const route: Route = { | [XSX](https://www.a9vg.com/list/news/XSX) | [news/XSX](https://rsshub.app/a9vg/news/XSX) | | [PC](https://www.a9vg.com/list/news/PC) | [news/PC](https://rsshub.app/a9vg/news/PC) | | [业界](https://www.a9vg.com/list/news/Industry) | [news/Industry](https://rsshub.app/a9vg/news/Industry) | -| [厂商](https://www.a9vg.com/list/news/Factory) | [news/Factory](https://rsshub.app/a9vg/news/Factory) | - `, +| [厂商](https://www.a9vg.com/list/news/Factory) | [news/Factory](https://rsshub.app/a9vg/news/Factory) |`, categories: ['game'], features: { diff --git a/lib/routes/aa1/60s.ts b/lib/routes/aa1/60s.ts index 80ddbc69207b..a6569c4b5906 100644 --- a/lib/routes/aa1/60s.ts +++ b/lib/routes/aa1/60s.ts @@ -132,7 +132,7 @@ export const route: Route = { }, }, description: `::: tip -订阅 [每天60秒读懂世界](https://60s.aa1.cn/category/news),其源网址为 \`https://60s.aa1.cn/category/news\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/aa1/60s/news\`](https://rsshub.app/aa1/60s/news) 或 [\`/aa1/60s/每天60秒读懂世界\`](https://rsshub.app/aa1/60s/每天60秒读懂世界)。 +订阅 [每天 60 秒读懂世界](https://60s.aa1.cn/category/news),其源网址为 \`https://60s.aa1.cn/category/news\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/aa1/60s/news\`](https://rsshub.app/aa1/60s/news) 或 [\`/aa1/60s/每天60秒读懂世界\`](https://rsshub.app/aa1/60s/每天60秒读懂世界)。 ::: | 分类 | ID | @@ -141,8 +141,7 @@ export const route: Route = { | [新闻词文章数据](https://60s.aa1.cn/category/freenewsdata) | [freenewsdata](https://rsshub.app/aa1/60s/freenewsdata) | | [最新](https://60s.aa1.cn/category/new) | [new](https://rsshub.app/aa1/60s/new) | | [本平台同款自动发文章插件](https://60s.aa1.cn/category/1) | [1](https://rsshub.app/aa1/60s/1) | -| [每天 60 秒读懂世界](https://60s.aa1.cn/category/news) | [news](https://rsshub.app/aa1/60s/news) | -`, +| [每天 60 秒读懂世界](https://60s.aa1.cn/category/news) | [news](https://rsshub.app/aa1/60s/news) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/aamacau/index.ts b/lib/routes/aamacau/index.ts index 0dd98c78af1f..86d330dffa4e 100644 --- a/lib/routes/aamacau/index.ts +++ b/lib/routes/aamacau/index.ts @@ -32,11 +32,11 @@ export const route: Route = { | breakingnews | weeklytopic | culture | press | case | special | ::: tip - 除了直接订阅分类全部文章(如 [每週專題](https://aamacau.com/topics/weeklytopic) 的对应路由为 [/aamacau/weeklytopic](https://rsshub.app/aamacau/weeklytopic)),你也可以订阅特定的专题,如 [【9-12】2021 澳門立法會選舉](https://aamacau.com/topics/【9-12】2021澳門立法會選舉) 的对应路由为 [/【9-12】2021 澳門立法會選舉](https://rsshub.app/aamacau/【9-12】2021澳門立法會選舉)。 +除了直接订阅分类全部文章(如 [每週專題](https://aamacau.com/topics/weeklytopic) 的对应路由为 [/aamacau/weeklytopic](https://rsshub.app/aamacau/weeklytopic)),你也可以订阅特定的专题,如 [【9-12】2021 澳門立法會選舉](https://aamacau.com/topics/【9-12】2021澳門立法會選舉) 的对应路由为 [/【9-12】2021 澳門立法會選舉](https://rsshub.app/aamacau/【9-12】2021澳門立法會選舉)。 - 分类中的专题也可以单独订阅,如 [新聞事件](https://aamacau.com/topics/case) 中的 [「武漢肺炎」新聞檔案](https://aamacau.com/topics/case/「武漢肺炎」新聞檔案) 对应路由为 [/case/「武漢肺炎」新聞檔案](https://rsshub.app/aamacau/case/「武漢肺炎」新聞檔案)。 +分类中的专题也可以单独订阅,如 [新聞事件](https://aamacau.com/topics/case) 中的 [「武漢肺炎」新聞檔案](https://aamacau.com/topics/case/「武漢肺炎」新聞檔案) 对应路由为 [/case/「武漢肺炎」新聞檔案](https://rsshub.app/aamacau/case/「武漢肺炎」新聞檔案)。 - 同理,其他分类同上例子也可以订阅特定的单独专题。 +同理,其他分类同上例子也可以订阅特定的单独专题。 :::`, }; diff --git a/lib/routes/abc/index.ts b/lib/routes/abc/index.ts index 8ac465c92f7f..bc66f7119447 100644 --- a/lib/routes/abc/index.ts +++ b/lib/routes/abc/index.ts @@ -21,13 +21,12 @@ export const route: Route = { }, name: 'Channel & Topic', categories: ['traditional-media'], - description: ` -::: tip - All Topics in [Topic Library](https://abc.net.au/news/topics) are supported, you can fill in the field after \`topic\` in its URL, or fill in the \`documentId\`. + description: `::: tip +All Topics in [Topic Library](https://abc.net.au/news/topics) are supported, you can fill in the field after \`topic\` in its URL, or fill in the \`documentId\`. - For example, the URL for [Computer Science](https://www.abc.net.au/news/topic/computer-science) is \`https://www.abc.net.au/news/topic/computer-science\`, the \`category\` is \`news/topic/computer-science\`, and the \`documentId\` of the Topic is \`2302\`, so the route is [/abc/news/topic/computer-science](https://rsshub.app/abc/news/topic/computer-science) and [/abc/2302](https://rsshub.app/abc/2302). +For example, the URL for [Computer Science](https://www.abc.net.au/news/topic/computer-science) is \`https://www.abc.net.au/news/topic/computer-science\`, the \`category\` is \`news/topic/computer-science\`, and the \`documentId\` of the Topic is \`2302\`, so the route is [/abc/news/topic/computer-science](https://rsshub.app/abc/news/topic/computer-science) and [/abc/2302](https://rsshub.app/abc/2302). - The supported channels are all listed in the table below. For other channels, please find the \`documentId\` in the source code of the channel page and fill it in as above. +The supported channels are all listed in the table below. For other channels, please find the \`documentId\` in the source code of the channel page and fill it in as above. :::`, maintainers: ['nczitzk', 'pseudoyu'], handler, diff --git a/lib/routes/abmedia/category.ts b/lib/routes/abmedia/category.ts index e12f635b6fc9..4594821ce4b1 100644 --- a/lib/routes/abmedia/category.ts +++ b/lib/routes/abmedia/category.ts @@ -32,7 +32,7 @@ export const route: Route = { handler, description: `参数可以从链接中拿到,如: - \`https://www.abmedia.io/category/technology-development\` 对应 \`/abmedia/technology-development\``, +\`https://www.abmedia.io/category/technology-development\` 对应 \`/abmedia/technology-development\``, }; async function handler(ctx) { diff --git a/lib/routes/accessbriefing/index.ts b/lib/routes/accessbriefing/index.ts index 40b8937a8df5..9ea022076b30 100644 --- a/lib/routes/accessbriefing/index.ts +++ b/lib/routes/accessbriefing/index.ts @@ -122,7 +122,7 @@ export const route: Route = { example: '/accessbriefing/latest/news', parameters: { category: 'Category, Latest News by default' }, description: `::: tip - If you subscribe to [Latest News](https://www.accessbriefing.com/latest/news),where the URL is \`https://www.accessbriefing.com/latest/news\`, extract the part \`https://www.accessbriefing.com/\` to the end, and use it as the parameter to fill in. Therefore, the route will be [\`/accessbriefing/latest/news\`](https://rsshub.app/accessbriefing/latest/news). +If you subscribe to [Latest News](https://www.accessbriefing.com/latest/news),where the URL is \`https://www.accessbriefing.com/latest/news\`, extract the part \`https://www.accessbriefing.com/\` to the end, and use it as the parameter to fill in. Therefore, the route will be [\`/accessbriefing/latest/news\`](https://rsshub.app/accessbriefing/latest/news). ::: #### Latest @@ -143,8 +143,7 @@ export const route: Route = { | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | [Interviews](https://www.accessbriefing.com/insight/interviews) | [insight/interviews](https://rsshub.app/target/site/insight/interviews) | | [Longer reads](https://www.accessbriefing.com/insight/longer-reads) | [insight/longer-reads](https://rsshub.app/target/site/insight/longer-reads) | -| [Videos and podcasts](https://www.accessbriefing.com/insight/videos-and-podcasts) | [insight/videos-and-podcasts](https://rsshub.app/target/site/insight/videos-and-podcasts) | - `, +| [Videos and podcasts](https://www.accessbriefing.com/insight/videos-and-podcasts) | [insight/videos-and-podcasts](https://rsshub.app/target/site/insight/videos-and-podcasts) |`, categories: ['new-media'], features: { diff --git a/lib/routes/acg17/post.ts b/lib/routes/acg17/post.ts index c3bd077cd64e..2db840a31ed0 100644 --- a/lib/routes/acg17/post.ts +++ b/lib/routes/acg17/post.ts @@ -32,7 +32,7 @@ async function handler() { const response = await got(`${host}/wp-json/wp/v2/posts?per_page=30`); const list = response.data; return { - title: `ACG17 - 全部文章`, + title: 'ACG17 - 全部文章', link: `${host}/blog`, description: 'ACG17 - 全部文章', item: list.map((item) => ({ diff --git a/lib/routes/acs/journal.tsx b/lib/routes/acs/journal.tsx index 84a95988c4a8..4ad4d5587da1 100644 --- a/lib/routes/acs/journal.tsx +++ b/lib/routes/acs/journal.tsx @@ -6,7 +6,7 @@ import { config } from '@/config'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/journal/:id', @@ -28,7 +28,7 @@ async function handler(ctx) { let title = ''; - const browser = await puppeteer(); + const browser = await playwright(); const items = await cache.tryGet( currentUrl, async () => { diff --git a/lib/routes/aeaweb/index.tsx b/lib/routes/aeaweb/index.tsx index 97b6d4bfd1c2..6d264824f660 100644 --- a/lib/routes/aeaweb/index.tsx +++ b/lib/routes/aeaweb/index.tsx @@ -30,7 +30,7 @@ export const route: Route = { description: `The URL of the journal [American Economic Review](https://www.aeaweb.org/journals/aer) is \`https://www.aeaweb.org/journals/aer\`, where \`aer\` is the id of the journal, so the route for this journal is \`/aeaweb/aer\`. ::: tip - More jounals can be found in [AEA Journals](https://www.aeaweb.org/journals). +More jounals can be found in [AEA Journals](https://www.aeaweb.org/journals). :::`, }; diff --git a/lib/routes/aeon/type.ts b/lib/routes/aeon/type.ts index a105aacbeba8..f9bebda440d2 100644 --- a/lib/routes/aeon/type.ts +++ b/lib/routes/aeon/type.ts @@ -66,7 +66,7 @@ export const route: Route = { handler, description: `Supported types: Essays and Videos. - Compared to the official one, the RSS feed generated by RSSHub not only has more fine-grained options, but also eliminates pull quotes, which can't be easily distinguished from other paragraphs by any RSS reader, but only disrupt the reading flow. This feed also provides users with a bio of the author at the top.`, +Compared to the official one, the RSS feed generated by RSSHub not only has more fine-grained options, but also eliminates pull quotes, which can't be easily distinguished from other paragraphs by any RSS reader, but only disrupt the reading flow. This feed also provides users with a bio of the author at the top.`, }; async function handler(ctx) { diff --git a/lib/routes/afdian/explore.ts b/lib/routes/afdian/explore.ts index e212ca4066c3..9e03ca74313d 100644 --- a/lib/routes/afdian/explore.ts +++ b/lib/routes/afdian/explore.ts @@ -39,7 +39,7 @@ export const route: Route = { | ---- | ---- | | rec | hot | - 目录类型 +目录类型 | 所有 | 绘画 | 视频 | 写作 | 游戏 | 音乐 | 播客 | 摄影 | 技术 | Vtuber | 舞蹈 | 体育 | 旅游 | 美食 | 时尚 | 数码 | 动画 | 其他 | | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ------ | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | diff --git a/lib/routes/agora0/index.ts b/lib/routes/agora0/index.ts index ed6f17a6765d..8863a29862af 100644 --- a/lib/routes/agora0/index.ts +++ b/lib/routes/agora0/index.ts @@ -28,8 +28,8 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `| muitinⒾ | aidemnⒾ | srettaⓂ | qⓅ | sucoⓋ | -| ------- | ------- | -------- | -- | ----- | -| initium | inmedia | matters | pq | vocus |`, +| ------- | ------- | ------- | -- | ----- | +| initium | inmedia | matters | pq | vocus |`, }; async function handler(ctx) { diff --git a/lib/routes/agri/index.ts b/lib/routes/agri/index.ts index cdacaf36f62f..2e5697cb0bd5 100644 --- a/lib/routes/agri/index.ts +++ b/lib/routes/agri/index.ts @@ -110,7 +110,7 @@ export const route: Route = { example: '/agri/zx/zxfb', parameters: { category: '分类,默认为 `zx/zxfb`,即最新发布,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [最新发布](http://www.agri.cn/zx/zxfb/),网址为 \`http://www.agri.cn/zx/zxfb/\`。截取 \`https://www.agri.cn/\` 到末尾的部分 \`zx/zxfb\` 作为参数填入,此时路由为 [\`/agri/zx/zxfb\`](https://rsshub.app/agri/zx/zxfb)。 +若订阅 [最新发布](http://www.agri.cn/zx/zxfb/),网址为 \`http://www.agri.cn/zx/zxfb/\`。截取 \`https://www.agri.cn/\` 到末尾的部分 \`zx/zxfb\` 作为参数填入,此时路由为 [\`/agri/zx/zxfb\`](https://rsshub.app/agri/zx/zxfb)。 ::: #### [机构](http://www.agri.cn/jg/) @@ -164,8 +164,7 @@ export const route: Route = { | [地方农业](http://www.agri.cn/video/dfny/beijing/) | [video/dfny/beijing](https://rsshub.app/agri/video/dfny/beijing) | | [气象农业](http://www.agri.cn/video/qxny/) | [video/qxny](https://rsshub.app/agri/video/qxny) | | [讲座培训](http://www.agri.cn/video/jzpx/) | [video/jzpx](https://rsshub.app/agri/video/jzpx) | -| [文化生活](http://www.agri.cn/video/whsh/) | [video/whsh](https://rsshub.app/agri/video/whsh) | - `, +| [文化生活](http://www.agri.cn/video/whsh/) | [video/whsh](https://rsshub.app/agri/video/whsh) |`, categories: ['new-media'], features: { diff --git a/lib/routes/aibase/discover.ts b/lib/routes/aibase/discover.ts index 3e592b386f3e..14e27b256fab 100644 --- a/lib/routes/aibase/discover.ts +++ b/lib/routes/aibase/discover.ts @@ -87,7 +87,7 @@ export const route: Route = { example: '/aibase/discover', parameters: { id: '发现分类,默认为空,即全部产品,可在对应发现分类页 URL 中找到' }, description: `::: tip - 若订阅 [图片背景移除](https://top.aibase.com/discover/37-49),网址为 \`https://top.aibase.com/discover/37-49\`。截取 \`https://top.aibase.com/discover/\` 到末尾的部分 \`37-49\` 作为参数填入,此时路由为 [\`/aibase/discover/37-49\`](https://rsshub.app/aibase/discover/37-49)。 +若订阅 [图片背景移除](https://top.aibase.com/discover/37-49),网址为 \`https://top.aibase.com/discover/37-49\`。截取 \`https://top.aibase.com/discover/\` 到末尾的部分 \`37-49\` 作为参数填入,此时路由为 [\`/aibase/discover/37-49\`](https://rsshub.app/aibase/discover/37-49)。 :::
@@ -95,36 +95,36 @@ export const route: Route = { #### 图像处理 -| 分类 | ID | -| ----------------------------------------------------- | ------------------------------------------------- | -| [图片背景移除](https://top.aibase.com/discover/37-49) | [37-49](https://rsshub.app/aibase/discover/37-49) | -| [图片无损放大](https://top.aibase.com/discover/37-50) | [37-50](https://rsshub.app/aibase/discover/37-50) | -| [图片AI修复](https://top.aibase.com/discover/37-51) | [37-51](https://rsshub.app/aibase/discover/37-51) | -| [图像生成](https://top.aibase.com/discover/37-52) | [37-52](https://rsshub.app/aibase/discover/37-52) | -| [Ai图片拓展](https://top.aibase.com/discover/37-53) | [37-53](https://rsshub.app/aibase/discover/37-53) | -| [Ai漫画生成](https://top.aibase.com/discover/37-54) | [37-54](https://rsshub.app/aibase/discover/37-54) | -| [Ai生成写真](https://top.aibase.com/discover/37-55) | [37-55](https://rsshub.app/aibase/discover/37-55) | -| [电商图片制作](https://top.aibase.com/discover/37-83) | [37-83](https://rsshub.app/aibase/discover/37-83) | -| [Ai图像转视频](https://top.aibase.com/discover/37-86) | [37-86](https://rsshub.app/aibase/discover/37-86) | +| 分类 | ID | +| ------------------------------------------------------ | ------------------------------------------------- | +| [图片背景移除](https://top.aibase.com/discover/37-49) | [37-49](https://rsshub.app/aibase/discover/37-49) | +| [图片无损放大](https://top.aibase.com/discover/37-50) | [37-50](https://rsshub.app/aibase/discover/37-50) | +| [图片 AI 修复](https://top.aibase.com/discover/37-51) | [37-51](https://rsshub.app/aibase/discover/37-51) | +| [图像生成](https://top.aibase.com/discover/37-52) | [37-52](https://rsshub.app/aibase/discover/37-52) | +| [Ai 图片拓展](https://top.aibase.com/discover/37-53) | [37-53](https://rsshub.app/aibase/discover/37-53) | +| [Ai 漫画生成](https://top.aibase.com/discover/37-54) | [37-54](https://rsshub.app/aibase/discover/37-54) | +| [Ai 生成写真](https://top.aibase.com/discover/37-55) | [37-55](https://rsshub.app/aibase/discover/37-55) | +| [电商图片制作](https://top.aibase.com/discover/37-83) | [37-83](https://rsshub.app/aibase/discover/37-83) | +| [Ai 图像转视频](https://top.aibase.com/discover/37-86) | [37-86](https://rsshub.app/aibase/discover/37-86) | #### 视频创作 -| 分类 | ID | -| --------------------------------------------------- | ------------------------------------------------- | -| [视频剪辑](https://top.aibase.com/discover/38-56) | [38-56](https://rsshub.app/aibase/discover/38-56) | -| [生成视频](https://top.aibase.com/discover/38-57) | [38-57](https://rsshub.app/aibase/discover/38-57) | -| [Ai动画制作](https://top.aibase.com/discover/38-58) | [38-58](https://rsshub.app/aibase/discover/38-58) | -| [字幕生成](https://top.aibase.com/discover/38-84) | [38-84](https://rsshub.app/aibase/discover/38-84) | +| 分类 | ID | +| ---------------------------------------------------- | ------------------------------------------------- | +| [视频剪辑](https://top.aibase.com/discover/38-56) | [38-56](https://rsshub.app/aibase/discover/38-56) | +| [生成视频](https://top.aibase.com/discover/38-57) | [38-57](https://rsshub.app/aibase/discover/38-57) | +| [Ai 动画制作](https://top.aibase.com/discover/38-58) | [38-58](https://rsshub.app/aibase/discover/38-58) | +| [字幕生成](https://top.aibase.com/discover/38-84) | [38-84](https://rsshub.app/aibase/discover/38-84) | #### 效率助手 -| 分类 | ID | -| --------------------------------------------------- | ------------------------------------------------- | -| [AI文档工具](https://top.aibase.com/discover/39-59) | [39-59](https://rsshub.app/aibase/discover/39-59) | -| [PPT](https://top.aibase.com/discover/39-60) | [39-60](https://rsshub.app/aibase/discover/39-60) | -| [思维导图](https://top.aibase.com/discover/39-61) | [39-61](https://rsshub.app/aibase/discover/39-61) | -| [表格处理](https://top.aibase.com/discover/39-62) | [39-62](https://rsshub.app/aibase/discover/39-62) | -| [Ai办公助手](https://top.aibase.com/discover/39-63) | [39-63](https://rsshub.app/aibase/discover/39-63) | +| 分类 | ID | +| ---------------------------------------------------- | ------------------------------------------------- | +| [AI 文档工具](https://top.aibase.com/discover/39-59) | [39-59](https://rsshub.app/aibase/discover/39-59) | +| [PPT](https://top.aibase.com/discover/39-60) | [39-60](https://rsshub.app/aibase/discover/39-60) | +| [思维导图](https://top.aibase.com/discover/39-61) | [39-61](https://rsshub.app/aibase/discover/39-61) | +| [表格处理](https://top.aibase.com/discover/39-62) | [39-62](https://rsshub.app/aibase/discover/39-62) | +| [Ai 办公助手](https://top.aibase.com/discover/39-63) | [39-63](https://rsshub.app/aibase/discover/39-63) | #### 写作灵感 @@ -135,27 +135,27 @@ export const route: Route = { #### 艺术灵感 -| 分类 | ID | -| --------------------------------------------------- | ------------------------------------------------- | -| [音乐创作](https://top.aibase.com/discover/41-65) | [41-65](https://rsshub.app/aibase/discover/41-65) | -| [设计创作](https://top.aibase.com/discover/41-66) | [41-66](https://rsshub.app/aibase/discover/41-66) | -| [Ai图标生成](https://top.aibase.com/discover/41-67) | [41-67](https://rsshub.app/aibase/discover/41-67) | +| 分类 | ID | +| ---------------------------------------------------- | ------------------------------------------------- | +| [音乐创作](https://top.aibase.com/discover/41-65) | [41-65](https://rsshub.app/aibase/discover/41-65) | +| [设计创作](https://top.aibase.com/discover/41-66) | [41-66](https://rsshub.app/aibase/discover/41-66) | +| [Ai 图标生成](https://top.aibase.com/discover/41-67) | [41-67](https://rsshub.app/aibase/discover/41-67) | #### 趣味 -| 分类 | ID | -| ----------------------------------------------------- | ------------------------------------------------- | -| [Ai名字生成器](https://top.aibase.com/discover/42-68) | [42-68](https://rsshub.app/aibase/discover/42-68) | -| [游戏娱乐](https://top.aibase.com/discover/42-71) | [42-71](https://rsshub.app/aibase/discover/42-71) | -| [其他](https://top.aibase.com/discover/42-72) | [42-72](https://rsshub.app/aibase/discover/42-72) | +| 分类 | ID | +| ------------------------------------------------------ | ------------------------------------------------- | +| [Ai 名字生成器](https://top.aibase.com/discover/42-68) | [42-68](https://rsshub.app/aibase/discover/42-68) | +| [游戏娱乐](https://top.aibase.com/discover/42-71) | [42-71](https://rsshub.app/aibase/discover/42-71) | +| [其他](https://top.aibase.com/discover/42-72) | [42-72](https://rsshub.app/aibase/discover/42-72) | #### 开发编程 -| 分类 | ID | -| --------------------------------------------------- | ------------------------------------------------- | -| [开发编程](https://top.aibase.com/discover/43-73) | [43-73](https://rsshub.app/aibase/discover/43-73) | -| [Ai开放平台](https://top.aibase.com/discover/43-74) | [43-74](https://rsshub.app/aibase/discover/43-74) | -| [Ai算力平台](https://top.aibase.com/discover/43-75) | [43-75](https://rsshub.app/aibase/discover/43-75) | +| 分类 | ID | +| ---------------------------------------------------- | ------------------------------------------------- | +| [开发编程](https://top.aibase.com/discover/43-73) | [43-73](https://rsshub.app/aibase/discover/43-73) | +| [Ai 开放平台](https://top.aibase.com/discover/43-74) | [43-74](https://rsshub.app/aibase/discover/43-74) | +| [Ai 算力平台](https://top.aibase.com/discover/43-75) | [43-75](https://rsshub.app/aibase/discover/43-75) | #### 聊天机器人 @@ -187,8 +187,8 @@ export const route: Route = { | 分类 | ID | | ----------------------------------------------- | ----------------------------------------------------- | | [法律](https://top.aibase.com/discover/138-139) | [138-139](https://rsshub.app/aibase/discover/138-139) | -
- `, + +`, categories: ['new-media'], features: { diff --git a/lib/routes/aibase/topic.ts b/lib/routes/aibase/topic.ts index dff5ea8ca49e..e0ddf9ac9399 100644 --- a/lib/routes/aibase/topic.ts +++ b/lib/routes/aibase/topic.ts @@ -57,11 +57,11 @@ export const route: Route = { example: '/aibase/topic', parameters: { id: '标签,默认为空,即全部产品,可在对应标签页 URL 中找到', filter: '过滤器,默认为 `id` 即最新,可选 `pv` 即热门' }, description: `::: tip - 若订阅 [AI](https://top.aibase.com/topic/AI),网址为 \`https://top.aibase.com/topic/AI\`。截取 \`https://top.aibase.com/topic\` 到末尾的部分 \`AI\` 作为参数填入,此时路由为 [\`/aibase/topic/AI\`](https://rsshub.app/aibase/topic/AI)。 +若订阅 [AI](https://top.aibase.com/topic/AI),网址为 \`https://top.aibase.com/topic/AI\`。截取 \`https://top.aibase.com/topic\` 到末尾的部分 \`AI\` 作为参数填入,此时路由为 [\`/aibase/topic/AI\`](https://rsshub.app/aibase/topic/AI)。 ::: ::: tip - 此处查看 [全部标签](https://top.aibase.com/topic) +此处查看 [全部标签](https://top.aibase.com/topic) :::
@@ -88,8 +88,8 @@ export const route: Route = { | [笔记](https://top.aibase.com/topic/%E7%AC%94%E8%AE%B0) | [搜索引擎](https://top.aibase.com/topic/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E) | [计算机视觉](https://top.aibase.com/topic/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89) | [社区](https://top.aibase.com/topic/%E7%A4%BE%E5%8C%BA) | [效率](https://top.aibase.com/topic/%E6%95%88%E7%8E%87) | | [知识管理](https://top.aibase.com/topic/%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86) | [LLM](https://top.aibase.com/topic/LLM) | [智能聊天](https://top.aibase.com/topic/%E6%99%BA%E8%83%BD%E8%81%8A%E5%A4%A9) | [社交](https://top.aibase.com/topic/%E7%A4%BE%E4%BA%A4) | [语言学习](https://top.aibase.com/topic/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0) | | [娱乐](https://top.aibase.com/topic/%E5%A8%B1%E4%B9%90) | [简历](https://top.aibase.com/topic/%E7%AE%80%E5%8E%86) | [OpenAI](https://top.aibase.com/topic/OpenAI) | [客户服务](https://top.aibase.com/topic/%E5%AE%A2%E6%88%B7%E6%9C%8D%E5%8A%A1) | [室内设计](https://top.aibase.com/topic/%E5%AE%A4%E5%86%85%E8%AE%BE%E8%AE%A1) | -
- `, + +`, categories: ['new-media'], features: { diff --git a/lib/routes/aiea/index.ts b/lib/routes/aiea/index.ts index d7ad69f45ae8..630e676f9716 100644 --- a/lib/routes/aiea/index.ts +++ b/lib/routes/aiea/index.ts @@ -49,7 +49,7 @@ async function handler(ctx) { return await buildData({ link, url: link, - title: `%title%`, + title: '%title%', params: { title: 'AIEA Seminars', }, diff --git a/lib/routes/aijishu/index.ts b/lib/routes/aijishu/index.ts index 9371e56433f6..1184967c33df 100644 --- a/lib/routes/aijishu/index.ts +++ b/lib/routes/aijishu/index.ts @@ -30,7 +30,7 @@ export const route: Route = { async function handler(ctx) { const { type, name = 'newest' } = ctx.req.param(); - const u = name === 'newest' ? `https://aijishu.com/` : `https://aijishu.com/${type}/${name}`; + const u = name === 'newest' ? 'https://aijishu.com/' : `https://aijishu.com/${type}/${name}`; const html = await got(u); const $ = load(html.data); diff --git a/lib/routes/aip/journal-pupp.ts b/lib/routes/aip/journal-pupp.ts index a6eb8dcafdb2..845661a89223 100644 --- a/lib/routes/aip/journal-pupp.ts +++ b/lib/routes/aip/journal-pupp.ts @@ -3,27 +3,27 @@ import { load } from 'cheerio'; import { config } from '@/config'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import cache from '@/utils/cache'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { isValidHost } from '@/utils/valid-host'; -import { puppeteerGet, renderDesc } from './utils'; +import { playwrightGet, renderDesc } from './utils'; const handler = async (ctx) => { const pub = ctx.req.param('pub'); const jrn = ctx.req.param('jrn'); - const host = `https://pubs.aip.org`; + const host = 'https://pubs.aip.org'; const jrnlUrl = `${host}/${pub}/${jrn}/issue`; if (!isValidHost(pub)) { throw new InvalidParameterError('Invalid pub'); } - // use Puppeteer due to the obstacle by cloudflare challenge - const browser = await puppeteer(); + // use Playwright due to the obstacle by cloudflare challenge + const browser = await playwright(); const { jrnlName, list } = await cache.tryGet( jrnlUrl, async () => { - const response = await puppeteerGet(jrnlUrl, browser); + const response = await playwrightGet(jrnlUrl, browser); const $ = load(response); const jrnlName = $('.header-journal-title').text(); const list = $('.card') diff --git a/lib/routes/aip/journal.ts b/lib/routes/aip/journal.ts index f958c3a5b400..68bea00e2867 100644 --- a/lib/routes/aip/journal.ts +++ b/lib/routes/aip/journal.ts @@ -29,14 +29,14 @@ export const route: Route = { description: `Refer to the URL format \`pubs.aip.org/:pub/:jrn\` ::: tip - More jounals can be found in [AIP Publications](https://publishing.aip.org/publications/find-the-right-journal). +More jounals can be found in [AIP Publications](https://publishing.aip.org/publications/find-the-right-journal). :::`, }; async function handler(ctx) { const pub = ctx.req.param('pub'); const jrn = ctx.req.param('jrn'); - const host = `https://pubs.aip.org`; + const host = 'https://pubs.aip.org'; const jrnlUrl = `${host}/${pub}/${jrn}/issue`; const { data: response } = await got.get(jrnlUrl); diff --git a/lib/routes/aip/utils.tsx b/lib/routes/aip/utils.tsx index a18ba6b306b5..ba592557f627 100644 --- a/lib/routes/aip/utils.tsx +++ b/lib/routes/aip/utils.tsx @@ -1,6 +1,6 @@ import { renderToString } from 'hono/jsx/dom/server'; -const puppeteerGet = async (url, browser) => { +const playwrightGet = async (url, browser) => { const page = await browser.newPage(); // await page.setExtraHTTPHeaders({ referer: host }); await page.setRequestInterception(true); @@ -41,4 +41,4 @@ const renderDesc = (title, authors, doi, img) => ); -export { puppeteerGet, renderDesc }; +export { playwrightGet, renderDesc }; diff --git a/lib/routes/air-level/namespace.ts b/lib/routes/air-level/namespace.ts index d85046bbd874..5edc0a61e270 100644 --- a/lib/routes/air-level/namespace.ts +++ b/lib/routes/air-level/namespace.ts @@ -3,10 +3,8 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Air-Level', url: 'air-level.com', - description: ` - - 可以订阅每个城市的空气质量,按照拼音订阅 - - 支持订阅每天的实时排名 - `, + description: `- 可以订阅每个城市的空气质量,按照拼音订阅 +- 支持订阅每天的实时排名`, categories: ['forecast'], lang: 'zh-CN', }; diff --git a/lib/routes/airchina/index.ts b/lib/routes/airchina/index.ts index dbe342e5821f..8f92d8b5fd0f 100644 --- a/lib/routes/airchina/index.ts +++ b/lib/routes/airchina/index.ts @@ -36,8 +36,8 @@ async function handler() { const data = await buildData({ link, url: link, - title: `%title%`, - description: `%description%`, + title: '%title%', + description: '%description%', params: { title: '国航服务公告', description: '中国国际航空公司服务公告', diff --git a/lib/routes/aisixiang/zhuanti.ts b/lib/routes/aisixiang/zhuanti.ts index 212adbd89c50..d6a9741212bf 100644 --- a/lib/routes/aisixiang/zhuanti.ts +++ b/lib/routes/aisixiang/zhuanti.ts @@ -25,7 +25,7 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 更多专题请见 [关键词](http://www.aisixiang.com/zhuanti/) +更多专题请见 [关键词](http://www.aisixiang.com/zhuanti/) :::`, }; diff --git a/lib/routes/ali213/news.ts b/lib/routes/ali213/news.ts index 0520769bab23..f19844e862d4 100644 --- a/lib/routes/ali213/news.ts +++ b/lib/routes/ali213/news.ts @@ -196,8 +196,7 @@ export const route: Route = { | 科技 | tech | | 电竞 | esports | | 娱乐 | amuse | -| 手游 | mobile | -`, +| 手游 | mobile |`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/ali213/zl.ts b/lib/routes/ali213/zl.ts index 16a1234e3ede..13c7cdf1a64f 100644 --- a/lib/routes/ali213/zl.ts +++ b/lib/routes/ali213/zl.ts @@ -173,8 +173,7 @@ export const route: Route = { | 首页 | 游戏 | 动漫 | 影视 | 娱乐 | | ---------------------------------------- | -------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | -| [index](https://www.ali213.net/news/zl/) | [game](https://www.ali213.net/news/zl/game/) | [comic](https://www.ali213.net/news/zl/comic/) | [movie](https://www.ali213.net/news/zl/movie/) | [amuse](https://www.ali213.net/news/zl/amuse/) | -`, +| [index](https://www.ali213.net/news/zl/) | [game](https://www.ali213.net/news/zl/game/) | [comic](https://www.ali213.net/news/zl/comic/) | [movie](https://www.ali213.net/news/zl/movie/) | [amuse](https://www.ali213.net/news/zl/amuse/) |`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/ally/rail.ts b/lib/routes/ally/rail.ts index 412d8bb9c422..c7c67e5b886c 100644 --- a/lib/routes/ally/rail.ts +++ b/lib/routes/ally/rail.ts @@ -29,7 +29,7 @@ export const route: Route = { handler, url: 'rail.ally.net.cn/', description: `::: tip - 默认抓取前 20 条,可通过 \`?limit=\` 改变。 +默认抓取前 20 条,可通过 \`?limit=\` 改变。 :::`, }; diff --git a/lib/routes/alpinelinux/pkgs.ts b/lib/routes/alpinelinux/pkgs.ts index bcaf1c4aa0a9..12a754649c7c 100644 --- a/lib/routes/alpinelinux/pkgs.ts +++ b/lib/routes/alpinelinux/pkgs.ts @@ -14,7 +14,7 @@ export const route: Route = { path: '/pkgs/:name/:routeParams?', parameters: { name: 'Packages name', routeParams: 'Filters of packages type. E.g. branch=edge&repo=main&arch=armv7&maintainer=Jakub%20Jirutka' }, example: '/alpinelinux/pkgs/nodejs', - description: `Alpine Linux packages update`, + description: 'Alpine Linux packages update', handler, radar: [ { diff --git a/lib/routes/alternativeto/platform.ts b/lib/routes/alternativeto/platform.ts index 8d3e5d1dfb06..06e96efb52a2 100644 --- a/lib/routes/alternativeto/platform.ts +++ b/lib/routes/alternativeto/platform.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { baseURL, puppeteerGet } from './utils'; +import { baseURL, playwrightGet } from './utils'; export const route: Route = { path: '/platform/:name/:routeParams?', @@ -27,7 +27,7 @@ export const route: Route = { name: 'Platform Software', maintainers: ['JimenezLi'], handler, - description: `> routeParms can be copied from original site URL, example: \`/alternativeto/platform/firefox/license=free\``, + description: '> routeParms can be copied from original site URL, example: `/alternativeto/platform/firefox/license=free`', }; async function handler(ctx) { @@ -35,8 +35,8 @@ async function handler(ctx) { const query = new URLSearchParams(ctx.req.param('routeParams')); const link = `https://alternativeto.net/platform/${name}/?${query.toString()}`; - // use Puppeteer due to the obstacle by cloudflare challenge - const html = await puppeteerGet(link, cache); + // use Playwright due to the obstacle by cloudflare challenge + const html = await playwrightGet(link, cache); const $ = load(html); return { diff --git a/lib/routes/alternativeto/software.ts b/lib/routes/alternativeto/software.ts index 8a23ff78b97f..0cb72177b3ad 100644 --- a/lib/routes/alternativeto/software.ts +++ b/lib/routes/alternativeto/software.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; -import { baseURL, puppeteerGet } from './utils'; +import { baseURL, playwrightGet } from './utils'; export const route: Route = { path: '/software/:name/:routeParams?', @@ -27,7 +27,7 @@ export const route: Route = { name: 'Software Alternatives', maintainers: ['JimenezLi'], handler, - description: `> routeParms can be copied from original site URL, example: \`/alternativeto/software/cpp/license=opensource&platform=windows\``, + description: '> routeParms can be copied from original site URL, example: `/alternativeto/software/cpp/license=opensource&platform=windows`', }; async function handler(ctx) { @@ -35,8 +35,8 @@ async function handler(ctx) { const query = new URLSearchParams(ctx.req.param('routeParams')); const link = `https://alternativeto.net/software/${name}/?${query.toString()}`; - // use Puppeteer due to the obstacle by cloudflare challenge - const html = await puppeteerGet(link, cache); + // use Playwright due to the obstacle by cloudflare challenge + const html = await playwrightGet(link, cache); const $ = load(html); return { diff --git a/lib/routes/alternativeto/utils.ts b/lib/routes/alternativeto/utils.ts index c53c34b8afe6..08fd66814fef 100644 --- a/lib/routes/alternativeto/utils.ts +++ b/lib/routes/alternativeto/utils.ts @@ -1,10 +1,10 @@ -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseURL = 'https://alternativeto.net'; -const puppeteerGet = (url, cache) => +const playwrightGet = (url, cache) => cache.tryGet(url, async () => { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { @@ -18,4 +18,4 @@ const puppeteerGet = (url, cache) => return html; }); -export { baseURL, puppeteerGet }; +export { baseURL, playwrightGet }; diff --git a/lib/routes/amazfitwatchfaces/index.ts b/lib/routes/amazfitwatchfaces/index.ts index d6c10fb11a7a..1806b8dc0866 100644 --- a/lib/routes/amazfitwatchfaces/index.ts +++ b/lib/routes/amazfitwatchfaces/index.ts @@ -272,37 +272,36 @@ export const route: Route = { description: `::: tip If you subscribe to [Updated watch faces for Amazfit X](https://amazfitwatchfaces.com/amazfit-x/updated),where the URL is \`https://amazfitwatchfaces.com/amazfit-x/updated\`, extract the part \`https://amazfitwatchfaces.com/\` to the end, which is \`amazfit-x/updated\`, and use it as the parameter to fill in. Therefore, the route will be [\`/amazfitwatchfaces/amazfit-x/updated\`](https://rsshub.app/amazfitwatchfaces/amazfit-x/updated). -If you subscribe to [TOP for the last 6 months (Only new) - Xiaomi Smart Band 9](https://amazfitwatchfaces.com/mi-band/top?compatible=Smart_Band_9&topof=6months),where the URL is \`https://amazfitwatchfaces.com/mi-band/top?compatible=Smart_Band_9&topof=6months\`, extract the part \`https://amazfitwatchfaces.com/\` to the end, which is \`mi-band/top\`, and use it as the parameter to fill in. Therefore, the route will be [\`/amazfitwatchfaces/mi-band/top/compatible=Smart_Band_9&topof=6months\`](https://rsshub.app/amazfitwatchfaces/mi-band/top/compatible=Smart_Band_9&topof=6months). +If you subscribe to [TOP for the last 6 months (Only new) - Xiaomi Smart Band 9](https://amazfitwatchfaces.com/mi-band/top?compatible=Smart_Band_9\\&topof=6months),where the URL is \`https://amazfitwatchfaces.com/mi-band/top?compatible=Smart_Band_9&topof=6months\`, extract the part \`https://amazfitwatchfaces.com/\` to the end, which is \`mi-band/top\`, and use it as the parameter to fill in. Therefore, the route will be [\`/amazfitwatchfaces/mi-band/top/compatible=Smart_Band_9&topof=6months\`](https://rsshub.app/amazfitwatchfaces/mi-band/top/compatible=Smart_Band_9\\&topof=6months). :::
More devices -| Device Name | Device Id | -| ------------------------------------------------------------------------------------------ | --------------- | -| [Amazfit X](https://amazfitwatchfaces.com/amazfit-x/fresh) | [amazfit-x](https://rsshub.app/amazfitwatchfaces/amazfit-x/fresh) | -| [Amazfit Band](https://amazfitwatchfaces.com/amazfit-band/fresh) | [amazfit-band](https://rsshub.app/amazfitwatchfaces/amazfit-band/fresh) | -| [Amazfit Bip](https://amazfitwatchfaces.com/bip/fresh) | [bip](https://rsshub.app/amazfitwatchfaces/bip/fresh) | -| [Amazfit Active](https://amazfitwatchfaces.com/active/fresh) | [active](https://rsshub.app/amazfitwatchfaces/active/fresh) | -| [Amazfit Balance](https://amazfitwatchfaces.com/balance/fresh) | [balance](https://rsshub.app/amazfitwatchfaces/balance/fresh) | -| [Amazfit Cheetah](https://amazfitwatchfaces.com/cheetah/fresh) | [cheetah](https://rsshub.app/amazfitwatchfaces/cheetah/fresh) | -| [Amazfit Falcon](https://amazfitwatchfaces.com/falcon/fresh) | [falcon](https://rsshub.app/amazfitwatchfaces/falcon/fresh) | -| [Amazfit GTR](https://amazfitwatchfaces.com/gtr/fresh) | [gtr](https://rsshub.app/amazfitwatchfaces/gtr/fresh) | -| [Amazfit GTS](https://amazfitwatchfaces.com/gts/fresh) | [gts](https://rsshub.app/amazfitwatchfaces/gts/fresh) | -| [Amazfit T-Rex](https://amazfitwatchfaces.com/t-rex/fresh) | [t-rex](https://rsshub.app/amazfitwatchfaces/t-rex/fresh) | -| [Amazfit Stratos](https://amazfitwatchfaces.com/pace/fresh) | [pace](https://rsshub.app/amazfitwatchfaces/pace/fresh) | -| [Amazfit Verge Lite](https://amazfitwatchfaces.com/verge-lite/fresh) | [verge-lite](https://rsshub.app/amazfitwatchfaces/verge-lite/fresh) | -| [Haylou Watches](https://amazfitwatchfaces.com/haylou/fresh) | [haylou](https://rsshub.app/amazfitwatchfaces/haylou/fresh) | -| [Huawei Watches](https://amazfitwatchfaces.com/huawei-watch-gt/fresh) | [huawei-watch-gt](https://rsshub.app/amazfitwatchfaces/huawei-watch-gt/fresh) | -| [Xiaomi Mi Band 4](https://amazfitwatchfaces.com/mi-band-4/fresh) | [mi-band-4](https://rsshub.app/amazfitwatchfaces/mi-band-4/fresh) | -| [Xiaomi Mi Band 5](https://amazfitwatchfaces.com/mi-band-5/fresh) | [mi-band-5](https://rsshub.app/amazfitwatchfaces/mi-band-5/fresh) | -| [Xiaomi Mi Band 6](https://amazfitwatchfaces.com/mi-band-6/fresh) | [mi-band-6](https://rsshub.app/amazfitwatchfaces/mi-band-6/fresh) | -| [Xiaomi Mi Band 7](https://amazfitwatchfaces.com/mi-band-7/fresh) | [mi-band-7](https://rsshub.app/amazfitwatchfaces/mi-band-7/fresh) | +| Device Name | Device Id | +| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | +| [Amazfit X](https://amazfitwatchfaces.com/amazfit-x/fresh) | [amazfit-x](https://rsshub.app/amazfitwatchfaces/amazfit-x/fresh) | +| [Amazfit Band](https://amazfitwatchfaces.com/amazfit-band/fresh) | [amazfit-band](https://rsshub.app/amazfitwatchfaces/amazfit-band/fresh) | +| [Amazfit Bip](https://amazfitwatchfaces.com/bip/fresh) | [bip](https://rsshub.app/amazfitwatchfaces/bip/fresh) | +| [Amazfit Active](https://amazfitwatchfaces.com/active/fresh) | [active](https://rsshub.app/amazfitwatchfaces/active/fresh) | +| [Amazfit Balance](https://amazfitwatchfaces.com/balance/fresh) | [balance](https://rsshub.app/amazfitwatchfaces/balance/fresh) | +| [Amazfit Cheetah](https://amazfitwatchfaces.com/cheetah/fresh) | [cheetah](https://rsshub.app/amazfitwatchfaces/cheetah/fresh) | +| [Amazfit Falcon](https://amazfitwatchfaces.com/falcon/fresh) | [falcon](https://rsshub.app/amazfitwatchfaces/falcon/fresh) | +| [Amazfit GTR](https://amazfitwatchfaces.com/gtr/fresh) | [gtr](https://rsshub.app/amazfitwatchfaces/gtr/fresh) | +| [Amazfit GTS](https://amazfitwatchfaces.com/gts/fresh) | [gts](https://rsshub.app/amazfitwatchfaces/gts/fresh) | +| [Amazfit T-Rex](https://amazfitwatchfaces.com/t-rex/fresh) | [t-rex](https://rsshub.app/amazfitwatchfaces/t-rex/fresh) | +| [Amazfit Stratos](https://amazfitwatchfaces.com/pace/fresh) | [pace](https://rsshub.app/amazfitwatchfaces/pace/fresh) | +| [Amazfit Verge Lite](https://amazfitwatchfaces.com/verge-lite/fresh) | [verge-lite](https://rsshub.app/amazfitwatchfaces/verge-lite/fresh) | +| [Haylou Watches](https://amazfitwatchfaces.com/haylou/fresh) | [haylou](https://rsshub.app/amazfitwatchfaces/haylou/fresh) | +| [Huawei Watches](https://amazfitwatchfaces.com/huawei-watch-gt/fresh) | [huawei-watch-gt](https://rsshub.app/amazfitwatchfaces/huawei-watch-gt/fresh) | +| [Xiaomi Mi Band 4](https://amazfitwatchfaces.com/mi-band-4/fresh) | [mi-band-4](https://rsshub.app/amazfitwatchfaces/mi-band-4/fresh) | +| [Xiaomi Mi Band 5](https://amazfitwatchfaces.com/mi-band-5/fresh) | [mi-band-5](https://rsshub.app/amazfitwatchfaces/mi-band-5/fresh) | +| [Xiaomi Mi Band 6](https://amazfitwatchfaces.com/mi-band-6/fresh) | [mi-band-6](https://rsshub.app/amazfitwatchfaces/mi-band-6/fresh) | +| [Xiaomi Mi Band 7](https://amazfitwatchfaces.com/mi-band-7/fresh) | [mi-band-7](https://rsshub.app/amazfitwatchfaces/mi-band-7/fresh) | | [Xiaomi Smart Band 8](https://amazfitwatchfaces.com/mi-band/fresh?compatible=Smart_Band_8) | [mi-band](https://rsshub.app/amazfitwatchfaces/mi-band/fresh/compatible=Smart_Band_8) | | [Xiaomi Smart Band 9](https://amazfitwatchfaces.com/mi-band/fresh?compatible=Smart_Band_9) | [mi-band](https://rsshub.app/amazfitwatchfaces/mi-band/fresh/compatible=Smart_Band_9) | -
-`, +`, categories: ['program-update'], features: { requireConfig: false, diff --git a/lib/routes/android/pixel-update-bulletin.ts b/lib/routes/android/pixel-update-bulletin.ts new file mode 100644 index 000000000000..ce1e3bfce376 --- /dev/null +++ b/lib/routes/android/pixel-update-bulletin.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'; + +export const route: Route = { + path: '/pixel-update-bulletin', + categories: ['program-update'], + example: '/android/pixel-update-bulletin', + radar: [ + { + source: ['source.android.com/docs/security/bulletin/pixel', 'source.android.com'], + }, + ], + name: 'Pixel Update Bulletins', + maintainers: ['TonyRL'], + handler, + url: 'source.android.com/docs/security/bulletin/pixel', +}; + +async function handler() { + const baseUrl = 'https://source.android.com'; + const link = `${baseUrl}/docs/security/bulletin/pixel`; + + const response = await ofetch(link, { + headers: { + Cookie: 'signin=autosignin; cookies_accepted=true; django_language=en;', + }, + }); + + const $ = load(response); + + const list = $('table tr:has(td:first-child a)') + .toArray() + .map((item) => { + const $item = $(item); + const a = $item.find('td:nth-child(1) a'); + return { + title: `Pixel Update Bulletin ${a.text()}`, + description: $item.find('td:nth-child(2)').html()?.trim(), + link: new URL(a.attr('href')!, baseUrl).href, + pubDate: parseDate($item.find('td:nth-child(3)').text()), + }; + }); + + return { + title: $('head title').text(), + link, + image: $('link[rel="apple-touch-icon"]').attr('href'), + item: list, + }; +} diff --git a/lib/routes/annualreviews/index.ts b/lib/routes/annualreviews/index.ts index 6329050781af..1abce26b925f 100644 --- a/lib/routes/annualreviews/index.ts +++ b/lib/routes/annualreviews/index.ts @@ -29,7 +29,7 @@ export const route: Route = { description: `The URL of the journal [Annual Review of Analytical Chemistry](https://www.annualreviews.org/journal/anchem) is \`https://www.annualreviews.org/journal/anchem\`, where \`anchem\` is the id of the journal, so the route for this journal is \`/annualreviews/anchem\`. ::: tip - More jounals can be found in [Browse Journals](https://www.annualreviews.org/action/showPublications). +More jounals can be found in [Browse Journals](https://www.annualreviews.org/action/showPublications). :::`, }; @@ -37,7 +37,7 @@ async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.annualreviews.org'; - const apiRootUrl = `https://api.crossref.org`; + const apiRootUrl = 'https://api.crossref.org'; const feedUrl = `${rootUrl}/r/${id}_rss`; const currentUrl = `${rootUrl}/toc/${id}/current`; diff --git a/lib/routes/anquanke/namespace.ts b/lib/routes/anquanke/namespace.ts index e9ad9674f516..41fc17f7ea8b 100644 --- a/lib/routes/anquanke/namespace.ts +++ b/lib/routes/anquanke/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: '安全客', url: 'anquanke.com', description: `::: tip -官方提供了混合的主页资讯 RSS: [https://api.anquanke.com/data/v1/rss](https://api.anquanke.com/data/v1/rss) +官方提供了混合的主页资讯 RSS: :::`, lang: 'zh-CN', }; diff --git a/lib/routes/apkpure/versions.ts b/lib/routes/apkpure/versions.ts index b9ec2216ba9e..911f05d2ae65 100644 --- a/lib/routes/apkpure/versions.ts +++ b/lib/routes/apkpure/versions.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/versions/:pkg/:region?', @@ -28,7 +28,7 @@ async function handler(ctx) { const baseUrl = 'https://apkpure.com'; const link = `${baseUrl}/${region}/${pkg}/versions`; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/apnews/sitemap.ts b/lib/routes/apnews/sitemap.ts index 1abf1fec0be8..48f68db257a2 100644 --- a/lib/routes/apnews/sitemap.ts +++ b/lib/routes/apnews/sitemap.ts @@ -61,7 +61,7 @@ async function handler(ctx) { .find(String.raw`news\:publication_date`) .text() ); - const lastmod = timezone(parseDate($(e).find(`lastmod`).text()), -4); + const lastmod = timezone(parseDate($(e).find('lastmod').text()), -4); const language = LANGUAGE_MAP.get( $(e) .find(String.raw`news\:language`) diff --git a/lib/routes/app-center/release.tsx b/lib/routes/app-center/release.tsx index 83441a32de56..65a235222a87 100644 --- a/lib/routes/app-center/release.tsx +++ b/lib/routes/app-center/release.tsx @@ -112,7 +112,7 @@ export const route: Route = { maintainers: ['Rongronggg9'], handler, description: `::: tip - The parameters can be extracted from the Release page URL: \`https://install.appcenter.ms/users/:user/apps/:app/distribution_groups/:distribution_group\` +The parameters can be extracted from the Release page URL: \`https://install.appcenter.ms/users/:user/apps/:app/distribution_groups/:distribution_group\` :::`, }; diff --git a/lib/routes/app-sales/index.ts b/lib/routes/app-sales/index.ts index 28d590e9a4c3..f0c772d3680d 100644 --- a/lib/routes/app-sales/index.ts +++ b/lib/routes/app-sales/index.ts @@ -142,26 +142,25 @@ To subscribe to [Highlights](https://www.app-sales.net/highlights/), where the s
More countries -| Currency | Country | ID | -| -------- | ------------- | --- | -| USD | United States | us | -| EUR | Austria | at | -| AUD | Australia | au | -| BRL | Brazil | br | -| CAD | Canada | ca | -| EUR | France | fr | -| EUR | Germany | de | -| INR | India | in | -| EUR | Italy | it | -| EUR | Netherlands | nl | -| PLN | Poland | pl | -| RUB | Russia | ru | -| EUR | Spain | es | -| SEK | Sweden | se | -| GBP | Great Britain | gb | +| Currency | Country | ID | +| -------- | ------------- | -- | +| USD | United States | us | +| EUR | Austria | at | +| AUD | Australia | au | +| BRL | Brazil | br | +| CAD | Canada | ca | +| EUR | France | fr | +| EUR | Germany | de | +| INR | India | in | +| EUR | Italy | it | +| EUR | Netherlands | nl | +| PLN | Poland | pl | +| RUB | Russia | ru | +| EUR | Spain | es | +| SEK | Sweden | se | +| GBP | Great Britain | gb | -
-`, +`, categories: ['program-update'], features: { requireConfig: false, diff --git a/lib/routes/app-sales/mostwanted.ts b/lib/routes/app-sales/mostwanted.ts index 31f16a1988b1..49995398da6f 100644 --- a/lib/routes/app-sales/mostwanted.ts +++ b/lib/routes/app-sales/mostwanted.ts @@ -132,34 +132,32 @@ export const route: Route = { ], }, }, - description: ` -| Last 24h | Last Week | All Time | + description: `| Last 24h | Last Week | All Time | | -------- | --------- | -------- | | 24h | week | alltime |
More countries -| Currency | Country | ID | -| -------- | ------------- | --- | -| USD | United States | us | -| EUR | Austria | at | -| AUD | Australia | au | -| BRL | Brazil | br | -| CAD | Canada | ca | -| EUR | France | fr | -| EUR | Germany | de | -| INR | India | in | -| EUR | Italy | it | -| EUR | Netherlands | nl | -| PLN | Poland | pl | -| RUB | Russia | ru | -| EUR | Spain | es | -| SEK | Sweden | se | -| GBP | Great Britain | gb | +| Currency | Country | ID | +| -------- | ------------- | -- | +| USD | United States | us | +| EUR | Austria | at | +| AUD | Australia | au | +| BRL | Brazil | br | +| CAD | Canada | ca | +| EUR | France | fr | +| EUR | Germany | de | +| INR | India | in | +| EUR | Italy | it | +| EUR | Netherlands | nl | +| PLN | Poland | pl | +| RUB | Russia | ru | +| EUR | Spain | es | +| SEK | Sweden | se | +| GBP | Great Britain | gb | -
-`, +`, categories: ['program-update'], features: { requireConfig: false, diff --git a/lib/routes/apple/apps.ts b/lib/routes/apple/apps.ts index c9fd61023c3c..402e4e8c6967 100644 --- a/lib/routes/apple/apps.ts +++ b/lib/routes/apple/apps.ts @@ -3,8 +3,6 @@ import { ViewType } from '@/types'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { appstoreBearerToken } from './utils'; - const platformIds = { osx: 'macOS', ios: 'iOS', @@ -64,9 +62,8 @@ export const route: Route = { name: 'App Update', maintainers: ['EkkoG', 'nczitzk'], handler, - description: ` -::: tip - For example, the URL of [GarageBand](https://apps.apple.com/us/app/garageband/id408709785) in the App Store is \`https://apps.apple.com/us/app/garageband/id408709785\`. In this case, the \`App Store Country\` parameter for the route is \`us\`, and the \`App id\` parameter is \`id408709785\`. So the route should be [\`/apple/apps/update/us/id408709785\`](https://rsshub.app/apple/apps/update/us/id408709785). + description: `::: tip +For example, the URL of [GarageBand](https://apps.apple.com/us/app/garageband/id408709785) in the App Store is \`https://apps.apple.com/us/app/garageband/id408709785\`. In this case, the \`App Store Country\` parameter for the route is \`us\`, and the \`App id\` parameter is \`id408709785\`. So the route should be [\`/apple/apps/update/us/id408709785\`](https://rsshub.app/apple/apps/update/us/id408709785). :::`, }; @@ -87,20 +84,20 @@ async function handler(ctx) { const rootUrl = 'https://apps.apple.com'; const currentUrl = new URL(`${country}/app/${id}`, rootUrl).href; - const bearer = await appstoreBearerToken(); - - const response = await ofetch(`https://amp-api-edge.apps.apple.com/v1/catalog/${country}/apps/${id.replace('id', '')}`, { + const response = await ofetch(`https://apps.apple.com/api/apps/v1/catalog/${country}/apps/${id.replace('id', '')}`, { headers: { - authorization: `Bearer ${bearer}`, + authorization: 'Bearer', origin: 'https://apps.apple.com', }, query: { platform: 'iphone', additionalPlatforms: 'appletv,ipad,iphone,mac,realityDevice,watch', - extend: 'accessibility,accessibilityDetails,ageRating,backgroundAssetsInfo,backgroundAssetsInfoWithOptional,customArtwork,customDeepLink,customIconArtwork,customPromotionalText,customScreenshotsByType,customVideoPreviewsByType,description,expectedReleaseDateDisplayFormat,fileSizeByDevice,gameDisplayName,iconArtwork,installSizeByDeviceInBytes,messagesScreenshots,miniGamesDeepLink,minimumOSVersion,privacy,privacyDetails,privacyPolicyUrl,remoteControllerRequirement,requirementsByDeviceFamily,supportURLForLanguage,supportedGameCenterFeatures,supportsFunCamera,supportsSharePlay,versionHistory,websiteUrl', + extend: 'accessibility,accessibilityDetails,ageRating,backgroundAssetsInfo,backgroundAssetsInfoWithOptional,customArtwork,customDeepLink,customIconArtwork,customPromotionalText,customScreenshotsByType,customVideoPreviewsByType,description,expectedReleaseDateDisplayFormat,fileSizeByDevice,gameDisplayName,iconArtwork,installSizeByDeviceInBytes,macRequiredCapabilities,medicalDeviceInfo,messagesScreenshots,miniGamesDeepLink,minimumOSVersion,privacy,privacyDetails,privacyPolicyUrl,remoteControllerRequirement,requirementsByDeviceFamily,supportURLForLanguage,supportedGameCenterFeatures,supportsFunCamera,supportsSharePlay,versionHistory,websiteUrl', + 'extend[apps]': 'distributionKind,isVerifiedForAppleSiliconMac', 'extend[app-events]': 'description,productArtwork,productVideo', - include: 'alternate-apps,app-bundles,customers-also-bought-apps,developer,developer-other-apps,merchandised-in-apps,related-editorial-items,reviews,top-in-apps', + include: 'alternate-apps,app-bundles,customers-also-bought-apps,developer,developer-other-apps,merchandised-in-apps,related-editorial-items,reviews', 'include[apps]': 'app-events', + views: 'top-in-app-purchasables', 'availableIn[app-events]': 'future', 'sparseLimit[apps:customers-also-bought-apps]': 40, 'sparseLimit[apps:developer-other-apps]': 40, diff --git a/lib/routes/apple/podcast.ts b/lib/routes/apple/podcast.ts index ecca6a797c55..ffaaaeec0ef5 100644 --- a/lib/routes/apple/podcast.ts +++ b/lib/routes/apple/podcast.ts @@ -37,7 +37,7 @@ async function handler(ctx) { const { id, region } = ctx.req.param(); const numericId = id.match(/id(\d+)/)?.[1]; const baseUrl = 'https://podcasts.apple.com'; - const link = `${baseUrl}/${region || `cn`}/podcast/${id}`; + const link = `${baseUrl}/${region || 'cn'}/podcast/${id}`; const response = await ofetch(link); diff --git a/lib/routes/apple/security-releases.ts b/lib/routes/apple/security-releases.ts index ff1a9d3f7dc0..9896caaa6fcf 100644 --- a/lib/routes/apple/security-releases.ts +++ b/lib/routes/apple/security-releases.ts @@ -134,8 +134,7 @@ export const route: Route = { }, description: `::: tip To subscribe to [Apple security releases](https://support.apple.com/en-us/100100), where the source URL is \`https://support.apple.com/en-us/100100\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/apple/security-releases/en-us\`](https://rsshub.app/apple/security-releases/en-us). -::: -`, +:::`, categories: ['program-update'], features: { requireConfig: false, @@ -172,7 +171,6 @@ To subscribe to [Apple security releases](https://support.apple.com/en-us/100100 }, description: `::: tip 若订阅 [Apple 安全性发布](https://support.apple.com/zh-cn/100100),网址为 \`https://support.apple.com/zh-cn/100100\`,请截取 \`https://support.apple.com/\` 到末尾 \`/100100\` 的部分 \`zh-cn\` 作为 \`language\` 参数填入,此时目标路由为 [\`/apple/security-releases/zh-cn\`](https://rsshub.app/apple/security-releases/zh-cn)。 -::: -`, +:::`, }, }; diff --git a/lib/routes/apple/utils.ts b/lib/routes/apple/utils.ts deleted file mode 100644 index 04cbf4b86776..000000000000 --- a/lib/routes/apple/utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { load } from 'cheerio'; - -import { config } from '@/config'; -import cache from '@/utils/cache'; -import ofetch from '@/utils/ofetch'; - -// App Store and Podcast use different bearer tokens -export const appstoreBearerToken = () => - cache.tryGet( - 'apple:podcast:bearer', - async () => { - const baseUrl = 'https://apps.apple.com'; - const response = await ofetch(`${baseUrl}/us/iphone/today`); - const $ = load(response); - - const moduleAddress = new URL($('head script[type="module"]').attr('src'), baseUrl).href; - const modulesResponse = await ofetch(moduleAddress, { - parseResponse: (txt) => txt, - }); - const bearerToken = modulesResponse.match(/="(eyJhbGci.*?)"/)[1]; - - return bearerToken as string; - }, - config.cache.contentExpire, - false - ); diff --git a/lib/routes/appstore/in-app-purchase.ts b/lib/routes/appstore/in-app-purchase.ts index 94688010f0c4..6e1ef782539f 100644 --- a/lib/routes/appstore/in-app-purchase.ts +++ b/lib/routes/appstore/in-app-purchase.ts @@ -1,6 +1,5 @@ import { load } from 'cheerio'; -import { appstoreBearerToken } from '@/routes/apple/utils'; import type { Route } from '@/types'; import ofetch from '@/utils/ofetch'; @@ -33,11 +32,10 @@ async function handler(ctx) { const res = await ofetch(link); const $ = load(res); const lang = $('html').attr('lang'); - const mediaToken = await appstoreBearerToken(); - const apiResponse = await ofetch(`https://amp-api-edge.apps.apple.com/v1/catalog/${country}/apps/${id.replace('id', '')}`, { + const apiResponse = await ofetch(`https://apps.apple.com/api/apps/v1/catalog/${country}/apps/${id.replace('id', '')}`, { headers: { - authorization: `Bearer ${mediaToken}`, + authorization: 'Bearer', origin: 'https://apps.apple.com', }, query: { diff --git a/lib/routes/arcteryx/new-arrivals.ts b/lib/routes/arcteryx/new-arrivals.ts index 5b51ae16b8c4..18ff9acdfca0 100644 --- a/lib/routes/arcteryx/new-arrivals.ts +++ b/lib/routes/arcteryx/new-arrivals.ts @@ -31,14 +31,14 @@ export const route: Route = { | ------------- | ------ | -------------- | | us | ca | gb | - gender +gender | male | female | | ---- | ------ | | mens | womens | ::: tip - Parameter \`country\` can be found within the url of \`Arcteryx\` website. +Parameter \`country\` can be found within the url of \`Arcteryx\` website. :::`, }; diff --git a/lib/routes/arcteryx/outlet.ts b/lib/routes/arcteryx/outlet.ts index 6341669745b9..86f011f411c1 100644 --- a/lib/routes/arcteryx/outlet.ts +++ b/lib/routes/arcteryx/outlet.ts @@ -31,14 +31,14 @@ export const route: Route = { | ------------- | ------ | -------------- | | us | ca | gb | - gender +gender | male | female | | ---- | ------ | | mens | womens | ::: tip - Parameter \`country\` can be found within the url of \`Arcteryx\` website. +Parameter \`country\` can be found within the url of \`Arcteryx\` website. :::`, }; diff --git a/lib/routes/asiafruitchina/categories.ts b/lib/routes/asiafruitchina/categories.ts index 157570857cc5..2d73c4eaeb6c 100644 --- a/lib/routes/asiafruitchina/categories.ts +++ b/lib/routes/asiafruitchina/categories.ts @@ -370,48 +370,47 @@ export const route: Route = {
更多分类 - | [全部](https://asiafruitchina.net/categories?gspx=all) | [橙](https://asiafruitchina.net/categories?gspx=chengzi) | [百香果](https://asiafruitchina.net/categories?gspx=baixiangguo) | [菠萝/凤梨](https://asiafruitchina.net/categories?gspx=boluo) | [菠萝蜜](https://asiafruitchina.net/categories?gspx=boluomi) | - | ------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------- | - | [all](https://rsshub.app/asiafruitchina/categories/all) | [chengzi](https://rsshub.app/asiafruitchina/categories/chengzi) | [baixiangguo](https://rsshub.app/asiafruitchina/categories/baixiangguo) | [boluo](https://rsshub.app/asiafruitchina/categories/boluo) | [boluomi](https://rsshub.app/asiafruitchina/categories/boluomi) | +| [全部](https://asiafruitchina.net/categories?gspx=all) | [橙](https://asiafruitchina.net/categories?gspx=chengzi) | [百香果](https://asiafruitchina.net/categories?gspx=baixiangguo) | [菠萝 / 凤梨](https://asiafruitchina.net/categories?gspx=boluo) | [菠萝蜜](https://asiafruitchina.net/categories?gspx=boluomi) | +| ------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | +| [all](https://rsshub.app/asiafruitchina/categories/all) | [chengzi](https://rsshub.app/asiafruitchina/categories/chengzi) | [baixiangguo](https://rsshub.app/asiafruitchina/categories/baixiangguo) | [boluo](https://rsshub.app/asiafruitchina/categories/boluo) | [boluomi](https://rsshub.app/asiafruitchina/categories/boluomi) | - | [草莓](https://asiafruitchina.net/categories?gspx=caomei) | [番荔枝/释迦](https://asiafruitchina.net/categories?gspx=fanlizhi) | [番茄](https://asiafruitchina.net/categories?gspx=fanqie) | [柑橘](https://asiafruitchina.net/categories?gspx=ganju) | [哈密瓜](https://asiafruitchina.net/categories?gspx=hamigua) | - | ------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------- | - | [caomei](https://rsshub.app/asiafruitchina/categories/caomei) | [fanlizhi](https://rsshub.app/asiafruitchina/categories/fanlizhi) | [fanqie](https://rsshub.app/asiafruitchina/categories/fanqie) | [ganju](https://rsshub.app/asiafruitchina/categories/ganju) | [hamigua](https://rsshub.app/asiafruitchina/categories/hamigua) | +| [草莓](https://asiafruitchina.net/categories?gspx=caomei) | [番荔枝 / 释迦](https://asiafruitchina.net/categories?gspx=fanlizhi) | [番茄](https://asiafruitchina.net/categories?gspx=fanqie) | [柑橘](https://asiafruitchina.net/categories?gspx=ganju) | [哈密瓜](https://asiafruitchina.net/categories?gspx=hamigua) | +| ------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------- | +| [caomei](https://rsshub.app/asiafruitchina/categories/caomei) | [fanlizhi](https://rsshub.app/asiafruitchina/categories/fanlizhi) | [fanqie](https://rsshub.app/asiafruitchina/categories/fanqie) | [ganju](https://rsshub.app/asiafruitchina/categories/ganju) | [hamigua](https://rsshub.app/asiafruitchina/categories/hamigua) | - | [核果](https://asiafruitchina.net/categories?gspx=heguo) | [红毛丹](https://asiafruitchina.net/categories?gspx=hongmaodan) | [火龙果](https://asiafruitchina.net/categories?gspx=huolongguo) | [浆果](https://asiafruitchina.net/categories?gspx=jiangguo) | [桔子](https://asiafruitchina.net/categories?gspx=juzi) | - | ----------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------- | - | [heguo](https://rsshub.app/asiafruitchina/categories/heguo) | [hongmaodan](https://rsshub.app/asiafruitchina/categories/hongmaodan) | [huolongguo](https://rsshub.app/asiafruitchina/categories/huolongguo) | [jiangguo](https://rsshub.app/asiafruitchina/categories/jiangguo) | [juzi](https://rsshub.app/asiafruitchina/categories/juzi) | +| [核果](https://asiafruitchina.net/categories?gspx=heguo) | [红毛丹](https://asiafruitchina.net/categories?gspx=hongmaodan) | [火龙果](https://asiafruitchina.net/categories?gspx=huolongguo) | [浆果](https://asiafruitchina.net/categories?gspx=jiangguo) | [桔子](https://asiafruitchina.net/categories?gspx=juzi) | +| ----------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------- | +| [heguo](https://rsshub.app/asiafruitchina/categories/heguo) | [hongmaodan](https://rsshub.app/asiafruitchina/categories/hongmaodan) | [huolongguo](https://rsshub.app/asiafruitchina/categories/huolongguo) | [jiangguo](https://rsshub.app/asiafruitchina/categories/jiangguo) | [juzi](https://rsshub.app/asiafruitchina/categories/juzi) | - | [蓝莓](https://asiafruitchina.net/categories?gspx=lanmei) | [梨](https://asiafruitchina.net/categories?gspx=li) | [荔枝](https://asiafruitchina.net/categories?gspx=lizhi) | [李子](https://asiafruitchina.net/categories?gspx=lizi) | [榴莲](https://asiafruitchina.net/categories?gspx=liulian) | - | ------------------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------- | - | [lanmei](https://rsshub.app/asiafruitchina/categories/lanmei) | [li](https://rsshub.app/asiafruitchina/categories/li) | [lizhi](https://rsshub.app/asiafruitchina/categories/lizhi) | [lizi](https://rsshub.app/asiafruitchina/categories/lizi) | [liulian](https://rsshub.app/asiafruitchina/categories/liulian) | +| [蓝莓](https://asiafruitchina.net/categories?gspx=lanmei) | [梨](https://asiafruitchina.net/categories?gspx=li) | [荔枝](https://asiafruitchina.net/categories?gspx=lizhi) | [李子](https://asiafruitchina.net/categories?gspx=lizi) | [榴莲](https://asiafruitchina.net/categories?gspx=liulian) | +| ------------------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------- | +| [lanmei](https://rsshub.app/asiafruitchina/categories/lanmei) | [li](https://rsshub.app/asiafruitchina/categories/li) | [lizhi](https://rsshub.app/asiafruitchina/categories/lizhi) | [lizi](https://rsshub.app/asiafruitchina/categories/lizi) | [liulian](https://rsshub.app/asiafruitchina/categories/liulian) | - | [龙眼](https://asiafruitchina.net/categories?gspx=lognyan) | [芦笋](https://asiafruitchina.net/categories?gspx=lusun) | [蔓越莓](https://asiafruitchina.net/categories?gspx=manyuemei) | [芒果](https://asiafruitchina.net/categories?gspx=mangguo) | [猕猴桃/奇异果](https://asiafruitchina.net/categories?gspx=mihoutao) | - | --------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------- | - | [lognyan](https://rsshub.app/asiafruitchina/categories/lognyan) | [lusun](https://rsshub.app/asiafruitchina/categories/lusun) | [manyuemei](https://rsshub.app/asiafruitchina/categories/manyuemei) | [mangguo](https://rsshub.app/asiafruitchina/categories/mangguo) | [mihoutao](https://rsshub.app/asiafruitchina/categories/mihoutao) | +| [龙眼](https://asiafruitchina.net/categories?gspx=lognyan) | [芦笋](https://asiafruitchina.net/categories?gspx=lusun) | [蔓越莓](https://asiafruitchina.net/categories?gspx=manyuemei) | [芒果](https://asiafruitchina.net/categories?gspx=mangguo) | [猕猴桃 / 奇异果](https://asiafruitchina.net/categories?gspx=mihoutao) | +| --------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- | +| [lognyan](https://rsshub.app/asiafruitchina/categories/lognyan) | [lusun](https://rsshub.app/asiafruitchina/categories/lusun) | [manyuemei](https://rsshub.app/asiafruitchina/categories/manyuemei) | [mangguo](https://rsshub.app/asiafruitchina/categories/mangguo) | [mihoutao](https://rsshub.app/asiafruitchina/categories/mihoutao) | - | [柠檬](https://asiafruitchina.net/categories?gspx=ningmeng) | [牛油果](https://asiafruitchina.net/categories?gspx=niuyouguo) | [苹果](https://asiafruitchina.net/categories?gspx=pingguo) | [葡萄/提子](https://asiafruitchina.net/categories?gspx=putao) | [其他](https://asiafruitchina.net/categories?gspx=qita) | - | ----------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------- | - | [ningmeng](https://rsshub.app/asiafruitchina/categories/ningmeng) | [niuyouguo](https://rsshub.app/asiafruitchina/categories/niuyouguo) | [pingguo](https://rsshub.app/asiafruitchina/categories/pingguo) | [putao](https://rsshub.app/asiafruitchina/categories/putao) | [qita](https://rsshub.app/asiafruitchina/categories/qita) | +| [柠檬](https://asiafruitchina.net/categories?gspx=ningmeng) | [牛油果](https://asiafruitchina.net/categories?gspx=niuyouguo) | [苹果](https://asiafruitchina.net/categories?gspx=pingguo) | [葡萄 / 提子](https://asiafruitchina.net/categories?gspx=putao) | [其他](https://asiafruitchina.net/categories?gspx=qita) | +| ----------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------- | +| [ningmeng](https://rsshub.app/asiafruitchina/categories/ningmeng) | [niuyouguo](https://rsshub.app/asiafruitchina/categories/niuyouguo) | [pingguo](https://rsshub.app/asiafruitchina/categories/pingguo) | [putao](https://rsshub.app/asiafruitchina/categories/putao) | [qita](https://rsshub.app/asiafruitchina/categories/qita) | - | [奇异莓](https://asiafruitchina.net/categories?gspx=qiyimei) | [热带水果](https://asiafruitchina.net/categories?gspx=redaishuiguo) | [山竹](https://asiafruitchina.net/categories?gspx=shanzhu) | [石榴](https://asiafruitchina.net/categories?gspx=shiliu) | [蔬菜](https://asiafruitchina.net/categories?gspx=shucai) | - | --------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | - | [qiyimei](https://rsshub.app/asiafruitchina/categories/qiyimei) | [redaishuiguo](https://rsshub.app/asiafruitchina/categories/redaishuiguo) | [shanzhu](https://rsshub.app/asiafruitchina/categories/shanzhu) | [shiliu](https://rsshub.app/asiafruitchina/categories/shiliu) | [shucai](https://rsshub.app/asiafruitchina/categories/shucai) | +| [奇异莓](https://asiafruitchina.net/categories?gspx=qiyimei) | [热带水果](https://asiafruitchina.net/categories?gspx=redaishuiguo) | [山竹](https://asiafruitchina.net/categories?gspx=shanzhu) | [石榴](https://asiafruitchina.net/categories?gspx=shiliu) | [蔬菜](https://asiafruitchina.net/categories?gspx=shucai) | +| --------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | +| [qiyimei](https://rsshub.app/asiafruitchina/categories/qiyimei) | [redaishuiguo](https://rsshub.app/asiafruitchina/categories/redaishuiguo) | [shanzhu](https://rsshub.app/asiafruitchina/categories/shanzhu) | [shiliu](https://rsshub.app/asiafruitchina/categories/shiliu) | [shucai](https://rsshub.app/asiafruitchina/categories/shucai) | - | [树莓](https://asiafruitchina.net/categories?gspx=shumei) | [桃](https://asiafruitchina.net/categories?gspx=tao) | [甜瓜](https://asiafruitchina.net/categories?gspx=tiangua) | [甜椒](https://asiafruitchina.net/categories?gspx=tianjiao) | [甜柿](https://asiafruitchina.net/categories?gspx=tianshi) | - | ------------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------- | - | [shumei](https://rsshub.app/asiafruitchina/categories/shumei) | [tao](https://rsshub.app/asiafruitchina/categories/tao) | [tiangua](https://rsshub.app/asiafruitchina/categories/tiangua) | [tianjiao](https://rsshub.app/asiafruitchina/categories/tianjiao) | [tianshi](https://rsshub.app/asiafruitchina/categories/tianshi) | +| [树莓](https://asiafruitchina.net/categories?gspx=shumei) | [桃](https://asiafruitchina.net/categories?gspx=tao) | [甜瓜](https://asiafruitchina.net/categories?gspx=tiangua) | [甜椒](https://asiafruitchina.net/categories?gspx=tianjiao) | [甜柿](https://asiafruitchina.net/categories?gspx=tianshi) | +| ------------------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------- | +| [shumei](https://rsshub.app/asiafruitchina/categories/shumei) | [tao](https://rsshub.app/asiafruitchina/categories/tao) | [tiangua](https://rsshub.app/asiafruitchina/categories/tiangua) | [tianjiao](https://rsshub.app/asiafruitchina/categories/tianjiao) | [tianshi](https://rsshub.app/asiafruitchina/categories/tianshi) | - | [香蕉](https://asiafruitchina.net/categories?gspx=xiangjiao) | [西瓜](https://asiafruitchina.net/categories?gspx=xigua) | [西梅](https://asiafruitchina.net/categories?gspx=ximei) | [杏](https://asiafruitchina.net/categories?gspx=xing) | [椰子](https://asiafruitchina.net/categories?gspx=yezi) | - | ------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | - | [xiangjiao](https://rsshub.app/asiafruitchina/categories/xiangjiao) | [xigua](https://rsshub.app/asiafruitchina/categories/xigua) | [ximei](https://rsshub.app/asiafruitchina/categories/ximei) | [xing](https://rsshub.app/asiafruitchina/categories/xing) | [yezi](https://rsshub.app/asiafruitchina/categories/yezi) | +| [香蕉](https://asiafruitchina.net/categories?gspx=xiangjiao) | [西瓜](https://asiafruitchina.net/categories?gspx=xigua) | [西梅](https://asiafruitchina.net/categories?gspx=ximei) | [杏](https://asiafruitchina.net/categories?gspx=xing) | [椰子](https://asiafruitchina.net/categories?gspx=yezi) | +| ------------------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | +| [xiangjiao](https://rsshub.app/asiafruitchina/categories/xiangjiao) | [xigua](https://rsshub.app/asiafruitchina/categories/xigua) | [ximei](https://rsshub.app/asiafruitchina/categories/ximei) | [xing](https://rsshub.app/asiafruitchina/categories/xing) | [yezi](https://rsshub.app/asiafruitchina/categories/yezi) | - | [杨梅](https://asiafruitchina.net/categories?gspx=yangmei) | [樱桃](https://asiafruitchina.net/categories?gspx=yintao) | [油桃](https://asiafruitchina.net/categories?gspx=youtao) | [柚子](https://asiafruitchina.net/categories?gspx=youzi) | - | --------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | - | [yangmei](https://rsshub.app/asiafruitchina/categories/yangmei) | [yintao](https://rsshub.app/asiafruitchina/categories/yintao) | [youtao](https://rsshub.app/asiafruitchina/categories/youtao) | [youzi](https://rsshub.app/asiafruitchina/categories/youzi) | +| [杨梅](https://asiafruitchina.net/categories?gspx=yangmei) | [樱桃](https://asiafruitchina.net/categories?gspx=yintao) | [油桃](https://asiafruitchina.net/categories?gspx=youtao) | [柚子](https://asiafruitchina.net/categories?gspx=youzi) | +| --------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | +| [yangmei](https://rsshub.app/asiafruitchina/categories/yangmei) | [yintao](https://rsshub.app/asiafruitchina/categories/yintao) | [youtao](https://rsshub.app/asiafruitchina/categories/youtao) | [youzi](https://rsshub.app/asiafruitchina/categories/youzi) | -
-`, +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/asianfanfics/tag.ts b/lib/routes/asianfanfics/tag.ts index 60e7eb8f3538..5ea76d76bb55 100644 --- a/lib/routes/asianfanfics/tag.ts +++ b/lib/routes/asianfanfics/tag.ts @@ -23,13 +23,13 @@ export const route: Route = { target: '/tag/:tag/:type', }, ], - description: `匹配asianfanfics标签,支持排序类型: + description: `匹配 asianfanfics 标签,支持排序类型: + - L: Latest 最近更新 - N: Newest 最近发布 - O: Oldest 最早发布 - C: Completed 已完成 -- OS: One Shots 短篇 -`, +- OS: One Shots 短篇`, handler, }; diff --git a/lib/routes/asianfanfics/text-search.ts b/lib/routes/asianfanfics/text-search.ts index 35e915c1f475..8980d4823542 100644 --- a/lib/routes/asianfanfics/text-search.ts +++ b/lib/routes/asianfanfics/text-search.ts @@ -22,7 +22,7 @@ export const route: Route = { target: '/text-search/:keyword', }, ], - description: '匹配asianfanfics搜索关键词', + description: '匹配 asianfanfics 搜索关键词', handler, }; diff --git a/lib/routes/asmr-200/index.tsx b/lib/routes/asmr-200/index.tsx index ce33df4a6d87..7916bf48d02e 100644 --- a/lib/routes/asmr-200/index.tsx +++ b/lib/routes/asmr-200/index.tsx @@ -69,9 +69,9 @@ export const route: Route = { name: '最新收录', maintainers: ['hualiong'], url: 'asmr-200.com', - description: `| 发售日期 | 收录日期 | 销量 | 价格 | 评价 | 随机 | RJ号 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | -| release | create_date | dl_count | price | rate_average_2dp | random | id |`, + description: `| 发售日期 | 收录日期 | 销量 | 价格 | 评价 | 随机 | RJ 号 | +| -------- | ------------ | --------- | ----- | ------------------ | ------ | ----- | +| release | create\\_date | dl\\_count | price | rate\\_average\\_2dp | random | id |`, handler: async (ctx) => { const { order = 'create_date', sort = 'desc', subtitle = '0' } = ctx.req.param(); const res = await ofetch('https://api.asmr-200.com/api/works', { query: { order, sort, page: 1, subtitle } }); diff --git a/lib/routes/atcoder/contest.ts b/lib/routes/atcoder/contest.ts index c6add824f88e..bba128343b55 100644 --- a/lib/routes/atcoder/contest.ts +++ b/lib/routes/atcoder/contest.ts @@ -23,11 +23,11 @@ export const route: Route = { handler, description: `Rated Range -| ABC Class (Rated for ~1999) | ARC Class (Rated for ~2799) | AGC Class (Rated for ~9999) | +| ABC Class (Rated for \\~1999) | ARC Class (Rated for \\~2799) | AGC Class (Rated for \\~9999) | | ---------------------------- | ---------------------------- | ---------------------------- | | 1 | 2 | 3 | - Category +Category | All | AtCoder Typical Contest | PAST Archive | Unofficial(unrated) | | --- | ----------------------- | ------------ | ------------------- | diff --git a/lib/routes/atptour/namespace.ts b/lib/routes/atptour/namespace.ts index 0916b893b623..0dae9d45f904 100644 --- a/lib/routes/atptour/namespace.ts +++ b/lib/routes/atptour/namespace.ts @@ -3,6 +3,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'ATP Tour', url: 'www.atptour.com', + categories: ['sport'], description: "News from the official site of men's professional tennis.", lang: 'en', }; diff --git a/lib/routes/atptour/news.ts b/lib/routes/atptour/news.ts index 0382bf2ecd15..062ebff64609 100644 --- a/lib/routes/atptour/news.ts +++ b/lib/routes/atptour/news.ts @@ -5,7 +5,7 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/news/:lang?', - categories: ['other'], + categories: ['sport'], example: '/atptour/news/en', parameters: { lang: 'en or es.' }, radar: [ diff --git a/lib/routes/baidu/tieba/forum.tsx b/lib/routes/baidu/tieba/forum.tsx index 8ba4ab80957b..9c001b7e5f96 100644 --- a/lib/routes/baidu/tieba/forum.tsx +++ b/lib/routes/baidu/tieba/forum.tsx @@ -34,7 +34,7 @@ async function handler(ctx) { 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`, { + const { data } = await got('https://tieba.baidu.com/f', { headers: { Referer: 'https://tieba.baidu.com/', }, diff --git a/lib/routes/baidu/tieba/search.tsx b/lib/routes/baidu/tieba/search.tsx index 3af8d03288c7..537559918462 100644 --- a/lib/routes/baidu/tieba/search.tsx +++ b/lib/routes/baidu/tieba/search.tsx @@ -27,11 +27,11 @@ export const route: Route = { description: `| 键 | 含义 | 接受的值 | 默认值 | | ------------ | ---------------------------------------------------------- | ------------- | ------ | | kw | 在名为 kw 的贴吧中搜索 | 任意名称 / 无 | 无 | -| only_thread | 只看主题帖,默认为 0 关闭 | 0/1 | 0 | +| only\\_thread | 只看主题帖,默认为 0 关闭 | 0/1 | 0 | | rn | 返回条目的数量 | 1-20 | 20 | | sm | 排序方式,0 为按时间顺序,1 为按时间倒序,2 为按相关性顺序 | 0/1/2 | 1 | - 用例:\`/baidu/tieba/search/neuro/kw=neurosama&only_thread=1&sm=2\``, +用例:\`/baidu/tieba/search/neuro/kw=neurosama&only_thread=1&sm=2\``, }; async function handler(ctx) { diff --git a/lib/routes/baidu/tieba/user.ts b/lib/routes/baidu/tieba/user.ts index a5a9288e45b8..d757866eb549 100644 --- a/lib/routes/baidu/tieba/user.ts +++ b/lib/routes/baidu/tieba/user.ts @@ -21,7 +21,7 @@ export const route: Route = { name: '用户帖子', maintainers: ['igxlin', 'nczitzk'], handler, - description: `用户 ID 可以通过打开用户的主页后查看地址栏的 \`un\` 字段来获取。`, + description: '用户 ID 可以通过打开用户的主页后查看地址栏的 `un` 字段来获取。', }; async function handler(ctx) { diff --git a/lib/routes/bandisoft/history.ts b/lib/routes/bandisoft/history.ts index 397c1ceec603..3eca78b4f13e 100644 --- a/lib/routes/bandisoft/history.ts +++ b/lib/routes/bandisoft/history.ts @@ -222,35 +222,34 @@ To subscribe to [Bandizip Version History](https://www.bandisoft.com/bandizip/hi
More languages -| Language | ID | -| -------------------- | --- | -| English | en | -| 中文(简体) | cn | -| 中文(繁體) | tw | -| 日本語 | jp | -| Русский | ru | -| Español | es | -| Français | fr | -| Deutsch | de | -| Italiano | it | -| Slovenčina | sk | -| Українська | uk | -| Беларуская | be | -| Dansk | da | -| Polski | pl | -| Português Brasileiro | br | -| Čeština | cs | -| Nederlands | nl | -| Slovenščina | sl | -| Türkçe | tr | -| ภาษาไทย | th | -| Ελληνικά | gr | -| Oʻzbek | uz | -| Romanian | ro | -| 한국어 | kr | +| Language | ID | +| -------------------- | -- | +| English | en | +| 中文 (简体) | cn | +| 中文 (繁體) | tw | +| 日本語 | jp | +| Русский | ru | +| Español | es | +| Français | fr | +| Deutsch | de | +| Italiano | it | +| Slovenčina | sk | +| Українська | uk | +| Беларуская | be | +| Dansk | da | +| Polski | pl | +| Português Brasileiro | br | +| Čeština | cs | +| Nederlands | nl | +| Slovenščina | sl | +| Türkçe | tr | +| ภาษาไทย | th | +| Ελληνικά | gr | +| Oʻzbek | uz | +| Romanian | ro | +| 한국어 | kr | -
-`, +`, categories: ['program-update'], features: { requireConfig: false, @@ -297,34 +296,33 @@ To subscribe to [Bandizip Version History](https://www.bandisoft.com/bandizip/hi
更多语言 -| Language | ID | -| -------------------- | --- | -| English | en | -| 中文(简体) | cn | -| 中文(繁體) | tw | -| 日本語 | jp | -| Русский | ru | -| Español | es | -| Français | fr | -| Deutsch | de | -| Italiano | it | -| Slovenčina | sk | -| Українська | uk | -| Беларуская | be | -| Dansk | da | -| Polski | pl | -| Português Brasileiro | br | -| Čeština | cs | -| Nederlands | nl | -| Slovenščina | sl | -| Türkçe | tr | -| ภาษาไทย | th | -| Ελληνικά | gr | -| Oʻzbek | uz | -| Romanian | ro | -| 한국어 | kr | +| Language | ID | +| -------------------- | -- | +| English | en | +| 中文 (简体) | cn | +| 中文 (繁體) | tw | +| 日本語 | jp | +| Русский | ru | +| Español | es | +| Français | fr | +| Deutsch | de | +| Italiano | it | +| Slovenčina | sk | +| Українська | uk | +| Беларуская | be | +| Dansk | da | +| Polski | pl | +| Português Brasileiro | br | +| Čeština | cs | +| Nederlands | nl | +| Slovenščina | sl | +| Türkçe | tr | +| ภาษาไทย | th | +| Ελληνικά | gr | +| Oʻzbek | uz | +| Romanian | ro | +| 한국어 | kr | -
-`, +`, }, }; diff --git a/lib/routes/bangumi.tv/subject/index.ts b/lib/routes/bangumi.tv/subject/index.ts index 88d30330400e..dbf9c7dd803e 100644 --- a/lib/routes/bangumi.tv/subject/index.ts +++ b/lib/routes/bangumi.tv/subject/index.ts @@ -29,7 +29,7 @@ export const route: Route = { maintainers: ['JimenezLi'], handler, description: `::: warning - 此通用路由仅用于对路由参数的描述,具体信息请查看下方与条目相关的路由 +此通用路由仅用于对路由参数的描述,具体信息请查看下方与条目相关的路由 :::`, }; diff --git a/lib/routes/banshujiang/index.ts b/lib/routes/banshujiang/index.ts index 3393ad2027ef..f55d9b175fbc 100644 --- a/lib/routes/banshujiang/index.ts +++ b/lib/routes/banshujiang/index.ts @@ -385,56 +385,56 @@ export const route: Route = { #### 编程语言 -| 分类 | ID | -| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| [ActionScript](http://www.banshujiang.cn/category/programming_language/ActionScript/page/1) | [category/programming_language/ActionScript](https://rsshub.app/banshujiang/programming_language/ActionScript) | -| [ASP.net](http://www.banshujiang.cn/category/programming_language/ASP.net/page/1) | [category/programming_language/ASP.net](https://rsshub.app/banshujiang/programming_language/ASP.net) | -| [C](http://www.banshujiang.cn/category/programming_language/C) | [category/programming_language/C](https://rsshub.app/banshujiang/programming_language/C) | -| [C#](http://www.banshujiang.cn/category/programming_language/C%23) | [category/programming_language/C%23](https://rsshub.app/banshujiang/programming_language/C%23) | -| [C++](http://www.banshujiang.cn/category/programming_language/C++) | [category/programming_language/C++](https://rsshub.app/banshujiang/programming_language/C++) | -| [CoffeeScript](http://www.banshujiang.cn/category/programming_language/CoffeeScript) | [category/programming_language/CoffeeScript](https://rsshub.app/banshujiang/programming_language/CoffeeScript) | -| [CSS](http://www.banshujiang.cn/category/programming_language/CSS) | [category/programming_language/CSS) | -| [Dart](http://www.banshujiang.cn/category/programming_language/Dart) | [category/programming_language/Dart](https://rsshub.app/banshujiang/programming_language/Dart) | -| [Elixir](http://www.banshujiang.cn/category/programming_language/Elixir) | [category/programming_language/Elixir](https://rsshub.app/banshujiang/programming_language/Elixir) | -| [Erlang](http://www.banshujiang.cn/category/programming_language/Erlang) | [category/programming_language/Erlang](https://rsshub.app/banshujiang/programming_language/Erlang) | -| [F#](http://www.banshujiang.cn/category/programming_language/F%23) | [category/programming_language/F%23](https://rsshub.app/banshujiang/programming_language/F%23) | -| [Go](http://www.banshujiang.cn/category/programming_language/Go) | [category/programming_language/Go](https://rsshub.app/banshujiang/programming_language/Go) | -| [Groovy](http://www.banshujiang.cn/category/programming_language/Groovy) | [category/programming_language/Groovy](https://rsshub.app/banshujiang/programming_language/Groovy) | -| [Haskell](http://www.banshujiang.cn/category/programming_language/Haskell) | [category/programming_language/Haskell](https://rsshub.app/banshujiang/programming_language/Haskell) | -| [HTML5](http://www.banshujiang.cn/category/programming_language/HTML5) | [category/programming_language/HTML5](https://rsshub.app/banshujiang/programming_language/HTML5) | -| [Java](http://www.banshujiang.cn/category/programming_language/Java) | [category/programming_language/Java](https://rsshub.app/banshujiang/programming_language/Java) | -| [JavaScript](http://www.banshujiang.cn/category/programming_language/JavaScript) | [category/programming_language/JavaScript](https://rsshub.app/banshujiang/programming_language/JavaScript) | -| [Kotlin](http://www.banshujiang.cn/category/programming_language/Kotlin) | [category/programming_language/Kotlin](https://rsshub.app/banshujiang/programming_language/Kotlin) | -| [Lua](http://www.banshujiang.cn/category/programming_language/Lua) | [category/programming_language/Lua](https://rsshub.app/banshujiang/programming_language/Lua) | -| [Objective-C](http://www.banshujiang.cn/category/programming_language/Objective-C) | [category/programming_language/Objective-C](https://rsshub.app/banshujiang/programming_language/Objective-C) | -| [Perl](http://www.banshujiang.cn/category/programming_language/Perl) | [category/programming_language/Perl](https://rsshub.app/banshujiang/programming_language/Perl) | -| [PHP](http://www.banshujiang.cn/category/programming_language/PHP) | [category/programming_language/PHP](https://rsshub.app/banshujiang/programming_language/PHP) | -| [PowerShell](http://www.banshujiang.cn/category/programming_language/PowerShell) | [category/programming_language/PowerShell](https://rsshub.app/banshujiang/programming_language/PowerShell) | -| [Python](http://www.banshujiang.cn/category/programming_language/Python) | [category/programming_language/Python](https://rsshub.app/banshujiang/programming_language/Python) | -| [R](http://www.banshujiang.cn/category/programming_language/R/page/1) | [category/programming_language/R](https://rsshub.app/banshujiang/programming_language/R) | -| [Ruby](http://www.banshujiang.cn/category/programming_language/Ruby/page/1) | [category/programming_language/Ruby](https://rsshub.app/banshujiang/programming_language/Ruby) | -| [Rust](http://www.banshujiang.cn/category/programming_language/Rust/page/1) | [category/programming_language/Rust](https://rsshub.app/banshujiang/programming_language/Rust) | -| [Scala](http://www.banshujiang.cn/category/programming_language/Scala/page/1) | [category/programming_language/Scala](https://rsshub.app/banshujiang/programming_language/Scala) | -| [Shell Script](http://www.banshujiang.cn/category/programming_language/Shell%20Script/page/1) | [category/programming_language/Shell%20Script](https://rsshub.app/banshujiang/programming_language/Shell%20Script) | -| [SQL](http://www.banshujiang.cn/category/programming_language/SQL/page/1) | [category/programming_language/SQL](https://rsshub.app/banshujiang/programming_language/SQL) | -| [Swift](http://www.banshujiang.cn/category/programming_language/Swift/page/1) | [category/programming_language/Swift](https://rsshub.app/banshujiang/programming_language/Swift) | -| [TypeScript](http://www.banshujiang.cn/category/programming_language/TypeScript/page/1) | [category/programming_language/TypeScript](https://rsshub.app/banshujiang/programming_language/TypeScript) | +| 分类 | ID | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| [ActionScript](http://www.banshujiang.cn/category/programming_language/ActionScript/page/1) | [category/programming\\_language/ActionScript](https://rsshub.app/banshujiang/programming_language/ActionScript) | +| [ASP.net](http://www.banshujiang.cn/category/programming_language/ASP.net/page/1) | [category/programming\\_language/ASP.net](https://rsshub.app/banshujiang/programming_language/ASP.net) | +| [C](http://www.banshujiang.cn/category/programming_language/C) | [category/programming\\_language/C](https://rsshub.app/banshujiang/programming_language/C) | +| [C#](http://www.banshujiang.cn/category/programming_language/C%23) | [category/programming\\_language/C%23](https://rsshub.app/banshujiang/programming_language/C%23) | +| [C++](http://www.banshujiang.cn/category/programming_language/C++) | [category/programming\\_language/C++](https://rsshub.app/banshujiang/programming_language/C++) | +| [CoffeeScript](http://www.banshujiang.cn/category/programming_language/CoffeeScript) | [category/programming\\_language/CoffeeScript](https://rsshub.app/banshujiang/programming_language/CoffeeScript) | +| [CSS](http://www.banshujiang.cn/category/programming_language/CSS) | \\[category/programming\\_language/CSS) | +| [Dart](http://www.banshujiang.cn/category/programming_language/Dart) | [category/programming\\_language/Dart](https://rsshub.app/banshujiang/programming_language/Dart) | +| [Elixir](http://www.banshujiang.cn/category/programming_language/Elixir) | [category/programming\\_language/Elixir](https://rsshub.app/banshujiang/programming_language/Elixir) | +| [Erlang](http://www.banshujiang.cn/category/programming_language/Erlang) | [category/programming\\_language/Erlang](https://rsshub.app/banshujiang/programming_language/Erlang) | +| [F#](http://www.banshujiang.cn/category/programming_language/F%23) | [category/programming\\_language/F%23](https://rsshub.app/banshujiang/programming_language/F%23) | +| [Go](http://www.banshujiang.cn/category/programming_language/Go) | [category/programming\\_language/Go](https://rsshub.app/banshujiang/programming_language/Go) | +| [Groovy](http://www.banshujiang.cn/category/programming_language/Groovy) | [category/programming\\_language/Groovy](https://rsshub.app/banshujiang/programming_language/Groovy) | +| [Haskell](http://www.banshujiang.cn/category/programming_language/Haskell) | [category/programming\\_language/Haskell](https://rsshub.app/banshujiang/programming_language/Haskell) | +| [HTML5](http://www.banshujiang.cn/category/programming_language/HTML5) | [category/programming\\_language/HTML5](https://rsshub.app/banshujiang/programming_language/HTML5) | +| [Java](http://www.banshujiang.cn/category/programming_language/Java) | [category/programming\\_language/Java](https://rsshub.app/banshujiang/programming_language/Java) | +| [JavaScript](http://www.banshujiang.cn/category/programming_language/JavaScript) | [category/programming\\_language/JavaScript](https://rsshub.app/banshujiang/programming_language/JavaScript) | +| [Kotlin](http://www.banshujiang.cn/category/programming_language/Kotlin) | [category/programming\\_language/Kotlin](https://rsshub.app/banshujiang/programming_language/Kotlin) | +| [Lua](http://www.banshujiang.cn/category/programming_language/Lua) | [category/programming\\_language/Lua](https://rsshub.app/banshujiang/programming_language/Lua) | +| [Objective-C](http://www.banshujiang.cn/category/programming_language/Objective-C) | [category/programming\\_language/Objective-C](https://rsshub.app/banshujiang/programming_language/Objective-C) | +| [Perl](http://www.banshujiang.cn/category/programming_language/Perl) | [category/programming\\_language/Perl](https://rsshub.app/banshujiang/programming_language/Perl) | +| [PHP](http://www.banshujiang.cn/category/programming_language/PHP) | [category/programming\\_language/PHP](https://rsshub.app/banshujiang/programming_language/PHP) | +| [PowerShell](http://www.banshujiang.cn/category/programming_language/PowerShell) | [category/programming\\_language/PowerShell](https://rsshub.app/banshujiang/programming_language/PowerShell) | +| [Python](http://www.banshujiang.cn/category/programming_language/Python) | [category/programming\\_language/Python](https://rsshub.app/banshujiang/programming_language/Python) | +| [R](http://www.banshujiang.cn/category/programming_language/R/page/1) | [category/programming\\_language/R](https://rsshub.app/banshujiang/programming_language/R) | +| [Ruby](http://www.banshujiang.cn/category/programming_language/Ruby/page/1) | [category/programming\\_language/Ruby](https://rsshub.app/banshujiang/programming_language/Ruby) | +| [Rust](http://www.banshujiang.cn/category/programming_language/Rust/page/1) | [category/programming\\_language/Rust](https://rsshub.app/banshujiang/programming_language/Rust) | +| [Scala](http://www.banshujiang.cn/category/programming_language/Scala/page/1) | [category/programming\\_language/Scala](https://rsshub.app/banshujiang/programming_language/Scala) | +| [Shell Script](http://www.banshujiang.cn/category/programming_language/Shell%20Script/page/1) | [category/programming\\_language/Shell%20Script](https://rsshub.app/banshujiang/programming_language/Shell%20Script) | +| [SQL](http://www.banshujiang.cn/category/programming_language/SQL/page/1) | [category/programming\\_language/SQL](https://rsshub.app/banshujiang/programming_language/SQL) | +| [Swift](http://www.banshujiang.cn/category/programming_language/Swift/page/1) | [category/programming\\_language/Swift](https://rsshub.app/banshujiang/programming_language/Swift) | +| [TypeScript](http://www.banshujiang.cn/category/programming_language/TypeScript/page/1) | [category/programming\\_language/TypeScript](https://rsshub.app/banshujiang/programming_language/TypeScript) | #### 移动开发 -| 分类 | ID | -| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -| [Android](http://www.banshujiang.cn/category/mobile_development/Android/page/1) | [category/mobile_development/Android](https://rsshub.app/banshujiang/mobile_development/Android) | -| [iOS](http://www.banshujiang.cn/category/mobile_development/iOS/page/1) | [category/mobile_development/iOS](https://rsshub.app/banshujiang/mobile_development/iOS) | +| 分类 | ID | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| [Android](http://www.banshujiang.cn/category/mobile_development/Android/page/1) | [category/mobile\\_development/Android](https://rsshub.app/banshujiang/mobile_development/Android) | +| [iOS](http://www.banshujiang.cn/category/mobile_development/iOS/page/1) | [category/mobile\\_development/iOS](https://rsshub.app/banshujiang/mobile_development/iOS) | #### 操作系统 -| 分类 | ID | -| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| [Linux](http://www.banshujiang.cn/category/operation_system/Linux/page/1) | [category/operation_system/Linux](https://rsshub.app/banshujiang/operation_system/Linux) | -| [Mac OS X](http://www.banshujiang.cn/category/operation_system/Mac%20OS%20X/page/1) | [category/operation_system/Mac%20OS%20X](https://rsshub.app/banshujiang/operation_system/Mac%20OS%20X) | -| [Unix](http://www.banshujiang.cn/category/operation_system/Unix/page/1) | [category/operation_system/Unix](https://rsshub.app/banshujiang/operation_system/Unix) | -| [Windows](http://www.banshujiang.cn/category/operation_system/Windows/page/1) | [category/operation_system/Windows](https://rsshub.app/banshujiang/operation_system/Windows) | +| 分类 | ID | +| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| [Linux](http://www.banshujiang.cn/category/operation_system/Linux/page/1) | [category/operation\\_system/Linux](https://rsshub.app/banshujiang/operation_system/Linux) | +| [Mac OS X](http://www.banshujiang.cn/category/operation_system/Mac%20OS%20X/page/1) | [category/operation\\_system/Mac%20OS%20X](https://rsshub.app/banshujiang/operation_system/Mac%20OS%20X) | +| [Unix](http://www.banshujiang.cn/category/operation_system/Unix/page/1) | [category/operation\\_system/Unix](https://rsshub.app/banshujiang/operation_system/Unix) | +| [Windows](http://www.banshujiang.cn/category/operation_system/Windows/page/1) | [category/operation\\_system/Windows](https://rsshub.app/banshujiang/operation_system/Windows) | #### 数据库 @@ -450,29 +450,28 @@ export const route: Route = { #### 开源软件 -| 分类 | ID | -| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| [Apache 项目](http://www.banshujiang.cn/category/open_source/Apache项目/page/1) | [category/open_source/Apache 项目](https://rsshub.app/banshujiang/open_source/Apache项目) | -| [Web 开发](http://www.banshujiang.cn/category/open_source/Web开发/page/1) | [category/open_source/Web 开发](https://rsshub.app/banshujiang/open_source/Web开发) | -| [区块链](http://www.banshujiang.cn/category/open_source/区块链/page/1) | [category/open_source/区块链](https://rsshub.app/banshujiang/open_source/区块链) | -| [程序开发](http://www.banshujiang.cn/category/open_source/程序开发/page/1) | [category/open_source/程序开发](https://rsshub.app/banshujiang/open_source/程序开发) | +| 分类 | ID | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| [Apache 项目](http://www.banshujiang.cn/category/open_source/Apache项目/page/1) | [category/open\\_source/Apache 项目](https://rsshub.app/banshujiang/open_source/Apache项目) | +| [Web 开发](http://www.banshujiang.cn/category/open_source/Web开发/page/1) | [category/open\\_source/Web 开发](https://rsshub.app/banshujiang/open_source/Web开发) | +| [区块链](http://www.banshujiang.cn/category/open_source/区块链/page/1) | [category/open\\_source/ 区块链](https://rsshub.app/banshujiang/open_source/区块链) | +| [程序开发](http://www.banshujiang.cn/category/open_source/程序开发/page/1) | [category/open\\_source/ 程序开发](https://rsshub.app/banshujiang/open_source/程序开发) | #### 其他 -| 分类 | ID | -| -------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| [人工智能](http://www.banshujiang.cn/category/other/人工智能/page/1) | [category/other/人工智能](https://rsshub.app/banshujiang/other/人工智能) | -| [容器技术](http://www.banshujiang.cn/category/other/容器技术/page/1) | [category/other/容器技术](https://rsshub.app/banshujiang/other/容器技术) | +| 分类 | ID | +| -------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| [人工智能](http://www.banshujiang.cn/category/other/人工智能/page/1) | [category/other/ 人工智能](https://rsshub.app/banshujiang/other/人工智能) | +| [容器技术](http://www.banshujiang.cn/category/other/容器技术/page/1) | [category/other/ 容器技术](https://rsshub.app/banshujiang/other/容器技术) | #### 语言 -| 分类 | ID | -| --------------------------------------------------------------- | ---------------------------------------------------------------------- | -| [中文](http://www.banshujiang.cn/category/language/中文/page/1) | [category/language/中文](https://rsshub.app/banshujiang/language/中文) | -| [英文](http://www.banshujiang.cn/category/language/英文/page/1) | [category/language/英文](https://rsshub.app/banshujiang/language/英文) | +| 分类 | ID | +| --------------------------------------------------------------- | ----------------------------------------------------------------------- | +| [中文](http://www.banshujiang.cn/category/language/中文/page/1) | [category/language/ 中文](https://rsshub.app/banshujiang/language/中文) | +| [英文](http://www.banshujiang.cn/category/language/英文/page/1) | [category/language/ 英文](https://rsshub.app/banshujiang/language/英文) | - -`, +`, categories: ['reading'], features: { requireConfig: false, diff --git a/lib/routes/banyuetan/index.ts b/lib/routes/banyuetan/index.ts index efcad0dd949b..20b1b369523d 100644 --- a/lib/routes/banyuetan/index.ts +++ b/lib/routes/banyuetan/index.ts @@ -184,9 +184,7 @@ export const route: Route = { | [评论](http://www.banyuetan.org/byt/banyuetanpinglun/index.html) | [banyuetanpinglun](https://rsshub.app/banyuetan/banyuetanpinglun) | | [基层治理](http://www.banyuetan.org/byt/jicengzhili/index.html) | [jicengzhili](https://rsshub.app/banyuetan/jicengzhili) | | [文化](http://www.banyuetan.org/byt/wenhua/index.html) | [wenhua](https://rsshub.app/banyuetan/wenhua) | -| [教育](http://www.banyuetan.org/byt/jiaoyu/index.html) | [jiaoyu](https://rsshub.app/banyuetan/jiaoyu) | - -`, +| [教育](http://www.banyuetan.org/byt/jiaoyu/index.html) | [jiaoyu](https://rsshub.app/banyuetan/jiaoyu) |`, categories: ['traditional-media'], features: { requireConfig: false, diff --git a/lib/routes/barronschina/index.ts b/lib/routes/barronschina/index.ts index f5e22d2526a0..63757da5bfb8 100644 --- a/lib/routes/barronschina/index.ts +++ b/lib/routes/barronschina/index.ts @@ -30,7 +30,7 @@ export const route: Route = { handler, url: 'barronschina.com.cn/', description: `::: tip - 栏目 id 留空则返回快讯,在对应页地址栏 \`columnId=\` 后可以看到。 +栏目 id 留空则返回快讯,在对应页地址栏 \`columnId=\` 后可以看到。 :::`, }; diff --git a/lib/routes/bbc/index.ts b/lib/routes/bbc/index.ts index 0068b4d51993..7bc8f90b514a 100644 --- a/lib/routes/bbc/index.ts +++ b/lib/routes/bbc/index.ts @@ -17,9 +17,9 @@ export const route: Route = { categories: ['traditional-media'], description: `Provides a better reading experience (full text articles) over the official ones. - Support major channels, refer to [BBC RSS feeds](https://www.bbc.co.uk/news/10628494). Eg, \`business\` for \`https://feeds.bbci.co.uk/news/business/rss.xml\`. +Support major channels, refer to [BBC RSS feeds](https://www.bbc.co.uk/news/10628494). Eg, \`business\` for \`https://feeds.bbci.co.uk/news/business/rss.xml\`. - - Channel contains sub-directories, such as \`https://feeds.bbci.co.uk/news/world/asia/rss.xml\`, replace \`/\` with \`-\`, \`/bbc/world-asia\`.`, +- Channel contains sub-directories, such as \`https://feeds.bbci.co.uk/news/world/asia/rss.xml\`, replace \`/\` with \`-\`, \`/bbc/world-asia\`.`, }; async function handler(ctx) { diff --git a/lib/routes/behance/user.tsx b/lib/routes/behance/user.tsx index 7cc26d5c7582..0bd133fcbb64 100644 --- a/lib/routes/behance/user.tsx +++ b/lib/routes/behance/user.tsx @@ -38,7 +38,7 @@ export const route: Route = { name: 'User Works', maintainers: ['MisteryMonster'], handler, - description: `Behance user's profile URL, like [https://www.behance.net/mishapetrick](https://www.behance.net/mishapetrick) the username will be \`mishapetrick\`。`, + description: `Behance user's profile URL, like the username will be \`mishapetrick\`。`, }; const getUserProfile = async (nodes, user) => diff --git a/lib/routes/beijingprice/index.ts b/lib/routes/beijingprice/index.ts index 519c6f677f25..d1ade1e7abbe 100644 --- a/lib/routes/beijingprice/index.ts +++ b/lib/routes/beijingprice/index.ts @@ -109,7 +109,7 @@ export const route: Route = { example: '/beijingprice/jgzx/xwzx', parameters: { category: '分类,默认为 `jgzx/xwzx` 即新闻资讯,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [新闻资讯](https://www.beijingprice.cn/jgzx/xwzx/),网址为 \`https://www.beijingprice.cn/jgzx/xwzx/\`。截取 \`https://beijingprice.cn/\` 到末尾 \`/\` 的部分 \`jgzx/xwzx\` 作为参数填入,此时路由为 [\`/beijingprice/jgzx/xwzx\`](https://rsshub.app/beijingprice/jgzx/xwzx)。 +若订阅 [新闻资讯](https://www.beijingprice.cn/jgzx/xwzx/),网址为 \`https://www.beijingprice.cn/jgzx/xwzx/\`。截取 \`https://beijingprice.cn/\` 到末尾 \`/\` 的部分 \`jgzx/xwzx\` 作为参数填入,此时路由为 [\`/beijingprice/jgzx/xwzx\`](https://rsshub.app/beijingprice/jgzx/xwzx)。 ::: #### [价格资讯](https://www.beijingprice.cn/jgzx/xwzx/) @@ -122,8 +122,7 @@ export const route: Route = { | [价格听证](https://www.beijingprice.cn/zhxx/jgtz/) | [价格监测定点单位名单](https://www.beijingprice.cn/zhxx/jgjcdddwmd/) | [部门预算决算](https://www.beijingprice.cn/bmys/) | | ------------------------------------------------------ | -------------------------------------------------------------------- | ------------------------------------------------- | -| [zhxx/jgtz](https://rsshub.app/beijingprice/zhxx/jgtz) | [zhxx/jgjcdddwmd](https://rsshub.app/beijingprice/zhxx/jgjcdddwmd) | [bmys](https://rsshub.app/beijingprice/bmys) | - `, +| [zhxx/jgtz](https://rsshub.app/beijingprice/zhxx/jgtz) | [zhxx/jgjcdddwmd](https://rsshub.app/beijingprice/zhxx/jgjcdddwmd) | [bmys](https://rsshub.app/beijingprice/bmys) |`, categories: ['government'], features: { diff --git a/lib/routes/bendibao/news.ts b/lib/routes/bendibao/news.ts index c97c77a9f53c..4e863dc8c4aa 100644 --- a/lib/routes/bendibao/news.ts +++ b/lib/routes/bendibao/news.ts @@ -37,9 +37,9 @@ export const route: Route = { | 广州 | gz | | 深圳 | sz | - 更多城市请参见 [这里](http://www.bendibao.com/city.htm) +更多城市请参见 [这里](http://www.bendibao.com/city.htm) - > **香港特别行政区** 和 **澳门特别行政区** 的本地宝城市页面不更新资讯。`, +> **香港特别行政区** 和 **澳门特别行政区** 的本地宝城市页面不更新资讯。`, }; async function handler(ctx) { @@ -59,7 +59,7 @@ async function handler(ctx) { const title = $('title') .text() - .replace(/-爱上本地宝,生活会更好/, '') + `焦点资讯`; + .replace(/-爱上本地宝,生活会更好/, '') + '焦点资讯'; let items = $('ul.focus-news li') .toArray() diff --git a/lib/routes/bestblogs/feeds.ts b/lib/routes/bestblogs/feeds.ts index 151f2b1e560b..95ea2614bb9a 100644 --- a/lib/routes/bestblogs/feeds.ts +++ b/lib/routes/bestblogs/feeds.ts @@ -100,8 +100,8 @@ async function handler(ctx) { })); return { - title: `Bestblogs.dev`, - link: `https://www.bestblogs.dev/feeds`, + title: 'Bestblogs.dev', + link: 'https://www.bestblogs.dev/feeds', item: items, }; } diff --git a/lib/routes/bestblogs/newsletter.ts b/lib/routes/bestblogs/newsletter.ts index 6ed9e2d7f50e..28a7a59095c8 100644 --- a/lib/routes/bestblogs/newsletter.ts +++ b/lib/routes/bestblogs/newsletter.ts @@ -23,12 +23,12 @@ export const route: Route = { async function handler(ctx) { const pageSize = Number.parseInt(ctx.req.query('limit') ?? '10', 10); - const response = await ofetch('https://api.bestblogs.dev/api/newsletter/list', { - method: 'POST', - body: { - currentPage: 1, + const response = await ofetch('https://www.bestblogs.dev/api/proxy/newsletters', { + method: 'GET', + query: { + page: 1, pageSize, - userLanguage: 'zh', + language: 'zh', }, }); diff --git a/lib/routes/bilibili/app.ts b/lib/routes/bilibili/app.ts index 2d0d3d403ec8..c8b67675edf8 100644 --- a/lib/routes/bilibili/app.ts +++ b/lib/routes/bilibili/app.ts @@ -27,7 +27,7 @@ export const route: Route = { handler, description: `| 安卓版 | iPhone 版 | iPad HD 版 | UWP 版 | TV 版 | | ------- | --------- | ---------- | ------ | ---------------- | -| android | iphone | ipad | win | android_tv_yst |`, +| android | iphone | ipad | win | android\\_tv\\_yst |`, }; async function handler(ctx) { diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index e4add3e2226c..213727ec6635 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -6,7 +6,7 @@ import { config } from '@/config'; import cache from '@/utils/cache'; import got from '@/utils/got'; import logger from '@/utils/logger'; -import { getPuppeteerPage } from '@/utils/puppeteer'; +import { getPlaywrightPage } from '@/utils/playwright'; import utils from './utils'; @@ -20,8 +20,8 @@ const subtitleLimiterQueue = new RateLimiterQueue(subtitleLimiter, { maxQueueSize: 4800, }); -const getCookie = (disableConfig = false) => { - if (Object.keys(config.bilibili.cookies).length > 0 && !disableConfig) { +const getConfiguredCookie = () => { + if (Object.keys(config.bilibili.cookies).length > 0) { // Update b_lsid in cookies for (const key of Object.keys(config.bilibili.cookies)) { const cookie = config.bilibili.cookies[key]; @@ -33,12 +33,20 @@ const getCookie = (disableConfig = false) => { return config.bilibili.cookies[Object.keys(config.bilibili.cookies)[Math.floor(Math.random() * Object.keys(config.bilibili.cookies).length)]] || ''; } +}; + +const getCookie = (disableConfig = false) => { + const configuredCookie = disableConfig ? undefined : getConfiguredCookie(); + if (configuredCookie !== undefined) { + return configuredCookie; + } + const key = 'bili-cookie'; return cache.tryGet(key, async () => { let waitForRequest = new Promise((resolve) => { resolve(''); }); - const { destroy } = await getPuppeteerPage('https://space.bilibili.com/1/dynamic', { + const { destroy } = await getPlaywrightPage('https://space.bilibili.com/1/dynamic', { onBeforeLoad: (page) => { waitForRequest = new Promise((resolve) => { page.on('requestfinished', async (request) => { @@ -196,7 +204,7 @@ const getUserInfoFromLiveID = (liveID) => { const getVideoNameFromId = (aid, bvid) => { const key = `bili-videoname-from-id-${bvid || aid}`; return cache.tryGet(key, async () => { - const { data } = await got(`https://api.bilibili.com/x/web-interface/view`, { + const { data } = await got('https://api.bilibili.com/x/web-interface/view', { searchParams: { aid: aid || undefined, bvid: bvid || undefined, @@ -374,6 +382,7 @@ const getArticleDataFromCvid = async (cvid, uid) => { export default { getCookie, + getConfiguredCookie, getWbiVerifyString, getUsernameFromUID, getUsernameAndFaceFromUID, diff --git a/lib/routes/bilibili/check-cookie.ts b/lib/routes/bilibili/check-cookie.ts index f320751b6578..f9886cf766ee 100644 --- a/lib/routes/bilibili/check-cookie.ts +++ b/lib/routes/bilibili/check-cookie.ts @@ -19,17 +19,17 @@ async function handler() { }; } - const response = await ofetch(`https://api.bilibili.com/x/web-interface/nav`, { + const response = await ofetch('https://api.bilibili.com/x/web-interface/nav', { headers: { - Referer: `https://space.bilibili.com/1/`, + Referer: 'https://space.bilibili.com/1/', Cookie: cookie as string, }, }); const isResponseValid = response.code === 0 && !!response.data.mid; - const subtitleResponse = await ofetch(`https://api.bilibili.com/x/player/wbi/v2?bvid=BV1iU411o7R2&cid=1550543560`, { + const subtitleResponse = await ofetch('https://api.bilibili.com/x/player/wbi/v2?bvid=BV1iU411o7R2&cid=1550543560', { headers: { - Referer: `https://www.bilibili.com/video/BV1iU411o7R2`, + Referer: 'https://www.bilibili.com/video/BV1iU411o7R2', Cookie: cookie, }, }); diff --git a/lib/routes/bilibili/dynamic.ts b/lib/routes/bilibili/dynamic.ts index 85c48effee30..3510eaf135bb 100644 --- a/lib/routes/bilibili/dynamic.ts +++ b/lib/routes/bilibili/dynamic.ts @@ -38,7 +38,7 @@ export const route: Route = { { name: 'BILIBILI_COOKIE_*', optional: true, - description: `如果没有此配置,那么必须开启 puppeteer 支持;BILIBILI_COOKIE_{uid}: 用于用户关注动态系列路由,对应 uid 的 b 站用户登录后的 Cookie 值,\`{uid}\` 替换为 uid,如 \`BILIBILI_COOKIE_2267573\`,获取方式: + description: `如果没有此配置,那么必须开启 Playwright 支持;BILIBILI_COOKIE_{uid}: 用于用户关注动态系列路由,对应 uid 的 b 站用户登录后的 Cookie 值,\`{uid}\` 替换为 uid,如 \`BILIBILI_COOKIE_2267573\`,获取方式: 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 diff --git a/lib/routes/bilibili/followers.ts b/lib/routes/bilibili/followers.ts index b0b34adf13d6..2739ff324635 100644 --- a/lib/routes/bilibili/followers.ts +++ b/lib/routes/bilibili/followers.ts @@ -37,7 +37,7 @@ export const route: Route = { maintainers: ['Qixingchen'], handler, description: `::: warning - UP 主粉丝现在需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +UP 主粉丝现在需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/followings-article.ts b/lib/routes/bilibili/followings-article.ts index 1798fd9352bf..0f9e865b0ea6 100644 --- a/lib/routes/bilibili/followings-article.ts +++ b/lib/routes/bilibili/followings-article.ts @@ -31,7 +31,7 @@ export const route: Route = { maintainers: ['woshiluo'], handler, description: `::: warning - 用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -75,7 +75,7 @@ async function handler(ctx) { return { title: `${name} 关注专栏动态`, - link: `https://t.bilibili.com/?tab=64`, + link: 'https://t.bilibili.com/?tab=64', item: out, }; } diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts index 879409655126..45d600558a6f 100644 --- a/lib/routes/bilibili/followings-dynamic.ts +++ b/lib/routes/bilibili/followings-dynamic.ts @@ -50,7 +50,7 @@ export const route: Route = { maintainers: ['TigerCubDen', 'JimenezLi'], handler, description: `::: warning - 用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -212,7 +212,7 @@ async function handler(ctx) { return { title: `${name} 关注的动态`, - link: `https://t.bilibili.com`, + link: 'https://t.bilibili.com', description: `${name} 关注的动态`, item: items, }; diff --git a/lib/routes/bilibili/followings-video.ts b/lib/routes/bilibili/followings-video.ts index dd64b01bd10e..fbef50eaa0a9 100644 --- a/lib/routes/bilibili/followings-video.ts +++ b/lib/routes/bilibili/followings-video.ts @@ -33,7 +33,7 @@ export const route: Route = { maintainers: ['LogicJake'], handler, description: `::: warning - 用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户动态需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -79,7 +79,7 @@ async function handler(ctx) { return { title: `${name} 关注视频动态`, - link: `https://t.bilibili.com/?tab=8`, + link: 'https://t.bilibili.com/?tab=8', item: out, }; } diff --git a/lib/routes/bilibili/followings.ts b/lib/routes/bilibili/followings.ts index 55644f48aa1c..a1140c5d0568 100644 --- a/lib/routes/bilibili/followings.ts +++ b/lib/routes/bilibili/followings.ts @@ -38,7 +38,7 @@ export const route: Route = { maintainers: ['Qixingchen'], handler, description: `::: warning - UP 主关注用户现在需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +UP 主关注用户现在需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/hot-search.ts b/lib/routes/bilibili/hot-search.ts index a644a3dc7d1a..ad759fff45d8 100644 --- a/lib/routes/bilibili/hot-search.ts +++ b/lib/routes/bilibili/hot-search.ts @@ -39,7 +39,7 @@ async function handler() { method: 'get', url, headers: { - Referer: `https://api.bilibili.com`, + Referer: 'https://api.bilibili.com', }, }); const trending = response?.data?.data?.trending; diff --git a/lib/routes/bilibili/live-area.ts b/lib/routes/bilibili/live-area.ts index 4fa7861c0bb3..bce9e945e643 100644 --- a/lib/routes/bilibili/live-area.ts +++ b/lib/routes/bilibili/live-area.ts @@ -18,7 +18,7 @@ export const route: Route = { maintainers: ['Qixingchen'], handler, description: `::: warning - 由于接口未提供开播时间,如果直播间未更换标题与分区,将视为一次。如果直播间更换分区与标题,将视为另一项 +由于接口未提供开播时间,如果直播间未更换标题与分区,将视为一次。如果直播间更换分区与标题,将视为另一项 :::`, }; diff --git a/lib/routes/bilibili/manga-followings.ts b/lib/routes/bilibili/manga-followings.ts index 29af1585a3ff..2b5460a1eea1 100644 --- a/lib/routes/bilibili/manga-followings.ts +++ b/lib/routes/bilibili/manga-followings.ts @@ -31,7 +31,7 @@ export const route: Route = { maintainers: ['yindaheng98'], handler, description: `::: warning - 用户追漫需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户追漫需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -47,7 +47,7 @@ async function handler(ctx) { const link = 'https://manga.bilibili.com/account-center'; const response = await got({ method: 'POST', - url: `https://manga.bilibili.com/twirp/bookshelf.v1.Bookshelf/ListFavorite?device=pc&platform=web`, + url: 'https://manga.bilibili.com/twirp/bookshelf.v1.Bookshelf/ListFavorite?device=pc&platform=web', json: { page_num: 1, page_size, order: 2, wait_free: 0 }, headers: { Referer: link, diff --git a/lib/routes/bilibili/message-at.ts b/lib/routes/bilibili/message-at.ts index c988a7fc5023..f930db10fcfc 100644 --- a/lib/routes/bilibili/message-at.ts +++ b/lib/routes/bilibili/message-at.ts @@ -31,8 +31,8 @@ export const route: Route = { name: '@我的', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/message-like.ts b/lib/routes/bilibili/message-like.ts index fe164ae8bf57..bfe7068cb9f0 100644 --- a/lib/routes/bilibili/message-like.ts +++ b/lib/routes/bilibili/message-like.ts @@ -31,8 +31,8 @@ export const route: Route = { name: '收到的赞', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/message-reply.ts b/lib/routes/bilibili/message-reply.ts index 8ad1a86c1bd6..e94b59b9a9ac 100644 --- a/lib/routes/bilibili/message-reply.ts +++ b/lib/routes/bilibili/message-reply.ts @@ -31,8 +31,8 @@ export const route: Route = { name: '回复我的', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/message-sessions.ts b/lib/routes/bilibili/message-sessions.ts index 14844b927395..f2299d2e40fe 100644 --- a/lib/routes/bilibili/message-sessions.ts +++ b/lib/routes/bilibili/message-sessions.ts @@ -32,8 +32,8 @@ export const route: Route = { name: '我的消息', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/message-system.ts b/lib/routes/bilibili/message-system.ts index ab6d6dfc618e..bbae21e48afe 100644 --- a/lib/routes/bilibili/message-system.ts +++ b/lib/routes/bilibili/message-system.ts @@ -31,8 +31,8 @@ export const route: Route = { name: '系统通知', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/bilibili/message-unread.ts b/lib/routes/bilibili/message-unread.ts index 84cfa907ff06..f4444c70fe26 100644 --- a/lib/routes/bilibili/message-unread.ts +++ b/lib/routes/bilibili/message-unread.ts @@ -30,10 +30,10 @@ export const route: Route = { name: '未读消息', maintainers: ['pilgrimlyieu'], handler, - description: `:::warning - 用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 + description: `::: warning +用户消息需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 - 此路由返回所有未读消息类型的汇总状态。 +此路由返回所有未读消息类型的汇总状态。 :::`, }; diff --git a/lib/routes/bilibili/partion.ts b/lib/routes/bilibili/partion.ts index bce20691edd9..33f959e871e0 100644 --- a/lib/routes/bilibili/partion.ts +++ b/lib/routes/bilibili/partion.ts @@ -25,109 +25,109 @@ export const route: Route = { | ------- | ------ | ---------------- | ---- | ---- | | 24 | 25 | 47 | 86 | 27 | - 番剧 +番剧 | 连载动画 | 完结动画 | 资讯 | 官方延伸 | | -------- | -------- | ---- | -------- | | 33 | 32 | 51 | 152 | - 国创 +国创 | 国产动画 | 国产原创相关 | 布袋戏 | 动态漫・广播剧 | 资讯 | | -------- | ------------ | ------ | -------------- | ---- | | 153 | 168 | 169 | 195 | 170 | - 音乐 +音乐 | 原创音乐 | 翻唱 | VOCALOID·UTAU | 电音 | 演奏 | MV | 音乐现场 | 音乐综合 | ~~OP/ED/OST~~ | | -------- | ---- | ------------- | ---- | ---- | --- | -------- | -------- | ------------- | | 28 | 31 | 30 | 194 | 59 | 193 | 29 | 130 | 54 | - 舞蹈 +舞蹈 | 宅舞 | 街舞 | 明星舞蹈 | 中国舞 | 舞蹈综合 | 舞蹈教程 | | ---- | ---- | -------- | ------ | -------- | -------- | | 20 | 198 | 199 | 200 | 154 | 156 | - 游戏 +游戏 | 单机游戏 | 电子竞技 | 手机游戏 | 网络游戏 | 桌游棋牌 | GMV | 音游 | Mugen | | -------- | -------- | -------- | -------- | -------- | --- | ---- | ----- | | 17 | 171 | 172 | 65 | 173 | 121 | 136 | 19 | - 知识 +知识 | 科学科普 | 社科人文 | 财经 | 校园学习 | 职业职场 | 野生技术协会 | | -------- | -------- | ---- | -------- | -------- | ------------ | | 201 | 124 | 207 | 208 | 209 | 122 | - ~~科技~~ +~~科技~~ | ~~演讲・公开课~~ | ~~星海~~ | ~~机械~~ | ~~汽车~~ | | ---------------- | -------- | -------- | -------- | | 39 | 96 | 98 | 176 | - 数码 +数码 | 手机平板 | 电脑装机 | 摄影摄像 | 影音智能 | | -------- | -------- | -------- | -------- | | 95 | 189 | 190 | 191 | - 生活 +生活 | 搞笑 | 日常 | 美食圈 | 动物圈 | 手工 | 绘画 | 运动 | 汽车 | 其他 | ~~ASMR~~ | | ---- | ---- | ------ | ------ | ---- | ---- | ---- | ---- | ---- | -------- | | 138 | 21 | 76 | 75 | 161 | 162 | 163 | 176 | 174 | 175 | - 鬼畜 +鬼畜 | 鬼畜调教 | 音 MAD | 人力 VOCALOID | 教程演示 | | -------- | ------ | ------------- | -------- | | 22 | 26 | 126 | 127 | - 时尚 +时尚 | 美妆 | 服饰 | 健身 | T 台 | 风向标 | | ---- | ---- | ---- | ---- | ------ | | 157 | 158 | 164 | 159 | 192 | - ~~广告~~ +~~广告~~ | ~~广告~~ | | -------- | | 166 | - 资讯 +资讯 | 热点 | 环球 | 社会 | 综合 | | ---- | ---- | ---- | ---- | | 203 | 204 | 205 | 206 | - 娱乐 +娱乐 | 综艺 | 明星 | Korea 相关 | | ---- | ---- | ---------- | | 71 | 137 | 131 | - 影视 +影视 | 影视杂谈 | 影视剪辑 | 短片 | 预告・资讯 | | -------- | -------- | ---- | ---------- | | 182 | 183 | 85 | 184 | - 纪录片 +纪录片 | 全部 | 人文・历史 | 科学・探索・自然 | 军事 | 社会・美食・旅行 | | ---- | ---------- | ---------------- | ---- | ---------------- | | 177 | 37 | 178 | 179 | 180 | - 电影 +电影 | 全部 | 华语电影 | 欧美电影 | 日本电影 | 其他国家 | | ---- | -------- | -------- | -------- | -------- | | 23 | 147 | 145 | 146 | 83 | - 电视剧 +电视剧 | 全部 | 国产剧 | 海外剧 | | ---- | ------ | ------ | diff --git a/lib/routes/bilibili/popular.ts b/lib/routes/bilibili/popular.ts index 6398942aecd0..b03e7a00ba17 100644 --- a/lib/routes/bilibili/popular.ts +++ b/lib/routes/bilibili/popular.ts @@ -27,7 +27,7 @@ async function handler(ctx) { const embed = !ctx.req.param('embed'); const response = await got({ method: 'get', - url: `https://api.bilibili.com/x/web-interface/popular`, + url: 'https://api.bilibili.com/x/web-interface/popular', headers: { Referer: 'https://www.bilibili.com/', }, @@ -35,9 +35,9 @@ async function handler(ctx) { const list = response.data.data.list; return { - title: `bilibili 综合热门`, + title: 'bilibili 综合热门', link: 'https://www.bilibili.com', - description: `bilibili 综合热门`, + description: 'bilibili 综合热门', item: list && list.map((item) => ({ diff --git a/lib/routes/bilibili/user-bangumi.ts b/lib/routes/bilibili/user-bangumi.ts index 889fbae4816e..1be92613fb6f 100644 --- a/lib/routes/bilibili/user-bangumi.ts +++ b/lib/routes/bilibili/user-bangumi.ts @@ -56,7 +56,7 @@ async function handler(ctx) { title: `[${item.new_ep.index_show}]${item.title}`, description: `${item.evaluate}
`, pubDate: new Date(item.new_ep.pub_time ?? Date.now()).toUTCString(), - link: `https://www.bilibili.com/bangumi/play/` + (item.new_ep.id ? `ep${item.new_ep.id}` : `ss${item.season_id}`), + link: 'https://www.bilibili.com/bangumi/play/' + (item.new_ep.id ? `ep${item.new_ep.id}` : `ss${item.season_id}`), })), }; } diff --git a/lib/routes/bilibili/video.ts b/lib/routes/bilibili/video.ts index 9448965ecdbe..9b2a66bdfae5 100644 --- a/lib/routes/bilibili/video.ts +++ b/lib/routes/bilibili/video.ts @@ -6,6 +6,7 @@ import { ViewType } from '@/types'; import got from '@/utils/got'; import { parseDuration } from '@/utils/helpers'; import logger from '@/utils/logger'; +import { getPlaywrightPage, type Page } from '@/utils/playwright'; import cache from './cache'; import utils, { getVideoUrl } from './utils'; @@ -35,11 +36,115 @@ export const route: Route = { handler, }; -async function handler(ctx: Context) { - const isJsonFeed = ctx.req.query('format') === 'json'; +interface VideoItem { + aid: number; + author?: string; + bvid?: string; + comment?: number; + created: number; + description: string; + length: string; + pic: string; + title: string; +} - const uid = ctx.req.param('uid'); - const embed = !ctx.req.param('embed'); +interface VideoListData { + list?: { + vlist?: VideoItem[]; + }; +} + +interface VideoListResponse { + code?: number; + data?: VideoListData; + message?: string; +} + +type BrowserResponse = Awaited>; + +const videoListApiPath = '/x/space/wbi/arc/search'; +const allowedBrowserRequestTypes = new Set(['document', 'script', 'xhr', 'fetch', 'image', 'font', 'stylesheet', 'other']); +const browserResponseTimeout = 45000; +const browserCloseTimeout = 90000; + +const getErrorMessage = (error: unknown) => (error instanceof Error ? error.message : String(error)); + +const isVideoListApiResponse = (response: BrowserResponse) => { + const request = response.request(); + return response.url().includes(videoListApiPath) && request.method() === 'GET' && (request.resourceType() === 'xhr' || request.resourceType() === 'fetch'); +}; + +const waitForVideoListResponse = async (page: Page) => { + try { + return { + response: await page.waitForResponse(isVideoListApiResponse, { timeout: browserResponseTimeout }), + }; + } catch (error) { + return { error }; + } +}; + +const navigateToVideoPage = async (page: Page, videoUrl: string) => { + try { + await page.goto(videoUrl, { timeout: browserResponseTimeout, waitUntil: 'domcontentloaded' }); + } catch (error) { + logger.warn(`[bilibili/video] video page navigation did not finish before the response wait ended: ${getErrorMessage(error)}`); + } +}; + +const getVideoListResponse = async (responsePromise: ReturnType) => { + const videoListResponseResult = await responsePromise; + if ('error' in videoListResponseResult) { + throw new Error(`Bilibili browser mode did not receive a video list response within ${browserResponseTimeout}ms: ${getErrorMessage(videoListResponseResult.error)}`); + } + + return videoListResponseResult.response; +}; + +const waitForVideoListResponseFromVideoPage = async (page: Page, videoUrl: string): Promise => { + const videoListResponsePromise = waitForVideoListResponse(page); + await navigateToVideoPage(page, videoUrl); + + const response = await getVideoListResponse(videoListResponsePromise); + + if (response.status() !== 200) { + throw new Error(`Bilibili browser mode returned unexpected video list API status ${response.status()}`); + } + + const contentType = response.headers()['content-type']; + if (!contentType?.includes('application/json')) { + throw new Error(`Bilibili browser mode returned non-JSON response with status ${response.status()}; BILIBILI_COOKIE_* may be required`); + } + + return response; +}; + +async function applyCookie(page: Page, cookie: string) { + const cookies = cookie + .split(';') + .map((item) => item.trim()) + .filter(Boolean) + .map((item) => { + const equalIndex = item.indexOf('='); + if (equalIndex <= 0) { + return; + } + + return { + name: item.slice(0, equalIndex).trim(), + value: item.slice(equalIndex + 1).trim(), + domain: '.bilibili.com', + path: '/', + }; + }) + .filter((item) => item !== undefined); + + if (cookies.length > 0) { + await page.setCookie(...cookies); + } +} + +async function fetchVideoListFromApi(uid: string): Promise { const cookie = await cache.getCookie(); const wbiVerifyString = await cache.getWbiVerifyString(); const dmImgList = utils.getDmImgList(); @@ -50,10 +155,10 @@ async function handler(ctx: Context) { utils.addRenderData(utils.addDmVerifyInfoWithInter(`mid=${uid}&ps=30&tid=0&pn=1&keyword=&order=pubdate&platform=web&web_location=1550101&order_avoided=true`, dmImgList, dmImgInter), renderData), wbiVerifyString ); - const response = await got(`https://api.bilibili.com/x/space/wbi/arc/search?${params}`, { + const response = await got(`https://api.bilibili.com${videoListApiPath}?${params}`, { headers: { Referer: `https://space.bilibili.com/${uid}`, - origin: `https://space.bilibili.com`, + origin: 'https://space.bilibili.com', Cookie: cookie, }, }); @@ -63,9 +168,75 @@ async function handler(ctx: Context) { throw new Error(`Got error code ${data.code} while fetching: ${data.message}`); } - const usernameAndFace = await cache.getUsernameAndFaceFromUID(uid); - const name = usernameAndFace[0] || data.data.list.vlist[0]?.author; - const face = usernameAndFace[1]; + return data.data; +} + +async function fetchVideoListFromBrowser(uid: string): Promise { + const cookie = cache.getConfiguredCookie(); + const videoUrl = `https://space.bilibili.com/${uid}/video`; + logger.info(`[bilibili/video] fetching via playwright: ${videoUrl}`); + + const { destroy, page } = await getPlaywrightPage(videoUrl, { + closeTimeout: browserCloseTimeout, + noGoto: true, + onBeforeLoad: async (page) => { + if (cookie) { + await applyCookie(page, cookie); + } + + await page.setRequestInterception(true); + page.on('request', (request) => { + allowedBrowserRequestTypes.has(request.resourceType()) ? request.continue() : request.abort(); + }); + }, + gotoConfig: { waitUntil: 'domcontentloaded' }, + }); + + try { + const response = await waitForVideoListResponseFromVideoPage(page, videoUrl); + const data = (await response.json()) as VideoListResponse; + if (data.code) { + logger.error(JSON.stringify(data.data)); + throw new Error(`Got error code ${data.code} while fetching in browser mode: ${data.message}`); + } + + if (!data.data) { + throw new Error('Bilibili browser response does not contain video list data'); + } + + return data.data; + } finally { + await destroy(); + } +} + +async function getVideoList(uid: string): Promise { + try { + return await fetchVideoListFromApi(uid); + } catch (error) { + logger.warn(`[bilibili/video] API request failed, falling back to browser mode: ${error}`); + return fetchVideoListFromBrowser(uid); + } +} + +async function handler(ctx: Context) { + const isJsonFeed = ctx.req.query('format') === 'json'; + + const uid = ctx.req.param('uid'); + const embed = !ctx.req.param('embed'); + const data = await getVideoList(uid); + const videos = data.list?.vlist ?? []; + + let name = videos[0]?.author || uid; + let face: string | undefined; + + try { + const usernameAndFace = await cache.getUsernameAndFaceFromUID(uid); + name = usernameAndFace[0] || name; + face = usernameAndFace[1]; + } catch (error) { + logger.warn(`[bilibili/video] failed to fetch user profile: ${error}`); + } return { title: `${name} 的 bilibili 空间`, @@ -74,32 +245,28 @@ async function handler(ctx: Context) { image: face ?? undefined, logo: face ?? undefined, icon: face ?? undefined, - item: - data.data && - data.data.list && - data.data.list.vlist && - (await Promise.all( - data.data.list.vlist.map(async (item) => { - const subtitles = isJsonFeed && !config.bilibili.excludeSubtitles && item.bvid ? await cache.getVideoSubtitleAttachment(item.bvid) : []; - return { - title: item.title, - description: utils.renderUGCDescription(embed, item.pic, item.description, item.aid, undefined, item.bvid), - pubDate: new Date(item.created * 1000).toUTCString(), - link: item.created > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, - author: name, - comments: item.comment, - attachments: item.bvid - ? [ - { - url: getVideoUrl(item.bvid), - mime_type: 'text/html', - duration_in_seconds: parseDuration(item.length), - }, - ...subtitles, - ] - : undefined, - }; - }) - )), + item: await Promise.all( + videos.map(async (item) => { + const subtitles = isJsonFeed && !config.bilibili.excludeSubtitles && item.bvid ? await cache.getVideoSubtitleAttachment(item.bvid) : []; + return { + title: item.title, + description: utils.renderUGCDescription(embed, item.pic, item.description, item.aid, undefined, item.bvid), + pubDate: new Date(item.created * 1000).toUTCString(), + link: item.created > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, + author: name, + comments: item.comment, + attachments: item.bvid + ? [ + { + url: getVideoUrl(item.bvid), + mime_type: 'text/html', + duration_in_seconds: parseDuration(item.length), + }, + ...subtitles, + ] + : undefined, + }; + }) + ), }; } diff --git a/lib/routes/bilibili/vsearch.ts b/lib/routes/bilibili/vsearch.ts index 7f2c94538be2..17596a01afc4 100644 --- a/lib/routes/bilibili/vsearch.ts +++ b/lib/routes/bilibili/vsearch.ts @@ -20,7 +20,7 @@ export const route: Route = { { name: 'BILIBILI_COOKIE_*', optional: true, - description: `如果没有此配置,那么必须开启 puppeteer 支持;BILIBILI_COOKIE_{uid}: 用于用户关注动态系列路由,对应 uid 的 b 站用户登录后的 Cookie 值,\`{uid}\` 替换为 uid,如 \`BILIBILI_COOKIE_2267573\`,获取方式: + description: `如果没有此配置,那么必须开启 Playwright 支持;BILIBILI_COOKIE_{uid}: 用于用户关注动态系列路由,对应 uid 的 b 站用户登录后的 Cookie 值,\`{uid}\` 替换为 uid,如 \`BILIBILI_COOKIE_2267573\`,获取方式: 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 diff --git a/lib/routes/bilibili/watchlater.ts b/lib/routes/bilibili/watchlater.ts index 2be2aee30cf7..5e8bcb13dec6 100644 --- a/lib/routes/bilibili/watchlater.ts +++ b/lib/routes/bilibili/watchlater.ts @@ -33,7 +33,7 @@ export const route: Route = { maintainers: ['JimenezLi'], handler, description: `::: warning - 用户稍后再看需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户稍后再看需要 b 站登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -49,7 +49,7 @@ async function handler(ctx) { const response = await got({ method: 'get', - url: `https://api.bilibili.com/x/v2/history/toview`, + url: 'https://api.bilibili.com/x/v2/history/toview', headers: { Referer: `https://space.bilibili.com/${uid}/`, Cookie: cookie, diff --git a/lib/routes/bing/daily-wallpaper.ts b/lib/routes/bing/daily-wallpaper.ts index c19a684de382..b4de4e85030d 100644 --- a/lib/routes/bing/daily-wallpaper.ts +++ b/lib/routes/bing/daily-wallpaper.ts @@ -23,12 +23,11 @@ export const route: Route = { handler, url: 'www.bing.com/', example: '/bing/type=UHD&story=1&lang=zh-CN', - description: `| 参数 | 含义 | 接受的值 | 默认值 | 备注 | -|-------|--------------------|-----------------------------------------------------------|-----------|--------------------------------------------------------| -| type | 输出壁纸的像素类型 | UHD/1920x1080/1920x1200/768x1366/1080x1920/1080x1920_logo | 1920x1080 | 1920x1200与1080x1920_logo带有水印,输入的值不在接受范围内都会输出成1920x1080 | -| story | 是否输出壁纸的故事 | 1/0 | 0 | 输入的值不为1都不会输出故事 | -| lang | 输出壁纸图文的地区(中文或者是英文) | zh/en | zh | zh/en输出的壁纸图文不一定是一样的;如果en不生效,试着部署到其他地方 | -`, + description: `| 参数 | 含义 | 接受的值 | 默认值 | 备注 | +| ----- | ----------------------------------- | ---------------------------------------------------------- | --------- | --------------------------------------------------------------------------------- | +| type | 输出壁纸的像素类型 | UHD/1920x1080/1920x1200/768x1366/1080x1920/1080x1920\\_logo | 1920x1080 | 1920x1200 与 1080x1920\\_logo 带有水印,输入的值不在接受范围内都会输出成 1920x1080 | +| story | 是否输出壁纸的故事 | 1/0 | 0 | 输入的值不为 1 都不会输出故事 | +| lang | 输出壁纸图文的地区 (中文或者是英文) | zh/en | zh | zh/en 输出的壁纸图文不一定是一样的;如果 en 不生效,试着部署到其他地方 |`, }; async function handler(ctx) { diff --git a/lib/routes/biquge/namespace.ts b/lib/routes/biquge/namespace.ts index 25da181955d1..b72d9bd8e20c 100644 --- a/lib/routes/biquge/namespace.ts +++ b/lib/routes/biquge/namespace.ts @@ -7,22 +7,22 @@ export const namespace: Namespace = { 此处的 **笔趣阁** 指网络上使用和 **笔趣阁** 样式相似模板的小说阅读网站,包括但不限于下方列举的网址。 ::: -| 网址 | 名称 | -| ---------------------------------------------------- | ---------- | -| [https://www.xbiquwx.la](https://www.xbiquwx.la) | 笔尖中文 | -| [http://www.biqu5200.net](http://www.biqu5200.net) | 笔趣阁 | -| [https://www.xbiquge.so](https://www.xbiquge.so) | 笔趣阁 | -| [https://www.biqugeu.net](https://www.biqugeu.net) | 顶点小说网 | -| [http://www.b520.cc](http://www.b520.cc) | 笔趣阁 | -| [https://www.ahfgb.com](https://www.ahfgb.com) | 笔趣鸽 | -| [https://www.ibiquge.la](https://www.ibiquge.la) | 香书小说 | -| [https://www.biquge.tv](https://www.biquge.tv) | 笔趣阁 | -| [https://www.bswtan.com](https://www.bswtan.com) | 笔书网 | -| [https://www.biquge.co](https://www.biquge.co) | 笔趣阁 | -| [https://www.bqzhh.com](https://www.bqzhh.com) | 笔趣阁 | -| [http://www.biqugse.com](http://www.biqugse.com) | 笔趣阁 | -| [https://www.ibiquge.info](https://www.ibiquge.info) | 爱笔楼 | -| [https://www.ishuquge.com](https://www.ishuquge.com) | 书趣阁 | -| [https://www.mayiwxw.com](https://www.mayiwxw.com) | 蚂蚁文学 |`, +| 网址 | 名称 | +| -------------------------- | ---------- | +| | 笔尖中文 | +| | 笔趣阁 | +| | 笔趣阁 | +| | 顶点小说网 | +| | 笔趣阁 | +| | 笔趣鸽 | +| | 香书小说 | +| | 笔趣阁 | +| | 笔书网 | +| | 笔趣阁 | +| | 笔趣阁 | +| | 笔趣阁 | +| | 爱笔楼 | +| | 书趣阁 | +| | 蚂蚁文学 |`, lang: 'zh-CN', }; diff --git a/lib/routes/bitget/announcement.ts b/lib/routes/bitget/announcement.ts index 46807eb2867d..3ac9b20874d3 100644 --- a/lib/routes/bitget/announcement.ts +++ b/lib/routes/bitget/announcement.ts @@ -178,27 +178,27 @@ export const route: Route = { }, ], name: 'Announcement', - description: ` -type: -| Type | Description | -| --- | --- | -| all | 全部通知 | -| new-listing | 新币上线 | -| latest-activities | 最新活动 | -| new-announcement | 最新公告 | + description: `type: + +| Type | Description | +| ----------------- | ----------- | +| all | 全部通知 | +| new-listing | 新币上线 | +| latest-activities | 最新活动 | +| new-announcement | 最新公告 | lang: -| Lang | Description | -| --- | --- | -| zh-CN | 中文 | -| en-US | English | -| es-ES | Español | -| fr-FR | Français | -| de-DE | Deutsch | -| ja-JP | 日本語 | -| ru-RU | Русский | -| ar-SA | العربية | -`, + +| Lang | Description | +| ----- | ----------- | +| zh-CN | 中文 | +| en-US | English | +| es-ES | Español | +| fr-FR | Français | +| de-DE | Deutsch | +| ja-JP | 日本語 | +| ru-RU | Русский | +| ar-SA | العربية |`, maintainers: ['YukiCoco'], handler, }; diff --git a/lib/routes/bjsk/index.ts b/lib/routes/bjsk/index.ts index c01bacdb5c9e..11bf8d02db69 100644 --- a/lib/routes/bjsk/index.ts +++ b/lib/routes/bjsk/index.ts @@ -24,9 +24,9 @@ export const route: Route = { maintainers: ['TonyRL'], handler, description: `::: tip - 路径处填写对应页面 URL 中 \`https://www.bjsk.org.cn/\` 和 \`.html\` 之间的字段。下面是一个例子。 +路径处填写对应页面 URL 中 \`https://www.bjsk.org.cn/\` 和 \`.html\` 之间的字段。下面是一个例子。 - 若订阅 [社科资讯 > 社科要闻](https://www.bjsk.org.cn/newslist-1394-1474-0.html) 则将对应页面 URL \`https://www.bjsk.org.cn/newslist-1394-1474-0.html\` 中 \`https://www.bjsk.org.cn/\` 和 \`.html\` 之间的字段 \`newslist-1394-1474-0\` 作为路径填入。此时路由为 [\`/bjsk/newslist-1394-1474-0\`](https://rsshub.app/bjsk/newslist-1394-1474-0) +若订阅 [社科资讯 > 社科要闻](https://www.bjsk.org.cn/newslist-1394-1474-0.html) 则将对应页面 URL \`https://www.bjsk.org.cn/newslist-1394-1474-0.html\` 中 \`https://www.bjsk.org.cn/\` 和 \`.html\` 之间的字段 \`newslist-1394-1474-0\` 作为路径填入。此时路由为 [\`/bjsk/newslist-1394-1474-0\`](https://rsshub.app/bjsk/newslist-1394-1474-0) :::`, }; diff --git a/lib/routes/bjtu/gs.ts b/lib/routes/bjtu/gs.ts index af3ab85f7cc9..dec0963cf217 100644 --- a/lib/routes/bjtu/gs.ts +++ b/lib/routes/bjtu/gs.ts @@ -169,29 +169,28 @@ export const route: Route = { name: '研究生院', maintainers: ['E1nzbern'], handler, - description: ` -| 文章来源 | 参数 | -| ----------------- | ------------ | -| 通知公告_招生 | noti_zs | -| 通知公告 | noti | -| 新闻动态 | news | -| 招生宣传 | zsxc | -| 培养 | py | -| 招生 | zs | -| 学位 | xw | -| 研工部 | ygb | + description: `| 文章来源 | 参数 | +| ------------------- | ------------ | +| 通知公告\\_招生 | noti\\_zs | +| 通知公告 | noti | +| 新闻动态 | news | +| 招生宣传 | zsxc | +| 培养 | py | +| 招生 | zs | +| 学位 | xw | +| 研工部 | ygb | | 通知公告 - 研工部 | ygbtzgg | | 新闻动态 - 研工部 | ygbnews | | 新闻封面 - 研工部 | ygbnewscover | -| 文章列表 | all | -| 博士招生 - 招生专题 | bszs_zszt | -| 硕士招生 - 招生专题 | sszs_zszt | -| 招生简章 - 招生专题 | zsjz_zszt | -| 政策法规 - 招生专题 | zcfg_zszt | +| 文章列表 | all | +| 博士招生 - 招生专题 | bszs\\_zszt | +| 硕士招生 - 招生专题 | sszs\\_zszt | +| 招生简章 - 招生专题 | zsjz\\_zszt | +| 政策法规 - 招生专题 | zcfg\\_zszt | ::: tip - 文章来源的命名均来自研究生院网站标题。 - 最常用的几项有“通知公告_招生”、“通知公告”、“博士招生 - 招生专题”、“硕士招生 - 招生专题”。 +文章来源的命名均来自研究生院网站标题。 +最常用的几项有 “通知公告\\_招生”、“通知公告”、“博士招生 - 招生专题”、“硕士招生 - 招生专题”。 :::`, }; diff --git a/lib/routes/blizzard/news-cn.ts b/lib/routes/blizzard/news-cn.ts index 2337527eb5a9..34fcea7baa23 100644 --- a/lib/routes/blizzard/news-cn.ts +++ b/lib/routes/blizzard/news-cn.ts @@ -34,11 +34,9 @@ export const route: Route = { ], name: '暴雪游戏国服新闻', maintainers: ['zhangpeng2k'], - description: ` -| 守望先锋 | 炉石传说 | 魔兽世界 | -|----------|----------|---------| -| ow | hs | wow | -`, + description: `| 守望先锋 | 炉石传说 | 魔兽世界 | +| -------- | -------- | -------- | +| ow | hs | wow |`, handler, }; diff --git a/lib/routes/blizzard/news.ts b/lib/routes/blizzard/news.ts index 4cbf0dcc555b..9eae841dbfd8 100644 --- a/lib/routes/blizzard/news.ts +++ b/lib/routes/blizzard/news.ts @@ -41,7 +41,7 @@ export const route: Route = { | BlizzCon | blizzcon | | Inside Blizzard | blizzard | - Language codes +Language codes | Language | Code | | ------------------ | ----- | diff --git a/lib/routes/bloomberg/index.ts b/lib/routes/bloomberg/index.ts index d2e8f6a35f8b..cb81d7c7924f 100644 --- a/lib/routes/bloomberg/index.ts +++ b/lib/routes/bloomberg/index.ts @@ -41,8 +41,7 @@ export const route: Route = { }, name: 'Bloomberg Site', maintainers: ['bigfei'], - description: ` -| Site ID | Title | + description: `| Site ID | Title | | ------------ | ------------ | | / | News | | bpol | Politics | @@ -55,8 +54,7 @@ export const route: Route = { | bview | Opinion | | equality | Equality | | businessweek | Businessweek | -| citylab | CityLab | - `, +| citylab | CityLab |`, handler, }; diff --git a/lib/routes/bloomberg/utils.ts b/lib/routes/bloomberg/utils.ts index b1ed65570ba3..215c3b716a57 100644 --- a/lib/routes/bloomberg/utils.ts +++ b/lib/routes/bloomberg/utils.ts @@ -466,8 +466,8 @@ const nodeRenderers = { } return nextNode(node.content); }, - br: () => `
`, - hr: () => `
`, + br: () => '
', + hr: () => '
', ad: () => {}, blockquote: async (node, nextNode) => `
${await nextNode(node.content)}
`, quote: async (node, nextNode) => `
${await nextNode(node.content)}
`, diff --git a/lib/routes/bluestacks/release.ts b/lib/routes/bluestacks/release.ts index 8823a1da4d74..bf3c823ecf1e 100644 --- a/lib/routes/bluestacks/release.ts +++ b/lib/routes/bluestacks/release.ts @@ -3,7 +3,7 @@ import * as cheerio from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const pageUrl = 'https://support.bluestacks.com/hc/en-us/articles/360056960211-Release-Notes-BlueStacks-5'; @@ -32,7 +32,7 @@ export const route: Route = { }; async function handler() { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/bntnews/index.ts b/lib/routes/bntnews/index.ts index 4db339beb58d..aa117467e9a4 100644 --- a/lib/routes/bntnews/index.ts +++ b/lib/routes/bntnews/index.ts @@ -32,8 +32,8 @@ export const route: Route = { name: 'Category', maintainers: ['iamsnn'], handler, - description: `| Beauty | Fashion | Star | Style+ | Photo | Life | Now | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | + description: `| Beauty | Fashion | Star | Style+ | Photo | Life | Now | +| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ | ------------ | | bnt003000000 | bnt002000000 | bnt004000000 | bnt007000000 | bnt009000000 | bnt005000000 | bnt008000000 |`, }; diff --git a/lib/routes/bnu/dwxgb.ts b/lib/routes/bnu/dwxgb.ts index 4b67dc0bc47d..d0c90099602c 100644 --- a/lib/routes/bnu/dwxgb.ts +++ b/lib/routes/bnu/dwxgb.ts @@ -26,7 +26,7 @@ export const route: Route = { name: '党委学生工作部', maintainers: ['Fatpandac'], handler, - description: `\`https://dwxgb.bnu.edu.cn/xwzx/tzgg/index.html\` 则对应为 \`/bnu/dwxgb/xwzx/tzgg`, + description: '`https://dwxgb.bnu.edu.cn/xwzx/tzgg/index.html` 则对应为 \\`/bnu/dwxgb/xwzx/tzgg', }; async function handler(ctx) { diff --git a/lib/routes/bnu/fe.ts b/lib/routes/bnu/fe.ts index 68ff77d1288e..1feb27d5e189 100644 --- a/lib/routes/bnu/fe.ts +++ b/lib/routes/bnu/fe.ts @@ -18,7 +18,7 @@ export const route: Route = { name: '教育学部-培养动态', maintainers: ['etShaw-zh'], handler, - description: `\`https://fe.bnu.edu.cn/pc/cms1info/list/1/18\` 则对应为 \`/bnu/fe/18`, + description: '`https://fe.bnu.edu.cn/pc/cms1info/list/1/18` 则对应为 \\`/bnu/fe/18', }; async function handler(ctx) { diff --git a/lib/routes/bnu/mba.ts b/lib/routes/bnu/mba.ts index 09715046c09e..8a2c251bfcd5 100644 --- a/lib/routes/bnu/mba.ts +++ b/lib/routes/bnu/mba.ts @@ -85,7 +85,7 @@ export const route: Route = { example: '/bnu/mba/xwdt', parameters: { category: '分类,默认为 xwdt,即新闻聚焦' }, description: `::: tip - 若订阅 [新闻聚焦](https://mba.bnu.edu.cn/xwdt/index.html),网址为 \`https://mba.bnu.edu.cn/xwdt/index.html\`。截取 \`https://mba.bnu.edu.cn/\` 到末尾 \`/index.html\` 的部分 \`xwdt\` 作为参数填入,此时路由为 [\`/bnu/mba/xwdt\`](https://rsshub.app/bnu/mba/xwdt)。 +若订阅 [新闻聚焦](https://mba.bnu.edu.cn/xwdt/index.html),网址为 \`https://mba.bnu.edu.cn/xwdt/index.html\`。截取 \`https://mba.bnu.edu.cn/\` 到末尾 \`/index.html\` 的部分 \`xwdt\` 作为参数填入,此时路由为 [\`/bnu/mba/xwdt\`](https://rsshub.app/bnu/mba/xwdt)。 ::: #### [主页](https://mba.bnu.edu.cn) @@ -116,8 +116,7 @@ export const route: Route = { | [校外导师](https://mba.bnu.edu.cn/zyfz/xwds/index.html) | [企业实践](https://mba.bnu.edu.cn/zyfz/zycp/index.html) | [就业创业](https://mba.bnu.edu.cn/zyfz/jycy/index.html) | | ------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- | -| [zyfz/xwds](https://rsshub.app/bnu/mba/zyfz/xwds) | [zyfz/zycp](https://rsshub.app/bnu/mba/zyfz/zycp) | [zyfz/jycy](https://rsshub.app/bnu/mba/zyfz/jycy) | - `, +| [zyfz/xwds](https://rsshub.app/bnu/mba/zyfz/xwds) | [zyfz/zycp](https://rsshub.app/bnu/mba/zyfz/zycp) | [zyfz/jycy](https://rsshub.app/bnu/mba/zyfz/jycy) |`, categories: ['university'], features: { diff --git a/lib/routes/bookwalker/search.tsx b/lib/routes/bookwalker/search.tsx index 5eb1f8cf8aea..1e2ae1daec68 100644 --- a/lib/routes/bookwalker/search.tsx +++ b/lib/routes/bookwalker/search.tsx @@ -89,7 +89,7 @@ export const route: Route = { }, }, description: `::: tip -订阅 [依發售日新至舊排序的文學小說](https://www.bookwalker.com.tw/search?order=sell_desc&s=34),其源网址为 \`https://www.bookwalker.com.tw/search?order=sell_desc&s=34\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/bookwalker/search/order=sell_desc&s=34\`](https://rsshub.app/bookwalker/search/order=sell_desc&s=34)。 +订阅 [依發售日新至舊排序的文學小說](https://www.bookwalker.com.tw/search?order=sell_desc\\&s=34),其源网址为 \`https://www.bookwalker.com.tw/search?order=sell_desc&s=34\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/bookwalker/search/order=sell_desc&s=34\`](https://rsshub.app/bookwalker/search/order=sell_desc\\&s=34)。 :::`, categories: ['shopping'], features: { diff --git a/lib/routes/booru/mmda.ts b/lib/routes/booru/mmda.ts index dc0fb6a8867e..69cc18361fa6 100644 --- a/lib/routes/booru/mmda.ts +++ b/lib/routes/booru/mmda.ts @@ -33,10 +33,10 @@ export const route: Route = { handler, description: `For example: - - 默认查询 (什么 tag 都不加):\`/booru/mmda/tags\` - - 默认查询单个 tag:\`/booru/mmda/tags/full_body\` - - 默认查询多个 tag:\`/booru/mmda/tags/full_body%20blue_eyes\` - - 默认查询根据作者查询:\`/booru/mmda/tags/user:xxxx\``, +- 默认查询 (什么 tag 都不加):\`/booru/mmda/tags\` +- 默认查询单个 tag:\`/booru/mmda/tags/full_body\` +- 默认查询多个 tag:\`/booru/mmda/tags/full_body%20blue_eyes\` +- 默认查询根据作者查询:\`/booru/mmda/tags/user:xxxx\``, }; async function handler(ctx) { diff --git a/lib/routes/bossdesign/index.ts b/lib/routes/bossdesign/index.ts index 28f8e4a616d2..c76f02563cbe 100644 --- a/lib/routes/bossdesign/index.ts +++ b/lib/routes/bossdesign/index.ts @@ -21,7 +21,7 @@ export const route: Route = { handler, description: `| Boss 笔记 | 电脑日志 | 素材资源 | 设计师神器 | 设计教程 | 设计资讯 | | --------- | --------------- | ---------------- | --------------- | --------------- | ------------------- | -| note | computer-skills | design-resources | design-software | design-tutorial | design_information |`, +| note | computer-skills | design-resources | design-software | design-tutorial | design\\_information |`, }; async function handler(ctx) { diff --git a/lib/routes/bse/index.ts b/lib/routes/bse/index.ts index 64435b249c4e..6154cf51cdc6 100644 --- a/lib/routes/bse/index.ts +++ b/lib/routes/bse/index.ts @@ -144,23 +144,23 @@ export const route: Route = { url: 'bse.cn/', description: `| 本所要闻 | 人才招聘 | 采购信息 | 业务通知 | | --------------- | -------- | -------- | ---------- | -| important_news | recruit | purchase | news_list | +| important\\_news | recruit | purchase | news\\_list | | 法律法规 | 公开征求意见 | 部门规章 | 发行融资 | | --------- | --------------- | ---------------- | ---------- | -| law_list | public_opinion | regulation_list | fxrz_list | +| law\\_list | public\\_opinion | regulation\\_list | fxrz\\_list | | 持续监管 | 交易管理 | 市场管理 | 上市委会议公告 | | ---------- | ---------- | ---------- | --------------- | -| cxjg_list | jygl_list | scgl_list | meeting_notice | +| cxjg\\_list | jygl\\_list | scgl\\_list | meeting\\_notice | | 上市委会议结果公告 | 上市委会议变更公告 | 并购重组委会议公告 | | ------------------ | ------------------ | ------------------ | -| meeting_result | meeting_change | bgcz_notice | +| meeting\\_result | meeting\\_change | bgcz\\_notice | | 并购重组委会议结果公告 | 并购重组委会议变更公告 | 终止审核 | 注册结果 | | ---------------------- | ---------------------- | ------------------ | ------------- | -| bgcz_result | bgcz_change | termination_audit | audit_result |`, +| bgcz\\_result | bgcz\\_change | termination\\_audit | audit\\_result |`, }; async function handler(ctx) { diff --git a/lib/routes/bsky/posts.ts b/lib/routes/bsky/posts.ts index 062543654d86..27e6a602d59a 100644 --- a/lib/routes/bsky/posts.ts +++ b/lib/routes/bsky/posts.ts @@ -33,17 +33,17 @@ export const route: Route = { name: 'Post', maintainers: ['TonyRL'], handler, - description: ` -| Filter Value | Description | -|--------------|-------------| -| posts_with_replies | Includes Posts, Replies, and Reposts | -| posts_no_replies | Includes Posts and Reposts, without Replies | -| posts_with_media | Shows only Posts containing media | -| posts_and_author_threads | Shows Posts and Threads, without Replies and Reposts | + description: `| Filter Value | Description | +| --------------------------- | ---------------------------------------------------- | +| posts\\_with\\_replies | Includes Posts, Replies, and Reposts | +| posts\\_no\\_replies | Includes Posts and Reposts, without Replies | +| posts\\_with\\_media | Shows only Posts containing media | +| posts\\_and\\_author\\_threads | Shows Posts and Threads, without Replies and Reposts | Default value for filter is \`posts_and_author_threads\` if not specified. Example: + - \`/bsky/profile/bsky.app/filter=posts_with_replies\``, }; diff --git a/lib/routes/bt0/namespace.ts b/lib/routes/bt0/namespace.ts index 9aec8c046775..b40101b70597 100644 --- a/lib/routes/bt0/namespace.ts +++ b/lib/routes/bt0/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: '不太灵影视', url: '2bt0.com', description: `::: tip - (1-9)bt0.com 都指向同一个 +(1-9) bt0.com 都指向同一个 :::`, lang: 'zh-CN', }; diff --git a/lib/routes/btbtla/namespace.ts b/lib/routes/btbtla/namespace.ts index c8492a5295fe..51f77863f4a2 100644 --- a/lib/routes/btbtla/namespace.ts +++ b/lib/routes/btbtla/namespace.ts @@ -3,5 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'BT影视', url: 'www.btbtla.com', - description: 'BT影视的页面内容,最近更新列表,视频种子列表。', + description: 'BT 影视的页面内容,最近更新列表,视频种子列表。', }; diff --git a/lib/routes/btzj/index.tsx b/lib/routes/btzj/index.tsx index ffb65e5e9ba2..4b5d450af7de 100644 --- a/lib/routes/btzj/index.tsx +++ b/lib/routes/btzj/index.tsx @@ -45,12 +45,12 @@ export const route: Route = { handler, url: 'btbtt20.com/', description: `::: tip - 分类页中域名末尾到 \`.htm\` 前的字段即为对应分类,如 [电影](https://www.btbtt20.com/forum-index-fid-951.htm) \`https://www.btbtt20.com/forum-index-fid-951.htm\` 中域名末尾到 \`.htm\` 前的字段为 \`forum-index-fid-951\`,所以路由应为 [\`/btzj/forum-index-fid-951\`](https://rsshub.app/btzj/forum-index-fid-951) +分类页中域名末尾到 \`.htm\` 前的字段即为对应分类,如 [电影](https://www.btbtt20.com/forum-index-fid-951.htm) \`https://www.btbtt20.com/forum-index-fid-951.htm\` 中域名末尾到 \`.htm\` 前的字段为 \`forum-index-fid-951\`,所以路由应为 [\`/btzj/forum-index-fid-951\`](https://rsshub.app/btzj/forum-index-fid-951) - 部分分类页,如 [电影](https://www.btbtt20.com/forum-index-fid-951.htm)、[剧集](https://www.btbtt20.com/forum-index-fid-950.htm) 等,提供了更复杂的分类筛选。你可以将选项选中后,获得结果分类页 URL 中分类参数,构成路由。如选中分类 [高清电影 - 年份:2021 - 地区:欧美](https://www.btbtt20.com/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0.htm) \`https://www.btbtt20.com/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0.htm\` 中域名末尾到 \`.htm\` 前的字段为 \`forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0\`,所以路由应为 [\`/btzj/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0\`](https://rsshub.app/btzj/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0) +部分分类页,如 [电影](https://www.btbtt20.com/forum-index-fid-951.htm)、[剧集](https://www.btbtt20.com/forum-index-fid-950.htm) 等,提供了更复杂的分类筛选。你可以将选项选中后,获得结果分类页 URL 中分类参数,构成路由。如选中分类 [高清电影 - 年份:2021 - 地区:欧美](https://www.btbtt20.com/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0.htm) \`https://www.btbtt20.com/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0.htm\` 中域名末尾到 \`.htm\` 前的字段为 \`forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0\`,所以路由应为 [\`/btzj/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0\`](https://rsshub.app/btzj/forum-index-fid-1183-typeid1-0-typeid2-738-typeid3-10086-typeid4-0) ::: - 基础分类如下: +基础分类如下: | 交流 | 电影 | 剧集 | 高清电影 | | ------------------- | ------------------- | ------------------- | -------------------- | @@ -69,11 +69,11 @@ export const route: Route = { | forum-index-fid-1187 | forum-index-fid-1191 | ::: tip - BT 之家的域名会变更,本路由以 \`https://www.btbtt20.com\` 为默认域名,若该域名无法访问,可以通过在路由后方加上 \`?domain=<域名>\` 指定路由访问的域名。如指定域名为 \`https://www.btbtt15.com\`,则在 \`/btzj\` 后加上 \`?domain=btbtt15.com\` 即可,此时路由为 [\`/btzj?domain=btbtt15.com\`](https://rsshub.app/btzj?domain=btbtt15.com) +BT 之家的域名会变更,本路由以 \`https://www.btbtt20.com\` 为默认域名,若该域名无法访问,可以通过在路由后方加上 \`?domain=<域名>\` 指定路由访问的域名。如指定域名为 \`https://www.btbtt15.com\`,则在 \`/btzj\` 后加上 \`?domain=btbtt15.com\` 即可,此时路由为 [\`/btzj?domain=btbtt15.com\`](https://rsshub.app/btzj?domain=btbtt15.com) - 如果加入了分类参数,直接在分类参数后加入 \`?domain=<域名>\` 即可。如指定分类 [剧集](https://www.btbtt20.com/forum-index-fid-950.htm) \`https://www.btbtt20.com/forum-index-fid-950.htm\` 并指定域名为 \`https://www.btbtt15.com\`,即在 \`/btzj/forum-index-fid-950\` 后加上 \`?domain=btbtt15.com\`,此时路由为 [\`/btzj/forum-index-fid-950?domain=btbtt15.com\`](https://rsshub.app/btzj/forum-index-fid-950?domain=btbtt15.com) +如果加入了分类参数,直接在分类参数后加入 \`?domain=<域名>\` 即可。如指定分类 [剧集](https://www.btbtt20.com/forum-index-fid-950.htm) \`https://www.btbtt20.com/forum-index-fid-950.htm\` 并指定域名为 \`https://www.btbtt15.com\`,即在 \`/btzj/forum-index-fid-950\` 后加上 \`?domain=btbtt15.com\`,此时路由为 [\`/btzj/forum-index-fid-950?domain=btbtt15.com\`](https://rsshub.app/btzj/forum-index-fid-950?domain=btbtt15.com) - 目前,你可以选择的域名有 \`btbtt10-20.com\` 共 10 个,或 \`88btbbt.com\`,该站也提供了专用网址查询工具。详见 [此贴](https://www.btbtt20.com/thread-index-fid-2-tid-4550191.htm) +目前,你可以选择的域名有 \`btbtt10-20.com\` 共 10 个,或 \`88btbbt.com\`,该站也提供了专用网址查询工具。详见 [此贴](https://www.btbtt20.com/thread-index-fid-2-tid-4550191.htm) :::`, }; diff --git a/lib/routes/buaa/jiaowu.ts b/lib/routes/buaa/jiaowu.ts index 3f7af41101fe..2bcfc4da5792 100644 --- a/lib/routes/buaa/jiaowu.ts +++ b/lib/routes/buaa/jiaowu.ts @@ -26,6 +26,7 @@ export const route: Route = { 1. 新闻快讯页面的链接中 \`onclick="javascript:onNewsList('03');return false;"\`,对应的路径参数为 \`03\`,完整路由为 \`/buaa/jiaowu/03\`; 2. 通知公告 > 公示专区页面的链接中 \`onclick="javascript:onNewsList2('0203','2');return false;"\`,对应的路径参数为 \`0203\`,完整路由为 \`/buaa/jiaowu/0203\`。 + :::`, categories: ['university'], features: { diff --git a/lib/routes/buaa/lib/space/newbook.tsx b/lib/routes/buaa/lib/space/newbook.tsx index 585f50ed7fd1..f9ffebe6f285 100644 --- a/lib/routes/buaa/lib/space/newbook.tsx +++ b/lib/routes/buaa/lib/space/newbook.tsx @@ -72,6 +72,7 @@ export const route: Route = { example: '/buaa/lib/space/newbook/', handler, description: `可通过参数进行筛选:\`/buaa/lib/space/newbook/key1=value1&key2=value2...\` + - \`dcpCode\`:学科分类代码 - 例: - 工学:\`08\` @@ -92,14 +93,14 @@ export const route: Route = { - 注意事项:只有本馆一个可选值。 - \`locaCode\`:馆藏地代码 - 例: - - 五层西-中文新书借阅室(A-Z类):\`02503\` + - 五层西 - 中文新书借阅室 (A-Z 类):\`02503\` - 默认值:无 - 注意事项:必须与 \`libCode\` 同时使用。 示例: + - \`buaa/lib/space/newbook\` 为所有新书 -- \`buaa/lib/space/newbook/clsNo=U&libCode=00000&locaCode=60001\` 为沙河教2图书馆所有中图分类号为 U(交通运输)的书籍 -`, +- \`buaa/lib/space/newbook/clsNo=U&libCode=00000&locaCode=60001\` 为沙河教 2 图书馆所有中图分类号为 U(交通运输)的书籍`, categories: ['university'], features: { diff --git a/lib/routes/buaa/news/index.ts b/lib/routes/buaa/news/index.ts index 24fef9850a8e..ad88f8572172 100644 --- a/lib/routes/buaa/news/index.ts +++ b/lib/routes/buaa/news/index.ts @@ -23,9 +23,9 @@ export const route: Route = { name: '新闻网', maintainers: ['AlanDecode'], handler, - description: `| 综合新闻 | 信息公告 | 学术文化 | 校园风采 | 科教在线 | 媒体北航 | 专题新闻 | 北航人物 | -| -------- | -------- | ----------- | -------- | -------- | -------- | -------- | -------- | -| zhxw | xxgg_new | xsjwhhd_new | xyfc_new | kjzx_new | mtbh_new | ztxw | bhrw |`, + description: `| 综合新闻 | 信息公告 | 学术文化 | 校园风采 | 科教在线 | 媒体北航 | 专题新闻 | 北航人物 | +| -------- | --------- | ------------ | --------- | --------- | --------- | -------- | -------- | +| zhxw | xxgg\\_new | xsjwhhd\\_new | xyfc\\_new | kjzx\\_new | mtbh\\_new | ztxw | bhrw |`, }; async function handler(ctx: Context): Promise { diff --git a/lib/routes/bugzilla/namespace.ts b/lib/routes/bugzilla/namespace.ts index 012d6382f7e9..f30d928795f1 100644 --- a/lib/routes/bugzilla/namespace.ts +++ b/lib/routes/bugzilla/namespace.ts @@ -6,7 +6,7 @@ export const namespace: Namespace = { description: 'Bugzilla instances hosted by organizations.', zh: { name: 'Bugzilla', - description: '各组织自建的Bugzilla实例。', + description: '各组织自建的 Bugzilla 实例。', }, lang: 'en', }; diff --git a/lib/routes/bullionvault/gold-news.ts b/lib/routes/bullionvault/gold-news.ts index 86a624f798f4..04d8393a95e8 100644 --- a/lib/routes/bullionvault/gold-news.ts +++ b/lib/routes/bullionvault/gold-news.ts @@ -170,8 +170,7 @@ If you subscribe to [Gold Price News](https://www.bullionvault.com/gold-news/gol | [Investment News](https://www.bullionvault.com/gold-news/news) | [news](https://rsshub.app/bullionvault/gold-news/news) | | [Gold Investor Index](https://www.bullionvault.com/gold-news/gold-investor-index) | [gold-investor-index](https://rsshub.app/bullionvault/gold-news/gold-investor-index) | | [Gold Infographics](https://www.bullionvault.com/gold-news/infographics) | [infographics](https://rsshub.app/bullionvault/gold-news/infographics) | -| [Market Fundamentals](https://www.bullionvault.com/gold-news/market-fundamentals) | [market-fundamentals](https://rsshub.app/bullionvault/gold-news/market-fundamentals) | -`, +| [Market Fundamentals](https://www.bullionvault.com/gold-news/market-fundamentals) | [market-fundamentals](https://rsshub.app/bullionvault/gold-news/market-fundamentals) |`, categories: ['finance'], features: { requireConfig: false, diff --git a/lib/routes/bwsg/index.ts b/lib/routes/bwsg/index.ts index 83e0fe54601a..51435d76f854 100644 --- a/lib/routes/bwsg/index.ts +++ b/lib/routes/bwsg/index.ts @@ -16,8 +16,7 @@ export const route: Route = { path: '*', maintainers: ['sk22'], categories: ['other'], - description: ` -Copy the query parameters for your https://www.bwsg.at/immobilien/immobilie-suchen + description: `Copy the query parameters for your search, omitting the leading \`?\` ::: tip diff --git a/lib/routes/byau/xinwen/index.ts b/lib/routes/byau/xinwen/index.ts index aa1892012b50..33a051dd52bd 100644 --- a/lib/routes/byau/xinwen/index.ts +++ b/lib/routes/byau/xinwen/index.ts @@ -22,8 +22,8 @@ export const route: Route = { handler, url: 'xinwen.byau.edu.cn', description: `| 学校要闻 | 校园动态 | -| ---- | ----------- | -| 3674 | 3676 |`, +| -------- | -------- | +| 3674 | 3676 |`, }; async function handler(ctx) { diff --git a/lib/routes/c114/roll.ts b/lib/routes/c114/roll.ts index 12629867a83a..76fbc4b1a1b5 100644 --- a/lib/routes/c114/roll.ts +++ b/lib/routes/c114/roll.ts @@ -12,7 +12,7 @@ export const handler = async (ctx) => { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; const rootUrl = 'https://www.c114.com.cn'; - const currentUrl = new URL(`news/roll.asp${original === 'true' ? `?o=true` : ''}`, rootUrl).href; + const currentUrl = new URL(`news/roll.asp${original === 'true' ? '?o=true' : ''}`, rootUrl).href; const { data: response } = await got(currentUrl, { responseType: 'buffer', diff --git a/lib/routes/cags/edu/index.ts b/lib/routes/cags/edu/index.ts index 9a117f4e49cb..b3758c945817 100644 --- a/lib/routes/cags/edu/index.ts +++ b/lib/routes/cags/edu/index.ts @@ -36,11 +36,9 @@ export const route: Route = { }, ], handler, - description: ` -| 通知公告 | 要闻简讯 | 博士生招生 | 硕士生招生 | 大学生夏令营 | + description: `| 通知公告 | 要闻简讯 | 博士生招生 | 硕士生招生 | 大学生夏令营 | | -------- | -------- | ---------- | ---------- | ------------ | -| tzgg | ywjx | zs_bss | zs_sss | zs_dxsxly | -`, +| tzgg | ywjx | zs\\_bss | zs\\_sss | zs\\_dxsxly |`, }; async function handler(ctx) { diff --git a/lib/routes/caicai/blog.ts b/lib/routes/caicai/blog.ts new file mode 100644 index 000000000000..bc2426bb666a --- /dev/null +++ b/lib/routes/caicai/blog.ts @@ -0,0 +1,138 @@ +import { load } from 'cheerio'; +import type { Context } from 'hono'; + +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: '/blog/:lang?', + categories: ['blog'], + example: '/caicai/blog', + parameters: { + lang: { + description: 'Language', + options: [ + { value: 'en', label: 'English' }, + { value: 'zh', label: '中文' }, + ], + default: 'en', + }, + }, + radar: [ + { + source: ['www.caicai.me/blogs'], + }, + { + source: ['www.caicai.me/zh/blogs'], + target: '/blog/zh', + }, + ], + name: 'Blog', + maintainers: ['TonyRL'], + handler, + url: 'www.caicai.me/blogs', +}; + +const baseUrl = 'https://www.caicai.me'; + +const renderAnnotation = (rich) => + rich + .map((t) => { + let s = t.content; + const a = t.annotations; + if (a.code) { + s = `${s}`; + } + if (a.bold) { + s = `${s}`; + } + if (a.italic) { + s = `${s}`; + } + if (a.strikethrough) { + s = `${s}`; + } + if (a.underline) { + s = `${s}`; + } + if (t.href) { + s = `${s}`; + } + return s; + }) + .join(''); + +const renderBlocks = (blocks) => + blocks + .map((b, i) => { + switch (b.type) { + case 'paragraph': + return b.text.length ? `

${renderAnnotation(b.text)}

` : ''; + case 'heading_2': + return b.text.length ? `

${renderAnnotation(b.text)}

` : ''; + case 'heading_3': + return b.text.length ? `

${renderAnnotation(b.text)}

` : ''; + case 'bulleted_list_item': { + if (!b.text.length) { + return ''; + } + const open = blocks[i - 1]?.type === 'bulleted_list_item' ? '' : '
    '; + const close = blocks[i + 1]?.type === 'bulleted_list_item' ? '' : '
'; + return `${open}
  • ${renderAnnotation(b.text)}
  • ${close}`; + } + case 'image': + return ``; + case 'divider': + return '
    '; + default: + return ''; + } + }) + .join(''); + +async function handler(ctx: Context) { + const { lang = 'en' } = ctx.req.param(); + const prefix = lang === 'en' ? '' : `/${lang}`; + const link = `${baseUrl}${prefix}/blogs`; + + const response = await ofetch(link); + const $ = load(response); + + const nextData = JSON.parse($('script#__NEXT_DATA__').text()); + + const list = nextData.props.pageProps.posts.map((post) => ({ + title: post.frontMatter.title, + link: `${baseUrl}${prefix}/blogs/${post.slug}`, + description: post.frontMatter.excerpt, + pubDate: parseDate(post.frontMatter.dateIso), + category: [post.frontMatter.group], + image: new URL(post.frontMatter.cover_image, baseUrl).href, + })); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link, async () => { + const response = await ofetch(item.link); + const $ = load(response); + + const { pageProps } = JSON.parse($('script#__NEXT_DATA__').text()).props; + const blocks = lang === 'en' ? pageProps.blocks : pageProps.chineseBlocks; + + item.description = renderBlocks(blocks); + + return item; + }) + ) + ); + + return { + title: $('head title').text(), + description: $('head meta[name="description"]').attr('content'), + link, + language: lang === 'en' ? ('en' as const) : ('zh-CN' as const), + image: `${baseUrl}/favicon.ico`, + item: items, + }; +} diff --git a/lib/routes/caicai/namespace.ts b/lib/routes/caicai/namespace.ts new file mode 100644 index 000000000000..c1f4d4ad3957 --- /dev/null +++ b/lib/routes/caicai/namespace.ts @@ -0,0 +1,10 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'CaiCai', + url: 'www.caicai.me', + categories: ['blog'], + zh: { + name: 'CaiCai 的博客', + }, +}; diff --git a/lib/routes/caixin/blog.ts b/lib/routes/caixin/blog.ts index 89d61546fb49..0a35b557dbd3 100644 --- a/lib/routes/caixin/blog.ts +++ b/lib/routes/caixin/blog.ts @@ -25,7 +25,7 @@ export const route: Route = { name: '用户博客', maintainers: [], handler, - description: `通过提取文章全文,以提供比官方源更佳的阅读体验.`, + description: '通过提取文章全文,以提供比官方源更佳的阅读体验.', }; async function handler(ctx) { @@ -94,7 +94,7 @@ async function handler(ctx) { const items = await Promise.all(posts.map((item) => cache.tryGet(item.link, () => parseBlogArticle(item)))); return { - title: `财新博客 - 全部`, + title: '财新博客 - 全部', link: 'https://blog.caixin.com', // description: introduce, // image: avatar, diff --git a/lib/routes/caixin/category.ts b/lib/routes/caixin/category.ts index 7a6f1953317a..0fd9f0e2072b 100644 --- a/lib/routes/caixin/category.ts +++ b/lib/routes/caixin/category.ts @@ -32,17 +32,17 @@ export const route: Route = { | ------- | ------- | ----- | ------- | ------------- | ------- | ------- | ------ | | economy | finance | china | science | international | opinion | culture | weekly | - 以金融板块为例的 category 列表:(其余 column 以类似方式寻找) +以金融板块为例的 category 列表:(其余 column 以类似方式寻找) | 监管 | 银行 | 证券基金 | 信托保险 | 投资 | 创新 | 市场 | | ---------- | ---- | -------- | ---------------- | ---------- | ---------- | ------ | -| regulation | bank | stock | insurance_trust | investment | innovation | market | +| regulation | bank | stock | insurance\\_trust | investment | innovation | market | - Category 列表: +Category 列表: | 封面报道 | 开卷 | 社论 | 时事 | 编辑寄语 | 经济 | 金融 | 商业 | 环境与科技 | 民生 | 副刊 | | ---------- | ----- | --------- | ---------------- | ------------ | ------- | ------- | -------- | ----------------------- | ------- | ------ | -| coverstory | first | editorial | current_affairs | editor_desk | economy | finance | business | environment_technology | cwcivil | column |`, +| coverstory | first | editorial | current\\_affairs | editor\\_desk | economy | finance | business | environment\\_technology | cwcivil | column |`, }; async function handler(ctx) { diff --git a/lib/routes/caixin/latest.ts b/lib/routes/caixin/latest.ts index 1c1a0ac73272..933dca07b746 100644 --- a/lib/routes/caixin/latest.ts +++ b/lib/routes/caixin/latest.ts @@ -29,7 +29,7 @@ export const route: Route = { maintainers: ['tpnonthealps'], handler, url: 'caixin.com/', - description: `说明:此 RSS feed 会自动抓取财新网的最新文章,但不包含 FM 及视频内容。订阅用户可根据文档设置环境变量后,在url传入\`fulltext=\`以解锁全文。`, + description: '说明:此 RSS feed 会自动抓取财新网的最新文章,但不包含 FM 及视频内容。订阅用户可根据文档设置环境变量后,在 url 传入`fulltext=`以解锁全文。', }; async function handler(ctx) { diff --git a/lib/routes/caixin/namespace.ts b/lib/routes/caixin/namespace.ts index b00547f4e02c..99e7f9008e39 100644 --- a/lib/routes/caixin/namespace.ts +++ b/lib/routes/caixin/namespace.ts @@ -3,6 +3,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '财新博客', url: 'caixin.com', - description: `> 网站部分内容需要付费订阅,RSS 仅做更新提醒,不含付费内容。若需要得到付费内容全文,请使用订阅账户在手机网页版登录,然后设置\`CAIXIN_COOKIE\`为至少包含cookie中的以下字段: \`SA_USER_UID\`, \`SA_USER_UNIT\`, \`SA_USER_DEVICE_TYPE\`, \`USER_LOGIN_CODE\``, + description: + '> 网站部分内容需要付费订阅,RSS 仅做更新提醒,不含付费内容。若需要得到付费内容全文,请使用订阅账户在手机网页版登录,然后设置`CAIXIN_COOKIE`为至少包含 cookie 中的以下字段: `SA_USER_UID`, `SA_USER_UNIT`, `SA_USER_DEVICE_TYPE`, `USER_LOGIN_CODE`', lang: 'zh-CN', }; diff --git a/lib/routes/caixin/utils-fulltext.ts b/lib/routes/caixin/utils-fulltext.ts index 3d1b01da06d3..e17c54b6b391 100644 --- a/lib/routes/caixin/utils-fulltext.ts +++ b/lib/routes/caixin/utils-fulltext.ts @@ -34,7 +34,7 @@ export async function getFulltext(url: string) { const sigValueHex = hextob64(sig.sign()); const isWeekly = url.includes('weekly'); - const res = await ofetch(`https://gateway.caixin.com/api/newauth/checkAuthByIdJsonp`, { + const res = await ofetch('https://gateway.caixin.com/api/newauth/checkAuthByIdJsonp', { query: { type: 1, page: isWeekly ? 0 : 1, diff --git a/lib/routes/cast/index.ts b/lib/routes/cast/index.ts index 041191454739..dd8e0f34f6d4 100644 --- a/lib/routes/cast/index.ts +++ b/lib/routes/cast/index.ts @@ -63,7 +63,7 @@ export const route: Route = { maintainers: ['KarasuShin', 'TonyRL'], handler, description: `::: tip - 在路由末尾处加上 \`?limit=限制获取数目\` 来限制获取条目数量,默认值为\`10\` +在路由末尾处加上 \`?limit=限制获取数目\` 来限制获取条目数量,默认值为\`10\` ::: | 分类 | 编码 | diff --git a/lib/routes/castbox/channel.ts b/lib/routes/castbox/channel.ts new file mode 100644 index 000000000000..cfda05902718 --- /dev/null +++ b/lib/routes/castbox/channel.ts @@ -0,0 +1,113 @@ +import crypto from 'node:crypto'; + +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +const PERMUTAION_MAP = [24, 13, 4, 19, 6, 0, 8, 21, 25, 7, 28, 1, 15, 31, 10, 9, 17, 18, 22, 11, 27, 23, 2, 26, 12, 5, 29, 14, 20, 30, 16, 3]; + +const getNonce = (params: Record) => { + const m = new Date().toISOString().slice(0, 10).replaceAll('-', ''); + + const sortedKeys = Object.keys(params).toSorted(); + const queryParts = sortedKeys.map((k) => `${k}=${params[k]}`); + const queryStr = queryParts.join('&'); + + const hashInput = `${queryStr}evst${m}`; + + const md5Hex = crypto.createHash('md5').update(hashInput).digest('hex'); + + const n = PERMUTAION_MAP.map((idx) => md5Hex[idx]).join(''); + + return { m, n, queryStr }; +}; + +export const route: Route = { + path: '/channel/:channel', + categories: ['multimedia'], + example: '/castbox/channel/Lemonade-Stand-id6776228', + parameters: { + channel: 'Channel', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: [ + { + source: ['castbox.fm/channel/:channel'], + target: '/channel/:channel', + }, + ], + name: 'Channels', + description: `Get the channel from the Castbox channel URL. For example, the URL of the channel "Lemonade Stand" is \`https://castbox.fm/channel/Lemonade-Stand-id6776228\`, where \`Lemonade-Stand-id6776228\` is the \`channel\` parameter. + +You can use the RSSHub global \`limit\` query parameter to specify the maximum number of episodes to fetch from the Castbox API (defaults to 50). For example: \`/castbox/channel/Lemonade-Stand-id6776228?limit=100\`.`, + maintainers: ['ananyatimalsina'], + handler: async (ctx) => { + const { channel } = ctx.req.param(); + const cid = channel.split('-id')[1]; + + if (!cid) { + throw new Error('Invalid channel format. Missing -id'); + } + + const channelParams = { cid, r: 1, raw: 1, web: 1 }; + const { m: cm, n: cn, queryStr: cQuery } = getNonce(channelParams); + + const channelData = await ofetch(`https://everest.castbox.fm/data/channel/v3?${cQuery}&m=${cm}&n=${cn}`); + + if (!channelData?.data) { + throw new Error('Failed to fetch channel data from Castbox'); + } + + const chData = channelData.data; + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit') as string, 10) : 50; + + const epParams = { cid, limit, r: 1, raw: 1, web: 1 }; + const { m: em, n: en, queryStr: eQuery } = getNonce(epParams); + + const epData = await ofetch(`https://everest.castbox.fm/data/episode_list/v2?${eQuery}&m=${em}&n=${en}`); + + if (!epData?.data?.episode_list) { + throw new Error('Failed to fetch episode list from Castbox'); + } + + const episodes = epData.data.episode_list; + + const items = episodes.map((ep: any) => { + let enclosure_type = 'audio/mpeg'; + if (ep.video === 1 || ep.url?.includes('.mp4')) { + enclosure_type = 'video/mp4'; + } else if (ep.url?.includes('.m4a')) { + enclosure_type = 'audio/mp4'; + } + + return { + title: ep.title, + description: ep.description, + pubDate: parseDate(ep.release_date), + link: `https://castbox.fm/episode/${encodeURIComponent(ep.title)}-id${cid}-id${ep.eid}`, + enclosure_url: ep.url, + enclosure_type, + enclosure_length: ep.size, + itunes_item_image: ep.big_cover_url || ep.cover_url, + itunes_duration: ep.duration ? Math.round(ep.duration / 1000) : undefined, + }; + }); + + return { + title: chData.title, + link: `https://castbox.fm/channel/${channel}`, + description: chData.description, + image: chData.big_cover_url || chData.small_cover_url, + language: chData.language, + itunes_author: chData.author, + item: items, + }; + }, +}; diff --git a/lib/routes/castbox/namespace.ts b/lib/routes/castbox/namespace.ts new file mode 100644 index 000000000000..cf504e0f6b42 --- /dev/null +++ b/lib/routes/castbox/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'Castbox', + url: 'castbox.fm', + description: 'Castbox is a podcast distribution network and producer.', +}; diff --git a/lib/routes/catti/news.ts b/lib/routes/catti/news.ts index 20fd5aae4f7a..dda96c2a1c0f 100644 --- a/lib/routes/catti/news.ts +++ b/lib/routes/catti/news.ts @@ -91,13 +91,11 @@ export const route: Route = { path: '/news/:category', name: 'CATTI 考试消息', maintainers: ['PrinOrange'], - description: ` -| Category | 标题 | 描述 | -|-----------|------------|--------------------| -| ggl | 通知公告 | CATTI 考试通知和公告 | -| ywdt | 要闻动态 | CATTI 考试要闻动态 | -| zxzc | 最新政策 | CATTI 考试最新政策 | -`, + description: `| Category | 标题 | 描述 | +| -------- | -------- | -------------------- | +| ggl | 通知公告 | CATTI 考试通知和公告 | +| ywdt | 要闻动态 | CATTI 考试要闻动态 | +| zxzc | 最新政策 | CATTI 考试最新政策 |`, handler, categories: ['study'], parameters: { diff --git a/lib/routes/cau/ele.ts b/lib/routes/cau/ele.ts index 99d251040716..38dd4e692c77 100644 --- a/lib/routes/cau/ele.ts +++ b/lib/routes/cau/ele.ts @@ -26,7 +26,7 @@ export const route: Route = { maintainers: ['shengmaosu'], handler, url: 'ciee.cau.edu.cn/col/col26712/index.html', - description: `#### 信电学院 {#zhong-guo-nong-ye-da-xue-yan-zhao-wang-tong-zhi-gong-gao-xin-dian-xue-yuan}`, + description: '#### 信电学院 {#zhong-guo-nong-ye-da-xue-yan-zhao-wang-tong-zhi-gong-gao-xin-dian-xue-yuan}', }; async function handler() { diff --git a/lib/routes/cbirc/index.ts b/lib/routes/cbirc/index.ts index 41a1bbfdc337..1f6d8e3bd916 100644 --- a/lib/routes/cbirc/index.ts +++ b/lib/routes/cbirc/index.ts @@ -4,69 +4,69 @@ import got from '@/utils/got'; const categories = { jgdt: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '监管动态', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=915,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=915,pageIndex=1,pageSize=18.json', title: '监管动态', }, ggtz: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '公告通知', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=925,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=925,pageIndex=1,pageSize=18.json', title: '公告通知', }, zcfg: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '政策法规', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=926,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=926,pageIndex=1,pageSize=18.json', title: '政策法规', }, zcjd: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '政策解读', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=916,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=916,pageIndex=1,pageSize=18.json', title: '政策解读', }, zqyj: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '征求意见', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=951,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=951,pageIndex=1,pageSize=18.json', title: '征求意见', }, xzxk: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '行政许可', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=930,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=930,pageIndex=1,pageSize=18.json', title: '行政许可', }, xzcf: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '行政处罚', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=931,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=931,pageIndex=1,pageSize=18.json', title: '行政处罚', }, xzjgcs: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '行政监管措施', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=932,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=932,pageIndex=1,pageSize=18.json', title: '行政监管措施', }, gzlw: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '工作论文', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=934,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=934,pageIndex=1,pageSize=18.json', title: '工作论文', }, jrzgyj: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '金融监管研究', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=935,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=935,pageIndex=1,pageSize=18.json', title: '金融监管研究', }, tjxx: { - baseUrl: `http://www.cbirc.gov.cn`, + baseUrl: 'http://www.cbirc.gov.cn', description: '统计信息', - link: `http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=954,pageIndex=1,pageSize=18.json`, + link: 'http://www.cbirc.gov.cn/cn/static/data/DocInfo/SelectDocByItemIdAndChild/data_itemId=954,pageIndex=1,pageSize=18.json', title: '统计信息', }, }; @@ -101,7 +101,7 @@ async function handler(ctx) { method: 'get', url: cat.link, headers: { - Referer: `http://www.cbirc.gov.cn`, + Referer: 'http://www.cbirc.gov.cn', }, }); return resp.data; diff --git a/lib/routes/cbndata/information.ts b/lib/routes/cbndata/information.ts index aad1a06eb853..0cbcbb6f2f88 100644 --- a/lib/routes/cbndata/information.ts +++ b/lib/routes/cbndata/information.ts @@ -197,8 +197,7 @@ export const route: Route = { | [美妆个护](https://www.cbndata.com/information?tag_id=1) | [1](https://rsshub.app/cbndata/information/1) | | [服饰鞋包](https://www.cbndata.com/information?tag_id=2559) | [2559](https://rsshub.app/cbndata/information/2559) | | [宠物](https://www.cbndata.com/information?tag_id=2419) | [2419](https://rsshub.app/cbndata/information/2419) | -| [营销](https://www.cbndata.com/information?tag_id=2484) | [2484](https://rsshub.app/cbndata/information/2484) | -`, +| [营销](https://www.cbndata.com/information?tag_id=2484) | [2484](https://rsshub.app/cbndata/information/2484) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/cbpanet/index.ts b/lib/routes/cbpanet/index.ts index 46b080bae408..7aa617d5f230 100644 --- a/lib/routes/cbpanet/index.ts +++ b/lib/routes/cbpanet/index.ts @@ -90,7 +90,7 @@ export const route: Route = { smallId: '子分类 id,默认为 `11`,即行业资讯,可在对应分类页 URL 中找到', }, description: `::: tip - 若订阅 [行业资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=11),网址为 \`https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=11\`。截取 \`https://www.cbpanet.com/\` 的 \`bigid\` 和 \`smallid\` 的部分作为参数填入,此时路由为 [\`/cbpanet/dzp_news/4/15\`](https://rsshub.app/cbpanet/dzp_news/4/15)。 +若订阅 [行业资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=11),网址为 \`https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=11\`。截取 \`https://www.cbpanet.com/\` 的 \`bigid\` 和 \`smallid\` 的部分作为参数填入,此时路由为 [\`/cbpanet/dzp_news/4/15\`](https://rsshub.app/cbpanet/dzp_news/4/15)。 :::
    @@ -98,52 +98,51 @@ export const route: Route = { #### [协会](https://www.cbpanet.com/dzp_xiehui.aspx) -| [协会介绍](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=1) | [协会章程](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=2) | [理事会](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=3) | [内设机构](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=4) | [协会通知](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=5) | [协会活动](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=6) | -| ------------------------------------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ | -| [1/1](https://rsshub.app/cbpanet/dzp_news/1/1) | [1/2](https://rsshub.app/cbpanet/dzp_news/1/2) | [1/3](https://rsshub.app/cbpanet/dzp_news/1/3) | [1/4](https://rsshub.app/cbpanet/dzp_news/1/4) | [1/5](https://rsshub.app/cbpanet/dzp_news/1/5) | [1/6](https://rsshub.app/cbpanet/dzp_news/1/6) | +| [协会介绍](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=1) | [协会章程](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=2) | [理事会](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=3) | [内设机构](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=4) | [协会通知](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=5) | [协会活动](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=6) | +| -------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| [1/1](https://rsshub.app/cbpanet/dzp_news/1/1) | [1/2](https://rsshub.app/cbpanet/dzp_news/1/2) | [1/3](https://rsshub.app/cbpanet/dzp_news/1/3) | [1/4](https://rsshub.app/cbpanet/dzp_news/1/4) | [1/5](https://rsshub.app/cbpanet/dzp_news/1/5) | [1/6](https://rsshub.app/cbpanet/dzp_news/1/6) | -| [出版物](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=7) | [会员权利与义务](https://www.cbpanet.com/dzp_news.aspx?bigid=1&smallid=30) | -| ---------------------------------------------------------------- | ------------------------------------------------------------------------- | -| [1/7](https://rsshub.app/cbpanet/dzp_news/1/7) | [1/30](https://rsshub.app/cbpanet/dzp_news/1/30) | +| [出版物](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=7) | [会员权利与义务](https://www.cbpanet.com/dzp_news.aspx?bigid=1\\&smallid=30) | +| ------------------------------------------------------------------ | --------------------------------------------------------------------------- | +| [1/7](https://rsshub.app/cbpanet/dzp_news/1/7) | [1/30](https://rsshub.app/cbpanet/dzp_news/1/30) | #### [行业资讯](https://www.cbpanet.com/dzp_news_list.aspx) -| [国内资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=8) | [海外资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=9) | [企业新闻](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=10) | [行业资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=11) | [热点聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=43) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=2&smallid=44) | -| ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [2/8](https://rsshub.app/cbpanet/dzp_news/2/8) | [2/9](https://rsshub.app/cbpanet/dzp_news/2/9) | [2/10](https://rsshub.app/cbpanet/dzp_news/2/10) | [2/11](https://rsshub.app/cbpanet/dzp_news/2/11) | [2/43](https://rsshub.app/cbpanet/dzp_news/2/43) | [2/44](https://rsshub.app/cbpanet/dzp_news/2/44) | +| [国内资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=8) | [海外资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=9) | [企业新闻](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=10) | [行业资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=11) | [热点聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=43) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=2\\&smallid=44) | +| -------------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [2/8](https://rsshub.app/cbpanet/dzp_news/2/8) | [2/9](https://rsshub.app/cbpanet/dzp_news/2/9) | [2/10](https://rsshub.app/cbpanet/dzp_news/2/10) | [2/11](https://rsshub.app/cbpanet/dzp_news/2/11) | [2/43](https://rsshub.app/cbpanet/dzp_news/2/43) | [2/44](https://rsshub.app/cbpanet/dzp_news/2/44) | #### [原料信息](https://www.cbpanet.com/dzp_yuanliao.aspx) -| [价格行情](https://www.cbpanet.com/dzp_news.aspx?bigid=3&smallid=12) | [分析预测](https://www.cbpanet.com/dzp_news.aspx?bigid=3&smallid=13) | [原料信息](https://www.cbpanet.com/dzp_news.aspx?bigid=3&smallid=40) | [热点聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=3&smallid=45) | -| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [3/12](https://rsshub.app/cbpanet/dzp_news/3/12) | [3/13](https://rsshub.app/cbpanet/dzp_news/3/13) | [3/40](https://rsshub.app/cbpanet/dzp_news/3/40) | [3/45](https://rsshub.app/cbpanet/dzp_news/3/45) | +| [价格行情](https://www.cbpanet.com/dzp_news.aspx?bigid=3\\&smallid=12) | [分析预测](https://www.cbpanet.com/dzp_news.aspx?bigid=3\\&smallid=13) | [原料信息](https://www.cbpanet.com/dzp_news.aspx?bigid=3\\&smallid=40) | [热点聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=3\\&smallid=45) | +| --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [3/12](https://rsshub.app/cbpanet/dzp_news/3/12) | [3/13](https://rsshub.app/cbpanet/dzp_news/3/13) | [3/40](https://rsshub.app/cbpanet/dzp_news/3/40) | [3/45](https://rsshub.app/cbpanet/dzp_news/3/45) | #### [法规标准](https://www.cbpanet.com/dzp_fagui.aspx) -| [法规资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=15) | [法律法规](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=16) | [国内标准](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=14) | [国外标准](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=17) | [法规聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=46) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=4&smallid=47) | -| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [4/15](https://rsshub.app/cbpanet/dzp_news/4/15) | [4/16](https://rsshub.app/cbpanet/dzp_news/4/16) | [4/14](https://rsshub.app/cbpanet/dzp_news/4/14) | [4/17](https://rsshub.app/cbpanet/dzp_news/4/17) | [4/46](https://rsshub.app/cbpanet/dzp_news/4/46) | [4/47](https://rsshub.app/cbpanet/dzp_news/4/47) | +| [法规资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=15) | [法律法规](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=16) | [国内标准](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=14) | [国外标准](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=17) | [法规聚焦](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=46) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=4\\&smallid=47) | +| --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [4/15](https://rsshub.app/cbpanet/dzp_news/4/15) | [4/16](https://rsshub.app/cbpanet/dzp_news/4/16) | [4/14](https://rsshub.app/cbpanet/dzp_news/4/14) | [4/17](https://rsshub.app/cbpanet/dzp_news/4/17) | [4/46](https://rsshub.app/cbpanet/dzp_news/4/46) | [4/47](https://rsshub.app/cbpanet/dzp_news/4/47) | #### [技术专区](https://www.cbpanet.com/dzp_jishu.aspx) -| [产品介绍](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=18) | [科技成果](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=19) | [学术论文](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=20) | [资料下载](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=21) | [专家](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=50) | [民间智库](https://www.cbpanet.com/dzp_news.aspx?bigid=5&smallid=57) | -| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------- | -| [5/18](https://rsshub.app/cbpanet/dzp_news/5/18) | [5/19](https://rsshub.app/cbpanet/dzp_news/5/19) | [5/20](https://rsshub.app/cbpanet/dzp_news/5/20) | [5/21](https://rsshub.app/cbpanet/dzp_news/5/21) | [5/50](https://rsshub.app/cbpanet/dzp_news/5/50) | [5/57](https://rsshub.app/cbpanet/dzp_news/5/57) | +| [产品介绍](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=18) | [科技成果](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=19) | [学术论文](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=20) | [资料下载](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=21) | [专家](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=50) | [民间智库](https://www.cbpanet.com/dzp_news.aspx?bigid=5\\&smallid=57) | +| --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------- | +| [5/18](https://rsshub.app/cbpanet/dzp_news/5/18) | [5/19](https://rsshub.app/cbpanet/dzp_news/5/19) | [5/20](https://rsshub.app/cbpanet/dzp_news/5/20) | [5/21](https://rsshub.app/cbpanet/dzp_news/5/21) | [5/50](https://rsshub.app/cbpanet/dzp_news/5/50) | [5/57](https://rsshub.app/cbpanet/dzp_news/5/57) | #### [豆制品消费指南](https://www.cbpanet.com/dzp_zhinan.aspx) -| [膳食指南](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=22) | [营养成分](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=23) | [豆食菜谱](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=24) | [问与答](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=31) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=48) | [消费热点](https://www.cbpanet.com/dzp_news.aspx?bigid=6&smallid=53) | -| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [6/22](https://rsshub.app/cbpanet/dzp_news/6/22) | [6/23](https://rsshub.app/cbpanet/dzp_news/6/23) | [6/24](https://rsshub.app/cbpanet/dzp_news/6/24) | [6/31](https://rsshub.app/cbpanet/dzp_news/6/31) | [6/48](https://rsshub.app/cbpanet/dzp_news/6/48) | [6/53](https://rsshub.app/cbpanet/dzp_news/6/53) | +| [膳食指南](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=22) | [营养成分](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=23) | [豆食菜谱](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=24) | [问与答](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=31) | [今日推荐](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=48) | [消费热点](https://www.cbpanet.com/dzp_news.aspx?bigid=6\\&smallid=53) | +| --------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [6/22](https://rsshub.app/cbpanet/dzp_news/6/22) | [6/23](https://rsshub.app/cbpanet/dzp_news/6/23) | [6/24](https://rsshub.app/cbpanet/dzp_news/6/24) | [6/31](https://rsshub.app/cbpanet/dzp_news/6/31) | [6/48](https://rsshub.app/cbpanet/dzp_news/6/48) | [6/53](https://rsshub.app/cbpanet/dzp_news/6/53) | #### [营养与健康](https://www.cbpanet.com/dzp_yingyang.aspx) -| [大豆营养概况](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=25) | [大豆食品和人类健康](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=26) | [世界豆类日,爱豆大行动](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=27) | [谣言粉碎机](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=29) | [最新资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=41) | [专家视点](https://www.cbpanet.com/dzp_news.aspx?bigid=7&smallid=49) | -| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | -| [7/25](https://rsshub.app/cbpanet/dzp_news/7/25) | [7/26](https://rsshub.app/cbpanet/dzp_news/7/26) | [7/27](https://rsshub.app/cbpanet/dzp_news/7/27) | [7/29](https://rsshub.app/cbpanet/dzp_news/7/29) | [7/41](https://rsshub.app/cbpanet/dzp_news/7/41) | [7/49](https://rsshub.app/cbpanet/dzp_news/7/49) | +| [大豆营养概况](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=25) | [大豆食品和人类健康](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=26) | [世界豆类日,爱豆大行动](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=27) | [谣言粉碎机](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=29) | [最新资讯](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=41) | [专家视点](https://www.cbpanet.com/dzp_news.aspx?bigid=7\\&smallid=49) | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [7/25](https://rsshub.app/cbpanet/dzp_news/7/25) | [7/26](https://rsshub.app/cbpanet/dzp_news/7/26) | [7/27](https://rsshub.app/cbpanet/dzp_news/7/27) | [7/29](https://rsshub.app/cbpanet/dzp_news/7/29) | [7/41](https://rsshub.app/cbpanet/dzp_news/7/41) | [7/49](https://rsshub.app/cbpanet/dzp_news/7/49) | -
    - `, +`, categories: ['new-media'], features: { diff --git a/lib/routes/ccac/news.ts b/lib/routes/ccac/news.ts index 8af1f2ff98c6..cf893ee8e771 100644 --- a/lib/routes/ccac/news.ts +++ b/lib/routes/ccac/news.ts @@ -4,7 +4,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import utils from './utils'; @@ -32,7 +32,7 @@ export const route: Route = { }; async function handler(ctx) { - const browser = await puppeteer(); + const browser = await playwright(); const lang = ctx.req.param('lang') ?? 'sc'; const type = utils.TYPE[ctx.req.param('type')]; diff --git a/lib/routes/ccagm/index.ts b/lib/routes/ccagm/index.ts index 1eaf3d35ecbc..62f3bd55aa11 100644 --- a/lib/routes/ccagm/index.ts +++ b/lib/routes/ccagm/index.ts @@ -145,28 +145,27 @@ export const route: Route = { ], }, }, - description: `:::tip + description: `::: tip 订阅 [协会动态](http://www.ccagm.org.cn/association-news),其源网址为 \`http://www.ccagm.org.cn/association-news\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/ccagm/association-news\`](https://rsshub.app/ccagm/association-news)。 :::
    更多分类 - | 栏目 | ID | - | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | - | [协会动态](http://www.ccagm.org.cn/association-news.html) | [association-news](https://rsshub.app/ccagm/association-news) | - | [会议活动](http://www.ccagm.org.cn/xh-activity/activities-huiyi.html) | [xh-activity/activities-huiyi](https://rsshub.app/ccagm/xh-activity/activities-huiyi) | - | [调研与报告](http://www.ccagm.org.cn/xh-activity/bg-yj.html) | [xh-activity/bg-yj](https://rsshub.app/ccagm/xh-activity/bg-yj) | - | [协会党建](http://www.ccagm.org.cn/xie-hui-dang-jian.html) | [xie-hui-dang-jian](https://rsshub.app/ccagm/xie-hui-dang-jian) | - | [行业新闻](http://www.ccagm.org.cn/members-info.html) | [members-info](https://rsshub.app/ccagm/members-info) | - | [行业研究](http://www.ccagm.org.cn/bg-yj.html) | [bg-yj](https://rsshub.app/ccagm/bg-yj) | - | [行业标准](http://www.ccagm.org.cn/industry-policy/industry-standard.html) | [industry-policy/industry-standard](https://rsshub.app/ccagm/industry-policy/industry-standard) | - | [法律法规](http://www.ccagm.org.cn/industry-policy/policies-regulations.html) | [industry-policy/policies-regulations](https://rsshub.app/ccagm/industry-policy/policies-regulations) | - | [资料下载](http://www.ccagm.org.cn/download.html) | [download](https://rsshub.app/ccagm/download) | - | [工作总结与计划](http://www.ccagm.org.cn/about-association/gong-zuo-zong-jie-yu-ji-hua.html) | [about-association/gong-zuo-zong-jie-yu-ji-hua](https://rsshub.app/ccagm/about-association/gong-zuo-zong-jie-yu-ji-hua) | - -
    -`, +| 栏目 | ID | +| -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| [协会动态](http://www.ccagm.org.cn/association-news.html) | [association-news](https://rsshub.app/ccagm/association-news) | +| [会议活动](http://www.ccagm.org.cn/xh-activity/activities-huiyi.html) | [xh-activity/activities-huiyi](https://rsshub.app/ccagm/xh-activity/activities-huiyi) | +| [调研与报告](http://www.ccagm.org.cn/xh-activity/bg-yj.html) | [xh-activity/bg-yj](https://rsshub.app/ccagm/xh-activity/bg-yj) | +| [协会党建](http://www.ccagm.org.cn/xie-hui-dang-jian.html) | [xie-hui-dang-jian](https://rsshub.app/ccagm/xie-hui-dang-jian) | +| [行业新闻](http://www.ccagm.org.cn/members-info.html) | [members-info](https://rsshub.app/ccagm/members-info) | +| [行业研究](http://www.ccagm.org.cn/bg-yj.html) | [bg-yj](https://rsshub.app/ccagm/bg-yj) | +| [行业标准](http://www.ccagm.org.cn/industry-policy/industry-standard.html) | [industry-policy/industry-standard](https://rsshub.app/ccagm/industry-policy/industry-standard) | +| [法律法规](http://www.ccagm.org.cn/industry-policy/policies-regulations.html) | [industry-policy/policies-regulations](https://rsshub.app/ccagm/industry-policy/policies-regulations) | +| [资料下载](http://www.ccagm.org.cn/download.html) | [download](https://rsshub.app/ccagm/download) | +| [工作总结与计划](http://www.ccagm.org.cn/about-association/gong-zuo-zong-jie-yu-ji-hua.html) | [about-association/gong-zuo-zong-jie-yu-ji-hua](https://rsshub.app/ccagm/about-association/gong-zuo-zong-jie-yu-ji-hua) | + +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/cccfna/index.ts b/lib/routes/cccfna/index.ts index 3d2b33d1d9bc..0ff78618e9d9 100644 --- a/lib/routes/cccfna/index.ts +++ b/lib/routes/cccfna/index.ts @@ -19,8 +19,7 @@ export const route: Route = { source: ['www.cccfna.org.cn/:category/:type?'], }, ], - description: ` -::: tip + description: `::: tip 存在**二级分类**的**一级分类**不能单独当作参数,如:\`/cccfna/hangyezixun\` ::: diff --git a/lib/routes/cccmc/index.ts b/lib/routes/cccmc/index.ts index 92441a0bb57d..2f5354f8afc3 100644 --- a/lib/routes/cccmc/index.ts +++ b/lib/routes/cccmc/index.ts @@ -152,8 +152,8 @@ export const route: Route = { | [党群动态](https://www.cccmc.org.cn/shdj/dqdt/) | [党内法规](https://www.cccmc.org.cn/shdj/dnfg/) | [青年工作](https://www.cccmc.org.cn/shdj/qngz/) | | ----------------------------------------------- | ----------------------------------------------- | ----------------------------------------------- | | [shdj/dqdt](https://rsshub.app/cccmc/shdj/dqdt) | [shdj/dnfg](https://rsshub.app/cccmc/shdj/dnfg) | [shdj/qngz](https://rsshub.app/cccmc/shdj/qngz) | - -`, + +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/ccf/news.ts b/lib/routes/ccf/news.ts index 99f634eb940a..74a1b2538a90 100644 --- a/lib/routes/ccf/news.ts +++ b/lib/routes/ccf/news.ts @@ -29,7 +29,7 @@ export const route: Route = { handler, description: `| CCF 新闻 | CCF 聚焦 | ACM 信息 | | ----------- | -------- | --------- | -| Media_list | Focus | ACM_News |`, +| Media\\_list | Focus | ACM\\_News |`, }; async function handler(ctx) { diff --git a/lib/routes/ccfa/index.tsx b/lib/routes/ccfa/index.tsx index b90b202f7df4..a497182bee3a 100644 --- a/lib/routes/ccfa/index.tsx +++ b/lib/routes/ccfa/index.tsx @@ -110,28 +110,27 @@ export const route: Route = { example: '/ccfa/1', parameters: { category: '分类,默认为 `1`,即协会动态,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [协会动态](https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1),网址为 \`https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1\`。截取 \`https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=\` 到末尾的部分 \`1\` 作为参数填入,此时路由为 [\`/ccfa/1\`](https://rsshub.app/ccfa/1)。 +若订阅 [协会动态](https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1),网址为 \`https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1\`。截取 \`https://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=\` 到末尾的部分 \`1\` 作为参数填入,此时路由为 [\`/ccfa/1\`](https://rsshub.app/ccfa/1)。 ::: -| 分类 | ID | -| ------------------------------------------------------------------------- | -------------------------------------- | -| [协会动态](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1) | [1](https://rsshub.app/ccfa/1) | -| [行业动态](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=2) | [2](https://rsshub.app/ccfa/2) | -| [政策/报告/标准](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=33) | [33](https://rsshub.app/ccfa/33) | -| [行业统计](http://www.ccfa.org.cn/portal/cn/lsbq.jsp?type=10003) | [10003](https://rsshub.app/ccfa/10003) | -| [创新案例](http://www.ccfa.org.cn/portal/cn/hybzs_list.jsp?type=10004) | [10004](https://rsshub.app/ccfa/10004) | -| [党建工作](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=7) | [7](https://rsshub.app/ccfa/7) | -| [新消费论坛](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=10005) | [10005](https://rsshub.app/ccfa/10005) | +| 分类 | ID | +| ---------------------------------------------------------------------------- | -------------------------------------- | +| [协会动态](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=1) | [1](https://rsshub.app/ccfa/1) | +| [行业动态](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=2) | [2](https://rsshub.app/ccfa/2) | +| [政策 / 报告 / 标准](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=33) | [33](https://rsshub.app/ccfa/33) | +| [行业统计](http://www.ccfa.org.cn/portal/cn/lsbq.jsp?type=10003) | [10003](https://rsshub.app/ccfa/10003) | +| [创新案例](http://www.ccfa.org.cn/portal/cn/hybzs_list.jsp?type=10004) | [10004](https://rsshub.app/ccfa/10004) | +| [党建工作](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=7) | [7](https://rsshub.app/ccfa/7) | +| [新消费论坛](http://www.ccfa.org.cn/portal/cn/xiehui_list.jsp?type=10005) | [10005](https://rsshub.app/ccfa/10005) | -#### [政策/报告/标准](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=33) +#### [政策 / 报告 / 标准](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=33) | 分类 | ID | | ------------------------------------------------------------------------------- | -------------------------------- | | [行业报告](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=33) | [33](https://rsshub.app/ccfa/33) | | [行业标准](http://www.ccfa.org.cn/portal/cn/hybz_list.jsp?type=34) | [34](https://rsshub.app/ccfa/34) | | [行业政策](http://www.ccfa.org.cn/portal/cn/fangyizhuanqu_list.jsp?type=39) | [39](https://rsshub.app/ccfa/39) | -| [政策权威解读](http://www.ccfa.org.cn/portal/cn/fangyizhuanqu_list.jsp?type=40) | [40](https://rsshub.app/ccfa/40) | - `, +| [政策权威解读](http://www.ccfa.org.cn/portal/cn/fangyizhuanqu_list.jsp?type=40) | [40](https://rsshub.app/ccfa/40) |`, categories: ['new-media'], features: { diff --git a/lib/routes/ccg/index.ts b/lib/routes/ccg/index.ts index 0594be88795f..d53458f64e56 100644 --- a/lib/routes/ccg/index.ts +++ b/lib/routes/ccg/index.ts @@ -148,8 +148,7 @@ export const route: Route = { | 分类 | ID | | -------------------------------------- | ----------------------------------- | | [新闻动态](http://www.ccg.org.cn/news) | [news](https://rsshub.app/ccg/news) | -| [媒体报道](http://www.ccg.org.cn/mtbd) | [mtbd](https://rsshub.app/ccg/mtbd) | -`, +| [媒体报道](http://www.ccg.org.cn/mtbd) | [mtbd](https://rsshub.app/ccg/mtbd) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/cctv/lm.ts b/lib/routes/cctv/lm.ts index a1af830997c4..f3b856f32f99 100644 --- a/lib/routes/cctv/lm.ts +++ b/lib/routes/cctv/lm.ts @@ -37,7 +37,7 @@ export const route: Route = { | zdzy | jjbxs | dydhly | ::: tip - 更多栏目请看 [这里](https://tv.cctv.com/lm) +更多栏目请看 [这里](https://tv.cctv.com/lm) :::`, }; diff --git a/lib/routes/cctv/xwlb.ts b/lib/routes/cctv/xwlb.ts index 9d8856f6b01b..c22da35122fe 100644 --- a/lib/routes/cctv/xwlb.ts +++ b/lib/routes/cctv/xwlb.ts @@ -44,7 +44,7 @@ export const route: Route = { maintainers: ['zengxs'], handler, url: 'tv.cctv.com/lm/xwlb', - description: `新闻联播内容摘要。`, + description: '新闻联播内容摘要。', }; async function handler(ctx) { diff --git a/lib/routes/cde/index.ts b/lib/routes/cde/index.ts index 2d342a48a9ee..b6d263089e06 100644 --- a/lib/routes/cde/index.ts +++ b/lib/routes/cde/index.ts @@ -94,13 +94,13 @@ export const route: Route = { name: '首页', maintainers: ['Fatpandac'], handler, - description: `- 频道 + description: `- 频道 | 新闻中心 | 政策法规 | | :------: | :------: | | news | policy | - - 类别 +- 类别 | 新闻中心 | 政务新闻 | 要闻导读 | 图片新闻 | 工作动态 | | :------: | :------: | :------: | :------: | :------: | diff --git a/lib/routes/ce/district.ts b/lib/routes/ce/district.ts index 0d26e3a19a9f..220715b565da 100644 --- a/lib/routes/ce/district.ts +++ b/lib/routes/ce/district.ts @@ -16,7 +16,7 @@ export const route: Route = { example: '/ce/district', parameters: { category: '栏目标识,默认为 roll(即时新闻)' }, description: `| 即时新闻 | 经济动态 | 独家视角 | 专题 | 数说地方 | 地方播报 | 专稿 | 港澳台 | -|----------|----------|----------|------|----------|----------|------|--------| +| -------- | -------- | -------- | ---- | -------- | -------- | ---- | ------ | | roll | jjdt | poll | ch | ssdf | dfbb | zg | gat |`, categories: ['traditional-media'], features: { diff --git a/lib/routes/cebbank/history.tsx b/lib/routes/cebbank/history.tsx index c80a095b57ed..a04ae82b1094 100644 --- a/lib/routes/cebbank/history.tsx +++ b/lib/routes/cebbank/history.tsx @@ -26,7 +26,6 @@ export const route: Route = { handler, description: `#### 总览 {#zhong-guo-guang-da-yin-hang-wai-hui-pai-jia-zong-lan} - #### 历史牌价 {#zhong-guo-guang-da-yin-hang-wai-hui-pai-jia-li-shi-pai-jia} | 美元 | 英镑 | 港币 | 瑞士法郎 | 瑞典克郎 | 丹麦克郎 | 挪威克郎 | 日元 | 加拿大元 | 澳大利亚元 | 新加坡元 | 欧元 | 澳门元 | 泰国铢 | 新西兰元 | 韩圆 | diff --git a/lib/routes/cgtn/podcast.ts b/lib/routes/cgtn/podcast.ts index d43adfb85f32..4354e79f2380 100644 --- a/lib/routes/cgtn/podcast.ts +++ b/lib/routes/cgtn/podcast.ts @@ -36,7 +36,7 @@ export const route: Route = { maintainers: ['5upernova-heng'], handler, description: `> 类型名与播客 id 可以在播客对应的 URL 中找到 - > 如 URL \`https://radio.cgtn.com/podcast/column/ezfm/More-to-Read/4\` ,其 \`category\` 为 \`ezfm\` ,\`id\` 为 \`4\`,对应的订阅路由为 [\`/podcast/ezfm/4\`](https://rsshub.app/podcast/ezfm/4)`, +> 如 URL \`https://radio.cgtn.com/podcast/column/ezfm/More-to-Read/4\` ,其 \`category\` 为 \`ezfm\` ,\`id\` 为 \`4\`,对应的订阅路由为 [\`/podcast/ezfm/4\`](https://rsshub.app/podcast/ezfm/4)`, }; async function handler(ctx) { diff --git a/lib/routes/chaoxing/qk.tsx b/lib/routes/chaoxing/qk.tsx index 18e962e13fba..12fa9f23444b 100644 --- a/lib/routes/chaoxing/qk.tsx +++ b/lib/routes/chaoxing/qk.tsx @@ -23,17 +23,17 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 全部期刊可以在 [这里](http://qk.chaoxing.com/space/index) 找到,你也可以从 [学科分类](https://qikan.chaoxing.com/jourclassify) 和 [期刊导航](https://qikan.chaoxing.com/search/openmag) 中发现更多期刊。 +全部期刊可以在 [这里](http://qk.chaoxing.com/space/index) 找到,你也可以从 [学科分类](https://qikan.chaoxing.com/jourclassify) 和 [期刊导航](https://qikan.chaoxing.com/search/openmag) 中发现更多期刊。 - 如订阅 [**上海文艺**](http://m.chaoxing.com/mqk/list?sw=&mags=6b5c39b3dd84352be512e29df0297437&isort=20&from=space),其 URL 为 \`http://m.chaoxing.com/mqk/list?mags=6b5c39b3dd84352be512e29df0297437\`。\`6b5c39b3dd84352be512e29df0297437\` 即为期刊 id,所得路由为 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437) +如订阅 [**上海文艺**](http://m.chaoxing.com/mqk/list?sw=\\&mags=6b5c39b3dd84352be512e29df0297437\\&isort=20\\&from=space),其 URL 为 \`http://m.chaoxing.com/mqk/list?mags=6b5c39b3dd84352be512e29df0297437\`。\`6b5c39b3dd84352be512e29df0297437\` 即为期刊 id,所得路由为 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437) ::: ::: warning - 你可以设置参数 **需要获取文章全文** 为 \`true\` \`yes\` \`t\` \`y\` 等值(或者忽略这个参数),RSS 的条目会携带期刊中的 **文章全文**,而不仅仅是 **文章概要**。但因为发起访问请求过多会被该网站屏蔽,你可以将其关闭(设置该参数为 \`false\` \`no\` \`f\` \`n\` 等值),这将会大大减少请求次数从而更难触发网站的反爬机制。 +你可以设置参数 **需要获取文章全文** 为 \`true\` \`yes\` \`t\` \`y\` 等值(或者忽略这个参数),RSS 的条目会携带期刊中的 **文章全文**,而不仅仅是 **文章概要**。但因为发起访问请求过多会被该网站屏蔽,你可以将其关闭(设置该参数为 \`false\` \`no\` \`f\` \`n\` 等值),这将会大大减少请求次数从而更难触发网站的反爬机制。 - 路由默认会获取 **30** 个条目。在路由后指定 \`?limit=<条目数量>\` 减少或增加单次获取条目数量,同样可以减少请求次数,如设置为一次获取 **10** 个条目,路由可以更改为 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437?limit=10\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437?limit=10) +路由默认会获取 **30** 个条目。在路由后指定 \`?limit=<条目数量>\` 减少或增加单次获取条目数量,同样可以减少请求次数,如设置为一次获取 **10** 个条目,路由可以更改为 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437?limit=10\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437?limit=10) - 在根据上文设置 **需要获取文章全文** 为不需要时,你可以将 \`limit\` 值增大,从而获取更多的条目,此时因为不获取全文也不会触发反爬机制,如 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437/false?limit=100\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437/false?limit=100) +在根据上文设置 **需要获取文章全文** 为不需要时,你可以将 \`limit\` 值增大,从而获取更多的条目,此时因为不获取全文也不会触发反爬机制,如 [\`/chaoxing/qk/6b5c39b3dd84352be512e29df0297437/false?limit=100\`](https://rsshub.app/chaoxing/qk/6b5c39b3dd84352be512e29df0297437/false?limit=100) :::`, }; diff --git a/lib/routes/chaping/banner.ts b/lib/routes/chaping/banner.ts index e26ecb272c83..662a3c1626b7 100644 --- a/lib/routes/chaping/banner.ts +++ b/lib/routes/chaping/banner.ts @@ -28,7 +28,7 @@ export const route: Route = { }; async function handler() { - const rootUrl = `https://chaping.cn/`; + const rootUrl = 'https://chaping.cn/'; const response = await got({ method: 'get', url: rootUrl, diff --git a/lib/routes/chikubi/navigation.ts b/lib/routes/chikubi/navigation.ts index 68b8f0ec40bd..b4304a5b222c 100644 --- a/lib/routes/chikubi/navigation.ts +++ b/lib/routes/chikubi/navigation.ts @@ -20,9 +20,9 @@ export const route: Route = { name: 'Navigation', maintainers: ['SnowAgar25'], handler, - description: `| 殿堂 | 動畫 | VR | 漫畫 | 音聲 | CG・イラスト | -| ---- | ----- | -- | ----- | ----- | -- | -| best | video | vr | comic | voice | cg |`, + description: `| 殿堂 | 動畫 | VR | 漫畫 | 音聲 | CG・イラスト | +| ---- | ----- | -- | ----- | ----- | ------------ | +| best | video | vr | comic | voice | cg |`, }; const navigationItems = { diff --git a/lib/routes/china/finance/finance.ts b/lib/routes/china/finance/finance.ts index aa1e3b258da9..014043c5648b 100644 --- a/lib/routes/china/finance/finance.ts +++ b/lib/routes/china/finance/finance.ts @@ -42,9 +42,9 @@ export const route: Route = { | ------- | --- | ------- | ------ | ------- | ----- | ---- | -------- | | tuijian | TMT | jinrong | dichan | xiaofei | yiyao | wine | IPO | - > Note: The default news num is \`30\`. +> Note: The default news num is \`30\`. - > 注意:默认新闻条数是 \`30\`。`, +> 注意:默认新闻条数是 \`30\`。`, }; async function handler(ctx) { diff --git a/lib/routes/chinacdc/index.ts b/lib/routes/chinacdc/index.ts index 35216afb3776..5bf1a2d60d5d 100644 --- a/lib/routes/chinacdc/index.ts +++ b/lib/routes/chinacdc/index.ts @@ -150,9 +150,9 @@ export const route: Route = { #### [党建园地](https://www.chinacdc.cn/dqgz/djgz/) -| [党建工作](https://www.chinacdc.cn/dqgz/djgz/) | [廉政文化](https://www.chinacdc.cn/djgz_13611/) | [工会工作](https://www.chinacdc.cn/ghgz/) | [团青工作](https://www.chinacdc.cn/tqgz/) | [理论学习](https://www.chinacdc.cn/tqgz_13618/) | -| -------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------- | -| [dqgz/djgz](https://rsshub.app/chinacdc/dqgz/djgz) | [dqgz/djgz_13611](https://rsshub.app/chinacdc/dqgz/djgz_13611) | [dqgz/ghgz](https://rsshub.app/chinacdc/dqgz/ghgz) | [dqgz/tqgz](https://rsshub.app/chinacdc/dqgz/tqgz) | [dqgz/tqgz_13618](https://rsshub.app/chinacdc/dqgz/tqgz_13618) | +| [党建工作](https://www.chinacdc.cn/dqgz/djgz/) | [廉政文化](https://www.chinacdc.cn/djgz_13611/) | [工会工作](https://www.chinacdc.cn/ghgz/) | [团青工作](https://www.chinacdc.cn/tqgz/) | [理论学习](https://www.chinacdc.cn/tqgz_13618/) | +| -------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------- | +| [dqgz/djgz](https://rsshub.app/chinacdc/dqgz/djgz) | [dqgz/djgz\\_13611](https://rsshub.app/chinacdc/dqgz/djgz_13611) | [dqgz/ghgz](https://rsshub.app/chinacdc/dqgz/ghgz) | [dqgz/tqgz](https://rsshub.app/chinacdc/dqgz/tqgz) | [dqgz/tqgz\\_13618](https://rsshub.app/chinacdc/dqgz/tqgz_13618) | #### [疾控应急](https://www.chinacdc.cn/jkyj/) @@ -183,8 +183,8 @@ export const route: Route = { #### [全球公卫](https://www.chinacdc.cn/qqgw/) | [合作伙伴](https://www.chinacdc.cn/qqgw/hzhb/) | [世界卫生组织合作中心和参比实验室](https://www.chinacdc.cn/qqgw/wszz/) | [国际交流 (港澳台交流)](https://www.chinacdc.cn/qqgw/gjjl/) | [公共卫生援外与合作](https://www.chinacdc.cn/qqgw/ggws/) | -| -------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------- | -| [qqgw/hzhb](https://rsshub.app/chinacdc/qqgw/hzhb) | [qqgw/wszz](https://rsshub.app/chinacdc/qqgw/wszz) | [qqgw/gjjl](https://rsshub.app/chinacdc/qqgw/gjjl) | [qqgw/ggws](https://rsshub.app/chinacdc/qqgw/ggws) | +| -------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------- | +| [qqgw/hzhb](https://rsshub.app/chinacdc/qqgw/hzhb) | [qqgw/wszz](https://rsshub.app/chinacdc/qqgw/wszz) | [qqgw/gjjl](https://rsshub.app/chinacdc/qqgw/gjjl) | [qqgw/ggws](https://rsshub.app/chinacdc/qqgw/ggws) | #### [人才建设](https://www.chinacdc.cn/rcjs/) @@ -196,11 +196,11 @@ export const route: Route = { | [全国法定传染病疫情情况](https://www.chinacdc.cn/jksj/jksj01/) | [全国新型冠状病毒感染疫情情况](https://www.chinacdc.cn/jksj/xgbdyq/) | [重点传染病和突发公共卫生事件风险评估报告](https://www.chinacdc.cn/jksj/jksj02/) | [全球传染病事件风险评估报告](https://www.chinacdc.cn/jksj/jksj03/) | [全国预防接种异常反应监测信息概况](https://www.chinacdc.cn/jksj/jksj04_14209/) | | -------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| [jksj/jksj01](https://rsshub.app/chinacdc/jksj/jksj01) | [jksj/xgbdyq](https://rsshub.app/chinacdc/jksj/xgbdyq) | [jksj/jksj02](https://rsshub.app/chinacdc/jksj/jksj02) | [jksj/jksj03](https://rsshub.app/chinacdc/jksj/jksj03) | [jksj/jksj04_14209](https://rsshub.app/chinacdc/jksj/jksj04_14209) | +| [jksj/jksj01](https://rsshub.app/chinacdc/jksj/jksj01) | [jksj/xgbdyq](https://rsshub.app/chinacdc/jksj/xgbdyq) | [jksj/jksj02](https://rsshub.app/chinacdc/jksj/jksj02) | [jksj/jksj03](https://rsshub.app/chinacdc/jksj/jksj03) | [jksj/jksj04\\_14209](https://rsshub.app/chinacdc/jksj/jksj04_14209) | -| [流感监测周报](https://www.chinacdc.cn/jksj/jksj04_14249/) | [全国急性呼吸道传染病哨点监测情况](https://www.chinacdc.cn/jksj/jksj04_14275/) | [健康报告](https://www.chinacdc.cn/jksj/jksj04/) | -| ------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------ | -| [jksj/jksj04_14249](https://rsshub.app/chinacdc/jksj/jksj04_14249) | [jksj/jksj04_14275](https://rsshub.app/chinacdc/jksj/jksj04_14275) | [jksj/jksj04](https://rsshub.app/chinacdc/jksj/jksj04) | +| [流感监测周报](https://www.chinacdc.cn/jksj/jksj04_14249/) | [全国急性呼吸道传染病哨点监测情况](https://www.chinacdc.cn/jksj/jksj04_14275/) | [健康报告](https://www.chinacdc.cn/jksj/jksj04/) | +| ------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------ | +| [jksj/jksj04\\_14249](https://rsshub.app/chinacdc/jksj/jksj04_14249) | [jksj/jksj04\\_14275](https://rsshub.app/chinacdc/jksj/jksj04_14275) | [jksj/jksj04](https://rsshub.app/chinacdc/jksj/jksj04) | #### [健康科普](https://www.chinacdc.cn/jkkp/) @@ -212,8 +212,7 @@ export const route: Route = { | -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------- | | [jkkp/yyjk](https://rsshub.app/chinacdc/jkkp/yyjk) | [jkkp/hjjk](https://rsshub.app/chinacdc/jkkp/hjjk) | [jkkp/zyjk](https://rsshub.app/chinacdc/jkkp/zyjk) | [jkkp/fsws](https://rsshub.app/chinacdc/jkkp/fsws) | - -`, +`, categories: ['government'], features: { requireConfig: false, diff --git a/lib/routes/chinadaily/language.ts b/lib/routes/chinadaily/language.ts index 47dc17388be0..dab02950b8a0 100644 --- a/lib/routes/chinadaily/language.ts +++ b/lib/routes/chinadaily/language.ts @@ -228,22 +228,21 @@ export const route: Route = {
    更多分类 -| 分类 | ID | -| ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| [精彩推荐](https://language.chinadaily.com.cn/thelatest) | [thelatest](https://rsshub.app/chinadaily/language/thelatest) | -| [每日一词](https://language.chinadaily.com.cn/news_hotwords/word_of_the_day) | [news_hotwords/word_of_the_day](https://rsshub.app/chinadaily/language/news_hotwords/word_of_the_day) | -| [双语新闻](https://language.chinadaily.com.cn/news_bilingual) | [news_bilingual](https://rsshub.app/chinadaily/language/news_bilingual) | -| [新闻热词](https://language.chinadaily.com.cn/news_hotwords) | [news_hotwords](https://rsshub.app/chinadaily/language/news_hotwords) | -| [实用口语](https://language.chinadaily.com.cn/practice_tongue) | [practice_tongue](https://rsshub.app/chinadaily/language/practice_tongue) | -| [译词课堂](https://language.chinadaily.com.cn/trans_collect) | [trans_collect](https://rsshub.app/chinadaily/language/trans_collect) | -| [图片新闻](https://language.chinadaily.com.cn/news_photo) | [news_photo](https://rsshub.app/chinadaily/language/news_photo) | -| [视频精选](https://language.chinadaily.com.cn/video_links) | [video_links](https://rsshub.app/chinadaily/language/video_links) | -| [新闻播报](https://language.chinadaily.com.cn/audio_cd) | [audio_cd](https://rsshub.app/chinadaily/language/audio_cd) | -| [专栏作家](https://language.chinadaily.com.cn/columnist) | [audio_cd](https://rsshub.app/chinadaily/language/columnist) | -| [权威发布](https://language.chinadaily.com.cn/5af95d44a3103f6866ee845c) | [5af95d44a3103f6866ee845c](https://rsshub.app/chinadaily/language/5af95d44a3103f6866ee845c) | +| 分类 | ID | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| [精彩推荐](https://language.chinadaily.com.cn/thelatest) | [thelatest](https://rsshub.app/chinadaily/language/thelatest) | +| [每日一词](https://language.chinadaily.com.cn/news_hotwords/word_of_the_day) | [news\\_hotwords/word\\_of\\_the\\_day](https://rsshub.app/chinadaily/language/news_hotwords/word_of_the_day) | +| [双语新闻](https://language.chinadaily.com.cn/news_bilingual) | [news\\_bilingual](https://rsshub.app/chinadaily/language/news_bilingual) | +| [新闻热词](https://language.chinadaily.com.cn/news_hotwords) | [news\\_hotwords](https://rsshub.app/chinadaily/language/news_hotwords) | +| [实用口语](https://language.chinadaily.com.cn/practice_tongue) | [practice\\_tongue](https://rsshub.app/chinadaily/language/practice_tongue) | +| [译词课堂](https://language.chinadaily.com.cn/trans_collect) | [trans\\_collect](https://rsshub.app/chinadaily/language/trans_collect) | +| [图片新闻](https://language.chinadaily.com.cn/news_photo) | [news\\_photo](https://rsshub.app/chinadaily/language/news_photo) | +| [视频精选](https://language.chinadaily.com.cn/video_links) | [video\\_links](https://rsshub.app/chinadaily/language/video_links) | +| [新闻播报](https://language.chinadaily.com.cn/audio_cd) | [audio\\_cd](https://rsshub.app/chinadaily/language/audio_cd) | +| [专栏作家](https://language.chinadaily.com.cn/columnist) | [audio\\_cd](https://rsshub.app/chinadaily/language/columnist) | +| [权威发布](https://language.chinadaily.com.cn/5af95d44a3103f6866ee845c) | [5af95d44a3103f6866ee845c](https://rsshub.app/chinadaily/language/5af95d44a3103f6866ee845c) | -
    -`, +`, categories: ['traditional-media'], features: { requireConfig: false, diff --git a/lib/routes/chinadegrees/province.tsx b/lib/routes/chinadegrees/province.tsx index 3638ed5f543c..e32c38d7da5a 100644 --- a/lib/routes/chinadegrees/province.tsx +++ b/lib/routes/chinadegrees/province.tsx @@ -5,7 +5,7 @@ import { config } from '@/config'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseUrl = 'http://www.chinadegrees.com.cn'; @@ -82,7 +82,7 @@ async function handler(ctx) { const data = await cache.tryGet( url, async () => { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/chinaisa/index.ts b/lib/routes/chinaisa/index.ts index 548079fa2b6a..1323ceaaaba3 100644 --- a/lib/routes/chinaisa/index.ts +++ b/lib/routes/chinaisa/index.ts @@ -21,8 +21,8 @@ export const route: Route = { name: '栏目', maintainers: ['nczitzk'], handler, - description: `| 栏目 | id | -| -------- | --------------------------------------------------------------- | + description: `| 栏目 | id | +| -------- | ---------------------------------------------------------------- | | 钢协动态 | 58af05dfb6b4300151760176d2aad0a04c275aaadbb1315039263f021f920dcd | | 钢协要闻 | 67ea4f106bd8f0843c0538d43833c463a0cd411fc35642cbd555a5f39fcf352b | | 会议报道 | e5070694f299a43b20d990e53b6a69dc02e755fef644ae667cf75deaff80407a | @@ -158,6 +158,7 @@ export const route: Route = { | 钢协刊物 | ed51af486f6d4b313b3aaf8fea0b32a4a2d4a89714c61992caf01942eb61831b | | 中国钢铁业 | 6440bdfccadf87908b13d8bbd9a66bb89bbd60cc5e175c018ca1c62c7d55e61f | | 钢铁信息 | 2b66af0b2cda9b420739e55e255a6f72f277557670ef861c9956da8fde25da05 | + `, }; diff --git a/lib/routes/chinamoney/notice.ts b/lib/routes/chinamoney/notice.ts index c524af19dfb8..df8c23369b11 100644 --- a/lib/routes/chinamoney/notice.ts +++ b/lib/routes/chinamoney/notice.ts @@ -27,33 +27,35 @@ export const route: Route = { description: `
    市场公告 - 外汇市场公告 +外汇市场公告 | 最新 | 市场公告通知 | 中心会员公告 | 会员信息公告 | | ---- | ------------ | ------------ | ------------ | | 2834 | 2835 | 2836 | 2837 | - 本币市场公告 +本币市场公告 | 最新 | 市场公告通知 | 中心会员公告 | 会员信息公告 | | -------------- | ------------ | ------------ | ------------ | | 2839,2840,2841 | 2839 | 2840 | 2841 | - 央行业务公告 +央行业务公告 | 最新 | 公开市场操作 | 中央国库现金管理 | | --------- | ------------ | ---------------- | | 2845,2846 | 2845 | 2846 | +
    本币市场 - 贷款市场报价利率 +贷款市场报价利率 | LPR 市场公告 | | ------------ | | 3686 | +
    `, }; diff --git a/lib/routes/chinania/index.ts b/lib/routes/chinania/index.ts index 9875fb0ded81..89a735d74f8f 100644 --- a/lib/routes/chinania/index.ts +++ b/lib/routes/chinania/index.ts @@ -81,7 +81,7 @@ export const route: Route = { example: '/chinania/xiehuidongtai/xiehuitongzhi', parameters: { category: '分类,默认为 `xiehuidongtai/xiehuitongzhi`,即协会通知,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [协会通知](https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/),网址为 \`https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/\`。截取 \`https://www.chinania.org.cn/html\` 到末尾 \`/\` 的部分 \`xiehuidongtai/xiehuitongzhi\` 作为参数填入,此时路由为 [\`/chinania/xiehuidongtai/xiehuitongzhi\`](https://rsshub.app/chinania/xiehuidongtai/xiehuitongzhi)。 +若订阅 [协会通知](https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/),网址为 \`https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/\`。截取 \`https://www.chinania.org.cn/html\` 到末尾 \`/\` 的部分 \`xiehuidongtai/xiehuitongzhi\` 作为参数填入,此时路由为 [\`/chinania/xiehuidongtai/xiehuitongzhi\`](https://rsshub.app/chinania/xiehuidongtai/xiehuitongzhi)。 :::
    @@ -89,7 +89,7 @@ export const route: Route = { #### [协会动态](https://www.chinania.org.cn/html/xiehuidongtai/) -| [协会动态](https://www.chinania.org.cn/html/xiehuidongtai/xiehuidongtai/) | [协会通知](https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/) | [有色企业50强](https://www.chinania.org.cn/html/xiehuidongtai/youseqiye50qiang/) | +| [协会动态](https://www.chinania.org.cn/html/xiehuidongtai/xiehuidongtai/) | [协会通知](https://www.chinania.org.cn/html/xiehuidongtai/xiehuitongzhi/) | [有色企业 50 强](https://www.chinania.org.cn/html/xiehuidongtai/youseqiye50qiang/) | | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | [xiehuidongtai/xiehuidongtai](https://rsshub.app/chinania/xiehuidongtai/xiehuidongtai) | [xiehuidongtai/xiehuitongzhi](https://rsshub.app/chinania/xiehuidongtai/xiehuitongzhi) | [xiehuidongtai/youseqiye50qiang](https://rsshub.app/chinania/xiehuidongtai/youseqiye50qiang) | @@ -129,8 +129,7 @@ export const route: Route = { | ------------------------------------------------------------------ | -------------------------------------------------------------------- | | [hyzl/huiyizhanlan](https://rsshub.app/chinania/hyzl/huiyizhanlan) | [hyzl/huizhanbaodao](https://rsshub.app/chinania/hyzl/huizhanbaodao) | -
    - `, +`, categories: ['new-media'], features: { diff --git a/lib/routes/chinaratings/credit-research.ts b/lib/routes/chinaratings/credit-research.ts index 4ccb5ae9ff25..7baab932be09 100644 --- a/lib/routes/chinaratings/credit-research.ts +++ b/lib/routes/chinaratings/credit-research.ts @@ -122,8 +122,7 @@ export const route: Route = { }, description: `::: tip 若订阅 [行业评论](https://www.chinaratings.com.cn/CreditResearch/Industry/Comment/),网址为 \`https://www.chinaratings.com.cn/CreditResearch/Industry/Comment/\`,请截取 \`https://www.chinaratings.com.cn/CreditResearch/\` 到末尾 \`/\` 的部分 \`Industry/Comment\` 作为 \`category\` 参数填入,此时目标路由为 [\`/chinaratings/CreditResearch/Industry/Comment\`](https://rsshub.app/chinaratings/CreditResearch/Industry/Comment)。 -::: -`, +:::`, categories: ['finance'], features: { requireConfig: false, diff --git a/lib/routes/chinatimes/index.ts b/lib/routes/chinatimes/index.ts index dc7a7bf0b67e..bfeb05a87d93 100644 --- a/lib/routes/chinatimes/index.ts +++ b/lib/routes/chinatimes/index.ts @@ -5,7 +5,7 @@ import cache from '@/utils/cache'; import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import timezone from '@/utils/timezone'; export const route: Route = { @@ -43,7 +43,7 @@ async function handler(ctx) { const response = await ofetch(link); const $ = load(response); - const browser = await puppeteer(); + const browser = await playwright(); const list = $('.articlebox-compact') .toArray() diff --git a/lib/routes/chongdiantou/namespace.ts b/lib/routes/chongdiantou/namespace.ts index b67d38e1b650..a50afe8cb602 100644 --- a/lib/routes/chongdiantou/namespace.ts +++ b/lib/routes/chongdiantou/namespace.ts @@ -5,5 +5,5 @@ export const namespace: Namespace = { url: 'www.chongdiantou.com', categories: ['new-media'], lang: 'zh-CN', - description: '充电头网是国内最早进行消费类电源技术及其周边配件(快充、充电头、充电器、无线充、车充、车载充电器、数据线、充电线材、移动电源及电芯、USB插排)评测、拆解的专业机构。', + description: '充电头网是国内最早进行消费类电源技术及其周边配件(快充、充电头、充电器、无线充、车充、车载充电器、数据线、充电线材、移动电源及电芯、USB 插排)评测、拆解的专业机构。', }; diff --git a/lib/routes/chsi/hotnews.ts b/lib/routes/chsi/hotnews.ts index e0d54ea8c19b..9caef413c8f5 100644 --- a/lib/routes/chsi/hotnews.ts +++ b/lib/routes/chsi/hotnews.ts @@ -67,7 +67,7 @@ async function handler() { ); return { - title: `中国研究生招生信息网 - 热点`, + title: '中国研究生招生信息网 - 热点', link: host, description: '中国研究生招生信息网 - 热点', item: items, diff --git a/lib/routes/chsi/kydt.ts b/lib/routes/chsi/kydt.ts index 830fbee1b05f..862ff9176b8b 100644 --- a/lib/routes/chsi/kydt.ts +++ b/lib/routes/chsi/kydt.ts @@ -65,7 +65,7 @@ async function handler() { ); return { - title: `中国研究生招生信息网 - 考研动态`, + title: '中国研究生招生信息网 - 考研动态', link: `${host}/kyzx/kydt/`, description: '中国研究生招生信息网 - 考研动态', item: items, diff --git a/lib/routes/chuapp/chuapp.ts b/lib/routes/chuapp/chuapp.ts index 7b472c9e4b5d..f533c4e7a807 100644 --- a/lib/routes/chuapp/chuapp.ts +++ b/lib/routes/chuapp/chuapp.ts @@ -13,14 +13,12 @@ export const route: Route = { parameters: { category: '栏目分类,见下表', }, - description: ` - | \`category\` | 栏目分类 | - | ------------ | ------- | - | \`daily\` | 每日聚焦 | - | \`pcz\` | 最好玩 | - | \`night\` | 触乐夜话 | - | \`news\` | 动态资讯 | - `, + description: `| \`category\` | 栏目分类 | +| ---------- | -------- | +| \`daily\` | 每日聚焦 | +| \`pcz\` | 最好玩 | +| \`night\` | 触乐夜话 | +| \`news\` | 动态资讯 |`, features: { requireConfig: false, requirePuppeteer: false, diff --git a/lib/routes/chub/characters.ts b/lib/routes/chub/characters.ts index ae237a85f881..df1bebf56b81 100644 --- a/lib/routes/chub/characters.ts +++ b/lib/routes/chub/characters.ts @@ -71,7 +71,7 @@ async function handler() { link: `${hostURL}/${item.fullPath}`, author: String(item.fullPath.split('/', 1)), enclosure_url: item.max_res_url, - enclosure_type: `image/png`, + enclosure_type: 'image/png', category: item.topics, })), }; diff --git a/lib/routes/cisia/index.ts b/lib/routes/cisia/index.ts index 7e3bb4feabf1..c1e5832b935f 100644 --- a/lib/routes/cisia/index.ts +++ b/lib/routes/cisia/index.ts @@ -85,7 +85,7 @@ export const route: Route = { example: '/cisia/9', parameters: { id: '栏目 id,默认为 `9`,即协会动态,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [市场信息](http://www.cisia.org/site/term/12.html),网址为 \`http://www.cisia.org/site/term/12.html\`。截取 \`https://www.cisia.org/site/term/\` 到末尾 \`.html\` 的部分 \`12\` 作为参数填入,此时路由为 [\`/cisia/12\`](https://rsshub.app/cisia/12)。 +若订阅 [市场信息](http://www.cisia.org/site/term/12.html),网址为 \`http://www.cisia.org/site/term/12.html\`。截取 \`https://www.cisia.org/site/term/\` 到末尾 \`.html\` 的部分 \`12\` 作为参数填入,此时路由为 [\`/cisia/12\`](https://rsshub.app/cisia/12)。 :::
    @@ -133,8 +133,7 @@ export const route: Route = { | -------------------------------------------------- | -------------------------------------------------- | | [35](https://rsshub.app/cisia/35) | [68](https://rsshub.app/cisia/68) | -
    - `, +`, categories: ['government'], features: { diff --git a/lib/routes/civitai/discussions.ts b/lib/routes/civitai/discussions.ts index 200574e1bfe8..9006f4946b80 100644 --- a/lib/routes/civitai/discussions.ts +++ b/lib/routes/civitai/discussions.ts @@ -70,7 +70,7 @@ async function handler(ctx) { return { title: `Civitai model ${params.modelId} discussions`, - link: `https://civitai.com/`, + link: 'https://civitai.com/', item: items, }; } diff --git a/lib/routes/civitai/models.ts b/lib/routes/civitai/models.ts index 06cafbe50808..d1a75ada0eea 100644 --- a/lib/routes/civitai/models.ts +++ b/lib/routes/civitai/models.ts @@ -28,7 +28,7 @@ export const route: Route = { }; async function handler() { - const { data } = await got(`https://civitai.com/api/v1/models`, { + const { data } = await got('https://civitai.com/api/v1/models', { searchParams: { limit: 20, sort: 'Newest', @@ -45,8 +45,8 @@ async function handler() { })); return { - title: `Civitai latest models`, - link: `https://civitai.com/`, + title: 'Civitai latest models', + link: 'https://civitai.com/', item: items, }; } diff --git a/lib/routes/cjlu/yjsy/index.ts b/lib/routes/cjlu/yjsy/index.ts index 894e488bfdce..68804d6e1930 100644 --- a/lib/routes/cjlu/yjsy/index.ts +++ b/lib/routes/cjlu/yjsy/index.ts @@ -4,7 +4,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import { getPuppeteerPage } from '@/utils/puppeteer'; +import { getPlaywrightPage } from '@/utils/playwright'; import timezone from '@/utils/timezone'; const host = 'https://yjsy.cjlu.edu.cn/'; @@ -77,8 +77,8 @@ export const route: Route = { maintainers: ['chrisis58'], handler, description: `| 研究生通知 | 教师通知 | -| -------- | -------- | -| yjstz | jstz |`, +| ---------- | -------- | +| yjstz | jstz |`, }; async function handler(ctx) { @@ -86,7 +86,7 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; const url = `${host}index/${cate}.htm`; - const { page, destroy, browser } = await getPuppeteerPage(url, { + const { page, destroy, browser } = await getPlaywrightPage(url, { onBeforeLoad: async (page) => { await page.setExtraHTTPHeaders(headers); await page.setUserAgent(headers['User-Agent']); diff --git a/lib/routes/claude/code-changelog.ts b/lib/routes/claude/code-changelog.ts index c5697ac52bc9..7d8483231530 100644 --- a/lib/routes/claude/code-changelog.ts +++ b/lib/routes/claude/code-changelog.ts @@ -4,6 +4,7 @@ import type { Context } from 'hono'; import type { Data, DataItem, Route } from '@/types'; import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; const handler = async (ctx: Context): Promise => { const limit = Number.parseInt(ctx.req.query('limit') ?? '25', 10); @@ -14,29 +15,27 @@ const handler = async (ctx: Context): Promise => { const response = await ofetch(targetUrl); const $: CheerioAPI = load(response); - const items: DataItem[] = $('div.markdown-heading') + const items: DataItem[] = $('div.update-container') .slice(0, limit) .toArray() .map((el): DataItem => { - const $heading = $(el); - const version = $heading.find('h2.heading-element').text().trim(); + const $entry = $(el); + const version = $entry.find('[data-component-part="update-label"]').text().trim(); if (!version) { return null as unknown as DataItem; } - const descriptionParts: string[] = []; - $heading.nextUntil('div.markdown-heading').each((_, sibling) => { - descriptionParts.push($(sibling).prop('outerHTML') ?? ''); - }); - const description = descriptionParts.join(''); + const dateText = $entry.find('[data-component-part="update-description"]').text().trim(); + const description = $entry.find('[data-component-part="update-content"]').html() ?? ''; - const anchor = $heading.find('a.anchor').attr('href') ?? `#${version.replaceAll('.', '')}`; - const link = `${targetUrl}${anchor}`; + const anchor = $entry.attr('id') ?? version.replaceAll('.', '-'); + const link = `${targetUrl}#${anchor}`; return { title: version, description, link, + pubDate: dateText ? parseDate(dateText) : undefined, guid: `claude-code-${version}`, id: `claude-code-${version}`, }; diff --git a/lib/routes/cls/subject.ts b/lib/routes/cls/subject.ts index b8ad2cd45e20..34f00049d729 100644 --- a/lib/routes/cls/subject.ts +++ b/lib/routes/cls/subject.ts @@ -123,9 +123,8 @@ export const route: Route = { example: '/cls/subject/1103', parameters: { category: '分类,默认为 1103,即A股盘面直播,可在对应话题页 URL 中找到' }, description: `::: tip - 若订阅 [有声早报](https://www.cls.cn/subject/1151),网址为 \`https://www.cls.cn/subject/1151\`。截取 \`https://www.cls.cn/subject/\` 到末尾的部分 \`1151\` 作为参数填入,此时路由为 [\`/cls/subject/1151\`](https://rsshub.app/cls/subject/1151)。 -::: - `, +若订阅 [有声早报](https://www.cls.cn/subject/1151),网址为 \`https://www.cls.cn/subject/1151\`。截取 \`https://www.cls.cn/subject/\` 到末尾的部分 \`1151\` 作为参数填入,此时路由为 [\`/cls/subject/1151\`](https://rsshub.app/cls/subject/1151)。 +:::`, categories: ['finance'], features: { diff --git a/lib/routes/cma/channel.tsx b/lib/routes/cma/channel.tsx index 59c9484bef6f..7eb1fd9673b1 100644 --- a/lib/routes/cma/channel.tsx +++ b/lib/routes/cma/channel.tsx @@ -47,7 +47,7 @@ export const route: Route = { | 环境气象公报 | 467 | ::: tip - 订阅更多细分频道,请前往对应上级频道页,使用下拉菜单选择项目后跳转到目标频道页,查看其 URL 找到对应频道 id +订阅更多细分频道,请前往对应上级频道页,使用下拉菜单选择项目后跳转到目标频道页,查看其 URL 找到对应频道 id :::`, }; diff --git a/lib/routes/cmde/index.ts b/lib/routes/cmde/index.ts index 5a5f567b6328..0f7776e45237 100644 --- a/lib/routes/cmde/index.ts +++ b/lib/routes/cmde/index.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import timezone from '@/utils/timezone'; const rootURL = 'https://www.cmde.org.cn'; @@ -18,7 +18,7 @@ export const route: Route = { async function handler(ctx) { const cate = ctx.req.param('cate') ?? 'xwdt/zxyw'; const url = `${rootURL}/${cate}/`; - const browser = await puppeteer(); + const browser = await playwright(); const data = await cache.tryGet(url, async () => { const page = await browser.newPage(); await page.setRequestInterception(true); diff --git a/lib/routes/cnbc/rss.ts b/lib/routes/cnbc/rss.ts index 06a3f8dda604..62d692a7f581 100644 --- a/lib/routes/cnbc/rss.ts +++ b/lib/routes/cnbc/rss.ts @@ -29,7 +29,7 @@ export const route: Route = { handler, description: `Provides a better reading experience (full articles) over the official ones. - Support all channels, refer to [CNBC RSS feeds](https://www.cnbc.com/rss-feeds/).`, +Support all channels, refer to [CNBC RSS feeds](https://www.cnbc.com/rss-feeds/).`, }; async function handler(ctx) { diff --git a/lib/routes/cnblogs/common.ts b/lib/routes/cnblogs/common.ts index c0dfee69edef..3e7be267182d 100644 --- a/lib/routes/cnblogs/common.ts +++ b/lib/routes/cnblogs/common.ts @@ -28,7 +28,7 @@ export const route: Route = { maintainers: ['hujingnb'], handler, url: 'www.cnblogs.com/pick', - description: `在博客园主页的分类出可查看所有类型。例如,go 的分类地址为: \`https://www.cnblogs.com/cate/go/\`, 则: [\`/cnblogs/cate/go\`](https://rsshub.app/cnblogs/cate/go)`, + description: '在博客园主页的分类出可查看所有类型。例如,go 的分类地址为: `https://www.cnblogs.com/cate/go/`, 则: [`/cnblogs/cate/go`](https://rsshub.app/cnblogs/cate/go)', }; async function handler(ctx) { diff --git a/lib/routes/cncf/reports.ts b/lib/routes/cncf/reports.ts index 7e7e2890b93e..16785af22506 100644 --- a/lib/routes/cncf/reports.ts +++ b/lib/routes/cncf/reports.ts @@ -47,7 +47,7 @@ async function handler() { ); return { - title: `CNCF - Reports`, + title: 'CNCF - Reports', link: url, item: items, }; diff --git a/lib/routes/cngold/index.ts b/lib/routes/cngold/index.ts index 201c66eaa2e4..400c25e2d611 100644 --- a/lib/routes/cngold/index.ts +++ b/lib/routes/cngold/index.ts @@ -80,7 +80,7 @@ export const route: Route = { example: '/cngold/news-325', parameters: { category: '分类,默认为 `news-325`,即行业资讯,可在对应分类页 URL 中找到, Category, `news-325`,即行业资讯by default' }, description: `::: tip - 若订阅 [行业资讯](https://www.cngold.org.cn/news-325.html),网址为 \`https://www.cngold.org.cn/news-325.html\`。截取 \`https://www.cngold.org.cn/\` 到末尾 \`.html\` 的部分 \`news-325\` 作为参数填入,此时路由为 [\`/cngold/news-325\`](https://rsshub.app/cngold/news-325)。 +若订阅 [行业资讯](https://www.cngold.org.cn/news-325.html),网址为 \`https://www.cngold.org.cn/news-325.html\`。截取 \`https://www.cngold.org.cn/\` 到末尾 \`.html\` 的部分 \`news-325\` 作为参数填入,此时路由为 [\`/cngold/news-325\`](https://rsshub.app/cngold/news-325)。 ::: #### 资讯中心 @@ -104,9 +104,9 @@ export const route: Route = { #### [行业培训](https://www.cngold.org.cn/training.html) -| [黄金投资分析师](https://www.cngold.org.cn/training-242.html) | [教育部1+X](https://www.cngold.org.cn/training-246.html) | [矿业权评估师](https://www.cngold.org.cn/training-338.html) | [其他培训](https://www.cngold.org.cn/training-247.html) | -| ------------------------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------- | -| [training-242](https://rsshub.app/cngold/training-242) | [training-246](https://rsshub.app/cngold/training-246) | [training-338](https://rsshub.app/cngold/training-338) | [training-247](https://rsshub.app/cngold/training-247) | +| [黄金投资分析师](https://www.cngold.org.cn/training-242.html) | [教育部 1+X](https://www.cngold.org.cn/training-246.html) | [矿业权评估师](https://www.cngold.org.cn/training-338.html) | [其他培训](https://www.cngold.org.cn/training-247.html) | +| ------------------------------------------------------------- | --------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------- | +| [training-242](https://rsshub.app/cngold/training-242) | [training-246](https://rsshub.app/cngold/training-246) | [training-338](https://rsshub.app/cngold/training-338) | [training-247](https://rsshub.app/cngold/training-247) | #### [黄金科技](https://www.cngold.org.cn/technology.html) @@ -114,8 +114,7 @@ export const route: Route = { | ------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------- | | [technology-318](https://rsshub.app/cngold/technology-318) | [technology-319](https://rsshub.app/cngold/technology-319) | [technology-320](https://rsshub.app/cngold/technology-320) | [technology-350](https://rsshub.app/cngold/technology-350) | - - `, +`, categories: ['new-media'], features: { diff --git a/lib/routes/cnki/author.ts b/lib/routes/cnki/author.ts index add89da884f6..01b5998c918a 100644 --- a/lib/routes/cnki/author.ts +++ b/lib/routes/cnki/author.ts @@ -23,7 +23,7 @@ export const route: Route = { }, example: '/cnki/author/丁晓东/中国人民大学', description: `::: tip - 可能仅限中国大陆服务器访问,以实际情况为准。 +可能仅限中国大陆服务器访问,以实际情况为准。 :::`, handler, }; diff --git a/lib/routes/cnljxh/index.ts b/lib/routes/cnljxh/index.ts index 6cc695cd3fb8..a78a42d0df14 100644 --- a/lib/routes/cnljxh/index.ts +++ b/lib/routes/cnljxh/index.ts @@ -268,7 +268,7 @@ export const route: Route = { ], }, }, - description: `:::tip + description: `::: tip 订阅 [协会公告](https://www.cnljxh.org.cn/news/?classid=10),其源网址为 \`https://www.cnljxh.org.cn/news/?classid=10\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/cnljxh/news/10\`](https://rsshub.app/cnljxh/news/10)。 订阅 [价格行情](https://www.cnljxh.org.cn/price/?classid=299),其源网址为 \`https://www.cnljxh.org.cn/price/?classid=299\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/cnljxh/price/299\`](https://rsshub.app/cnljxh/price/299)。 @@ -331,9 +331,9 @@ export const route: Route = { #### 价格指数 -| [焦炭指数(MyCpic)](https://www.cnljxh.org.cn/date/?classid=5575) | [炼焦煤指数(MyCpic)](https://www.cnljxh.org.cn/date/?classid=5907) | [山西焦炭价格指数(SCSPI)](https://www.cnljxh.org.cn/news/index.php?classid=34) | [中价·新华焦煤价格指数(CCP)](https://www.cnljxh.org.cn/news/index.php?classid=35) | -| ---------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -| [5575](https://rsshub.app/cnljxh/date/5575) | [5907](https://rsshub.app/cnljxh/date/5907) | [34](https://rsshub.app/cnljxh/news/34) | [35](https://rsshub.app/cnljxh/news/35) | +| [焦炭指数 (MyCpic)](https://www.cnljxh.org.cn/date/?classid=5575) | [炼焦煤指数 (MyCpic)](https://www.cnljxh.org.cn/date/?classid=5907) | [山西焦炭价格指数(SCSPI)](https://www.cnljxh.org.cn/news/index.php?classid=34) | [中价・新华焦煤价格指数(CCP)](https://www.cnljxh.org.cn/news/index.php?classid=35) | +| ----------------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| [5575](https://rsshub.app/cnljxh/date/5575) | [5907](https://rsshub.app/cnljxh/date/5907) | [34](https://rsshub.app/cnljxh/news/34) | [35](https://rsshub.app/cnljxh/news/35) | #### 市场信息 @@ -341,8 +341,7 @@ export const route: Route = { | -------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | | [19](https://rsshub.app/cnljxh/news/19) | [20](https://rsshub.app/cnljxh/news/20) | [21](https://rsshub.app/cnljxh/news/21) | [22](https://rsshub.app/cnljxh/news/22) | [31](https://rsshub.app/cnljxh/news/31) | - -`, +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/cntheory/paper.tsx b/lib/routes/cntheory/paper.tsx index ea375805160b..4d63c0459dc5 100644 --- a/lib/routes/cntheory/paper.tsx +++ b/lib/routes/cntheory/paper.tsx @@ -23,7 +23,7 @@ export const route: Route = { name: '学习时报', maintainers: ['nczitzk'], handler, - description: `如订阅 **第 A1 版:国内大局**,路由为 [\`/cntheory/paper/国内大局\`](https://rsshub.app/cntheory/paper/国内大局)。`, + description: '如订阅 **第 A1 版:国内大局**,路由为 [`/cntheory/paper/国内大局`](https://rsshub.app/cntheory/paper/国内大局)。', }; async function handler(ctx) { diff --git a/lib/routes/cntv/column.tsx b/lib/routes/cntv/column.tsx index 3f02793ed7c0..a8f1d881a949 100644 --- a/lib/routes/cntv/column.tsx +++ b/lib/routes/cntv/column.tsx @@ -31,7 +31,7 @@ export const route: Route = { 打开栏目具体某一期页面,F12 控制台输入\`column_id\`得到栏目 ID。 ::: - 栏目 +栏目 | 新闻联播 | 新闻周刊 | 天下足球 | | -------------------- | -------------------- | -------------------- | diff --git a/lib/routes/cnu/jwc.ts b/lib/routes/cnu/jwc.ts index c18fe69a1f65..a91ab883b6e2 100644 --- a/lib/routes/cnu/jwc.ts +++ b/lib/routes/cnu/jwc.ts @@ -52,7 +52,7 @@ async function handler() { return { title: item.find('span.title').text().trim(), link: linkUrl, - pubDate: pubDate || undefined, + pubDate, category: categoryName ? [categoryName] : undefined, description: '', }; diff --git a/lib/routes/colamanga/manga.ts b/lib/routes/colamanga/manga.ts index e5d0acb01330..626f1b0d8067 100644 --- a/lib/routes/colamanga/manga.ts +++ b/lib/routes/colamanga/manga.ts @@ -4,7 +4,7 @@ import type { Context } from 'hono'; import type { Route } from '@/types'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import timezone from '@/utils/timezone'; const domain = 'www.colamanga.com'; @@ -43,7 +43,7 @@ async function handler(ctx: Context) { const id = ctx.req.param('id'); const url = `https://${domain}/${id}`; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); diff --git a/lib/routes/comic-fuz/magazine.ts b/lib/routes/comic-fuz/magazine.ts index 0d6c09072ac7..37268378db8a 100644 --- a/lib/routes/comic-fuz/magazine.ts +++ b/lib/routes/comic-fuz/magazine.ts @@ -30,7 +30,7 @@ export const route: Route = { const { id } = ctx.req.param(); const baseUrl = 'https://comic-fuz.com'; const openUrl = `${baseUrl}/magazine/${id}`; - const imgUrl = `https://img.comic-fuz.com`; + const imgUrl = 'https://img.comic-fuz.com'; const response = await ofetch(openUrl, { headers: { diff --git a/lib/routes/comic-fuz/manga.ts b/lib/routes/comic-fuz/manga.ts index 341e85f95437..c6df1c73bb2a 100644 --- a/lib/routes/comic-fuz/manga.ts +++ b/lib/routes/comic-fuz/manga.ts @@ -30,7 +30,7 @@ export const route: Route = { const { id } = ctx.req.param(); const baseUrl = 'https://comic-fuz.com'; const openUrl = `${baseUrl}/manga/${id}`; - const imgUrl = `https://img.comic-fuz.com`; + const imgUrl = 'https://img.comic-fuz.com'; const response = await ofetch(openUrl, { headers: { diff --git a/lib/routes/consumer/index.ts b/lib/routes/consumer/index.ts index 404cbc683ed3..d66de0fd0420 100644 --- a/lib/routes/consumer/index.ts +++ b/lib/routes/consumer/index.ts @@ -33,7 +33,7 @@ export const route: Route = { | ---------- | -------- | --------- | -------- | | test | life | complaint | topic | - 语言 +语言 | 简体中文 | 繁体中文 | | -------- | -------- | diff --git a/lib/routes/coolapk/dyh.ts b/lib/routes/coolapk/dyh.ts index a998c093f9b5..10186e4a55ad 100644 --- a/lib/routes/coolapk/dyh.ts +++ b/lib/routes/coolapk/dyh.ts @@ -27,7 +27,7 @@ export const route: Route = { maintainers: ['xizeyoupan'], handler, description: `::: tip - 仅限于采集**站内订阅**的看看号的内容。看看号 ID 可在看看号界面右上分享 - 复制链接得到。 +仅限于采集**站内订阅**的看看号的内容。看看号 ID 可在看看号界面右上分享 - 复制链接得到。 :::`, }; diff --git a/lib/routes/coolapk/hot.ts b/lib/routes/coolapk/hot.ts index 13b21508ea2f..6aec2286542c 100644 --- a/lib/routes/coolapk/hot.ts +++ b/lib/routes/coolapk/hot.ts @@ -58,11 +58,11 @@ const getLinkAndTitle = (type, period) => { statDays: '30days', }, }; - link = `#/feed/coolPictureList?statDays=` + trans[period].statDays + `&listType=statFavNum&buildCard=1&title=` + trans[period].description + `&page=1`; + link = '#/feed/coolPictureList?statDays=' + trans[period].statDays + '&listType=statFavNum&buildCard=1&title=' + trans[period].description + '&page=1'; res.title = '酷图榜-' + trans[period].description; } else { - link = `#/feed/statList?statType=` + periods[period].statType + `&sortField=` + types[type].sortField + `&title=` + periods[period].description + `&page=1`; - res.title = types[type].title + `-` + periods[period].description; + link = '#/feed/statList?statType=' + periods[period].statType + '&sortField=' + types[type].sortField + '&title=' + periods[period].description + '&page=1'; + res.title = types[type].title + '-' + periods[period].description; } res.link = baseURL + encodeURIComponent(link); return res; @@ -99,7 +99,7 @@ export const route: Route = { | period | daily | weekly | ::: tip - 今日热门没有周榜,酷图榜日榜的参数会变成周榜,周榜的参数会变成月榜。 +今日热门没有周榜,酷图榜日榜的参数会变成周榜,周榜的参数会变成月榜。 :::`, }; @@ -129,7 +129,7 @@ async function handler(ctx) { return { title, link: 'https://www.coolapk.com/', - description: `热榜-` + title, + description: '热榜-' + title, item: out, }; } diff --git a/lib/routes/coolapk/huati.ts b/lib/routes/coolapk/huati.ts index 5a21aeb3a1e2..37731016f2eb 100644 --- a/lib/routes/coolapk/huati.ts +++ b/lib/routes/coolapk/huati.ts @@ -44,7 +44,7 @@ async function handler(ctx) { } return { title: `酷安话题-${tag}`, - link: `https://www.coolapk.com/`, + link: 'https://www.coolapk.com/', description: `酷安话题-${tag}`, item: out, }; diff --git a/lib/routes/coolapk/namespace.ts b/lib/routes/coolapk/namespace.ts index 7c3147c6cbc8..c977aed59874 100644 --- a/lib/routes/coolapk/namespace.ts +++ b/lib/routes/coolapk/namespace.ts @@ -3,8 +3,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '酷安', url: 'coolapk.com', - description: ` -::: tip + description: `::: tip 即日起,多数路由图片防盗链。 需要将 \`ALLOW_USER_HOTLINK_TEMPLATE\` 环境变量设置为 \`true\` ,然后配置\`image_hotlink_template\` 。 详见 [#16715](https://github.com/DIYgod/RSSHub/issues/16715) diff --git a/lib/routes/coolapk/utils.ts b/lib/routes/coolapk/utils.ts index 865b063bba17..9ae9bc8412b7 100644 --- a/lib/routes/coolapk/utils.ts +++ b/lib/routes/coolapk/utils.ts @@ -88,13 +88,13 @@ const parseDynamic = async (item) => { const result = await ofetch(itemUrl, { headers: getHeaders(), }); - const message = `

    ` + result.data?.message.split('\n').join('
    ') + `

    `; + const message = '

    ' + result.data?.message.split('\n').join('
    ') + '

    '; const picArr = item.picArr.filter(Boolean).map((i) => ``); // 若无图片,item.picArr=[""] return message + picArr.join(''); }); } else { const picArr = item.picArr.filter(Boolean).map((i) => ``); - description = `

    ` + item.message + `

    ` + picArr.join(''); + description = '

    ' + item.message + '

    ' + picArr.join(''); } const $ = load('
    ' + description + '
    '); title = $('.title-filter').text().trim(); // no need to perform substring because it's will be handled by RSSHub 'TITLE_LENGTH_LIMIT' @@ -103,7 +103,7 @@ const parseDynamic = async (item) => { if (type === 17) { const keys = item.extra_key.split(','); - description += `

    ` + item.vote.message_title + ` 已选${keys.length}项

    `; + description += '

    ' + item.vote.message_title + ` 已选${keys.length}项

    `; for (const i of item.vote.options) { if (keys.includes(String(i.id))) { description += `

    ${i.title}√

    `; diff --git a/lib/routes/coomer/index.tsx b/lib/routes/coomer/index.tsx index f2ba15ddb75b..4afffa1b4b91 100644 --- a/lib/routes/coomer/index.tsx +++ b/lib/routes/coomer/index.tsx @@ -37,12 +37,12 @@ export const route: Route = { description: `Sources | Posts | OnlyFans | Fansly | CandFans | -| ----- | -------- | ------- | -------- | -| posts | onlyfans | fansly | candfans | +| ----- | -------- | ------ | -------- | +| posts | onlyfans | fansly | candfans | ::: tip - When \`posts\` is selected as the value of the parameter **source**, the parameter **id** does not take effect. - There is an optinal parameter **limit** which controls the number of posts to fetch, default value is 25. +When \`posts\` is selected as the value of the parameter **source**, the parameter **id** does not take effect. +There is an optinal parameter **limit** which controls the number of posts to fetch, default value is 25. :::`, }; diff --git a/lib/routes/costar/press-releases.ts b/lib/routes/costar/press-releases.ts index 7afabf862fbf..579c1afa2580 100644 --- a/lib/routes/costar/press-releases.ts +++ b/lib/routes/costar/press-releases.ts @@ -111,10 +111,9 @@ export const route: Route = { description: 'Filter', }, }, - description: `:::tip -To subscribe to [Press Releases - Asia Pacific - Preliminary](https://www.costar.com/products/benchmark/resources/press-releases?region=406&tag=581), where the source URL is \`https://www.costar.com/products/benchmark/resources/press-releases?region=406&tag=581\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/costar/press-releases/region=406&tag=581\`](https://rsshub.app/costar/press-releases/region=406&tag=581). -::: -`, + description: `::: tip +To subscribe to [Press Releases - Asia Pacific - Preliminary](https://www.costar.com/products/benchmark/resources/press-releases?region=406\\&tag=581), where the source URL is \`https://www.costar.com/products/benchmark/resources/press-releases?region=406&tag=581\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/costar/press-releases/region=406&tag=581\`](https://rsshub.app/costar/press-releases/region=406\\&tag=581). +:::`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/counter-strike/news.ts b/lib/routes/counter-strike/news.ts index f82187934c63..0053926a607b 100644 --- a/lib/routes/counter-strike/news.ts +++ b/lib/routes/counter-strike/news.ts @@ -123,7 +123,7 @@ export const route: Route = { example: '/counter-strike/news', parameters: { category: 'Category, `updates` or `all`, `all` by default', language: 'Language, english by default, see below for more languages' }, description: `::: tip - If you subscribe to [Updates in English](https://www.counter-strike.net/news/updates?l=english),where the URL is \`https://www.counter-strike.net/news/updates?l=english\`, extract the \`l\`, which is \`english\`, and use it as the parameter to fill in. Therefore, the route will be [\`/counter-strike/news/updates/english\`](https://rsshub.app/counter-strike/news/updates/english). +If you subscribe to [Updates in English](https://www.counter-strike.net/news/updates?l=english),where the URL is \`https://www.counter-strike.net/news/updates?l=english\`, extract the \`l\`, which is \`english\`, and use it as the parameter to fill in. Therefore, the route will be [\`/counter-strike/news/updates/english\`](https://rsshub.app/counter-strike/news/updates/english). :::
    @@ -160,8 +160,7 @@ export const route: Route = { | Tiếng Việt (Vietnamese) | vietnamese | | Español - Latinoamérica (Spanish - Latin America) | latam | -
    - `, +`, categories: ['game'], features: { diff --git a/lib/routes/cpcaauto/index.ts b/lib/routes/cpcaauto/index.ts index e359184f2b27..770dcc41d580 100644 --- a/lib/routes/cpcaauto/index.ts +++ b/lib/routes/cpcaauto/index.ts @@ -82,43 +82,42 @@ export const route: Route = { example: '/cpcaauto/news/news', parameters: { type: '分类,默认为 news,可在对应分类页 URL 中找到', id: 'id,默认为 news,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [行业新闻 > 国内乘用车](http://cpcaauto.com/news.php?types=news&anid=10),网址为 \`http://cpcaauto.com/news.php?types=news&anid=10\`。截取 \`types\` 和 \`anid\` 的部分 \`\` 作为参数填入,此时路由为 [\`/cpcaauto/news/news/10\`](https://rsshub.app/cpcaauto/news/news/10)。 +若订阅 [行业新闻 > 国内乘用车](http://cpcaauto.com/news.php?types=news\\&anid=10),网址为 \`http://cpcaauto.com/news.php?types=news&anid=10\`。截取 \`types\` 和 \`anid\` 的部分 \\\`\\\` 作为参数填入,此时路由为 [\`/cpcaauto/news/news/10\`](https://rsshub.app/cpcaauto/news/news/10)。 ::: #### [行业新闻](http://cpcaauto.com/news.php?types=news) -| [国内乘用车](http://cpcaauto.com/news.php?types=news&anid=10) | [进口及国外乘用车](http://cpcaauto.com/news.php?types=news&anid=64) | [后市场](http://cpcaauto.com/news.php?types=news&anid=44) | [商用车](http://cpcaauto.com/news.php?types=news&anid=62) | -| ----------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------- | -| [news/10](https://rsshub.app/cpcaauto/news/news/10) | [news/64](https://rsshub.app/cpcaauto/news/news/64) | [news/44](https://rsshub.app/cpcaauto/news/news/44) | [news/62](https://rsshub.app/cpcaauto/news/news/62) | +| [国内乘用车](http://cpcaauto.com/news.php?types=news\\&anid=10) | [进口及国外乘用车](http://cpcaauto.com/news.php?types=news\\&anid=64) | [后市场](http://cpcaauto.com/news.php?types=news\\&anid=44) | [商用车](http://cpcaauto.com/news.php?types=news\\&anid=62) | +| -------------------------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------- | +| [news/10](https://rsshub.app/cpcaauto/news/news/10) | [news/64](https://rsshub.app/cpcaauto/news/news/64) | [news/44](https://rsshub.app/cpcaauto/news/news/44) | [news/62](https://rsshub.app/cpcaauto/news/news/62) | #### [车市解读](http://cpcaauto.com/news.php?types=csjd) -| [周度](http://cpcaauto.com/news.php?types=csjd&anid=128) | [月度](http://cpcaauto.com/news.php?types=csjd&anid=129) | [指数](http://cpcaauto.com/news.php?types=csjd&anid=130) | [预测](http://cpcaauto.com/news.php?types=csjd&anid=131) | -| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| [csjd/128](https://rsshub.app/cpcaauto/news/csjd/128) | [csjd/129](https://rsshub.app/cpcaauto/news/csjd/129) | [csjd/130](https://rsshub.app/cpcaauto/news/csjd/130) | [csjd/131](https://rsshub.app/cpcaauto/news/csjd/131) | +| [周度](http://cpcaauto.com/news.php?types=csjd\\&anid=128) | [月度](http://cpcaauto.com/news.php?types=csjd\\&anid=129) | [指数](http://cpcaauto.com/news.php?types=csjd\\&anid=130) | [预测](http://cpcaauto.com/news.php?types=csjd\\&anid=131) | +| --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | +| [csjd/128](https://rsshub.app/cpcaauto/news/csjd/128) | [csjd/129](https://rsshub.app/cpcaauto/news/csjd/129) | [csjd/130](https://rsshub.app/cpcaauto/news/csjd/130) | [csjd/131](https://rsshub.app/cpcaauto/news/csjd/131) | #### [发布会报告](http://cpcaauto.com/news.php?types=bgzl) -| [上海市场上牌数](http://cpcaauto.com/news.php?types=bgzl&anid=119) | [京城车市](http://cpcaauto.com/news.php?types=bgzl&anid=122) | [进口车市场分析](http://cpcaauto.com/news.php?types=bgzl&anid=120) | [二手车市场分析](http://cpcaauto.com/news.php?types=bgzl&anid=121) | [价格指数](http://cpcaauto.com/news.php?types=bgzl&anid=124) | -| ---------------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------- | -| [bgzl/119](https://rsshub.app/cpcaauto/news/bgzl/119) | [bgzl/122](https://rsshub.app/cpcaauto/news/bgzl/122) | [bgzl/120](https://rsshub.app/cpcaauto/news/bgzl/120) | [bgzl/121](https://rsshub.app/cpcaauto/news/bgzl/121) | [bgzl/124](https://rsshub.app/cpcaauto/news/bgzl/124) | +| [上海市场上牌数](http://cpcaauto.com/news.php?types=bgzl\\&anid=119) | [京城车市](http://cpcaauto.com/news.php?types=bgzl\\&anid=122) | [进口车市场分析](http://cpcaauto.com/news.php?types=bgzl\\&anid=120) | [二手车市场分析](http://cpcaauto.com/news.php?types=bgzl\\&anid=121) | [价格指数](http://cpcaauto.com/news.php?types=bgzl\\&anid=124) | +| ------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------- | +| [bgzl/119](https://rsshub.app/cpcaauto/news/bgzl/119) | [bgzl/122](https://rsshub.app/cpcaauto/news/bgzl/122) | [bgzl/120](https://rsshub.app/cpcaauto/news/bgzl/120) | [bgzl/121](https://rsshub.app/cpcaauto/news/bgzl/121) | [bgzl/124](https://rsshub.app/cpcaauto/news/bgzl/124) | -| [热点评述](http://cpcaauto.com/news.php?types=bgzl&anid=125) | [新能源月报](http://cpcaauto.com/news.php?types=bgzl&anid=126) | [商用车月报](http://cpcaauto.com/news.php?types=bgzl&anid=127) | [政策分析](http://cpcaauto.com/news.php?types=bgzl&anid=123) | -| ---------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------- | -| [bgzl/125](https://rsshub.app/cpcaauto/news/bgzl/125) | [bgzl/126](https://rsshub.app/cpcaauto/news/bgzl/126) | [bgzl/127](https://rsshub.app/cpcaauto/news/bgzl/127) | [bgzl/123](https://rsshub.app/cpcaauto/news/bgzl/123) | +| [热点评述](http://cpcaauto.com/news.php?types=bgzl\\&anid=125) | [新能源月报](http://cpcaauto.com/news.php?types=bgzl\\&anid=126) | [商用车月报](http://cpcaauto.com/news.php?types=bgzl\\&anid=127) | [政策分析](http://cpcaauto.com/news.php?types=bgzl\\&anid=123) | +| ------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | +| [bgzl/125](https://rsshub.app/cpcaauto/news/bgzl/125) | [bgzl/126](https://rsshub.app/cpcaauto/news/bgzl/126) | [bgzl/127](https://rsshub.app/cpcaauto/news/bgzl/127) | [bgzl/123](https://rsshub.app/cpcaauto/news/bgzl/123) | #### [经济与政策](http://cpcaauto.com/news.php?types=meeting) -| [一周经济](http://cpcaauto.com/news.php?types=meeting&anid=46) | [一周政策](http://cpcaauto.com/news.php?types=meeting&anid=47) | -| ------------------------------------------------------------------ | ------------------------------------------------------------------ | -| [meeting/46](https://rsshub.app/cpcaauto/news/meeting/46) | [meeting/47](https://rsshub.app/cpcaauto/news/meeting/47) | +| [一周经济](http://cpcaauto.com/news.php?types=meeting\\&anid=46) | [一周政策](http://cpcaauto.com/news.php?types=meeting\\&anid=47) | +| --------------------------------------------------------------- | --------------------------------------------------------------- | +| [meeting/46](https://rsshub.app/cpcaauto/news/meeting/46) | [meeting/47](https://rsshub.app/cpcaauto/news/meeting/47) | #### [乘联会论坛](http://cpcaauto.com/news.php?types=yjsy) -| [论坛文章](http://cpcaauto.com/news.php?types=yjsy&anid=49) | [两会](http://cpcaauto.com/news.php?types=yjsy&anid=111) | [车展看点](http://cpcaauto.com/news.php?types=yjsy&anid=113) | -| --------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------- | -| [yjsy/49](https://rsshub.app/cpcaauto/news/yjsy/49) | [yjsy/111](https://rsshub.app/cpcaauto/news/yjsy/111) | [yjsy/113](https://rsshub.app/cpcaauto/news/yjsy/113) | - `, +| [论坛文章](http://cpcaauto.com/news.php?types=yjsy\\&anid=49) | [两会](http://cpcaauto.com/news.php?types=yjsy\\&anid=111) | [车展看点](http://cpcaauto.com/news.php?types=yjsy\\&anid=113) | +| ------------------------------------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------- | +| [yjsy/49](https://rsshub.app/cpcaauto/news/yjsy/49) | [yjsy/111](https://rsshub.app/cpcaauto/news/yjsy/111) | [yjsy/113](https://rsshub.app/cpcaauto/news/yjsy/113) |`, categories: ['new-media'], features: { diff --git a/lib/routes/cpta/handler.ts b/lib/routes/cpta/handler.ts index 8c4e753c9bf4..c7a02dce6f32 100644 --- a/lib/routes/cpta/handler.ts +++ b/lib/routes/cpta/handler.ts @@ -100,12 +100,10 @@ export const route: Route = { parameters: { category: '栏目参数,可见下表描述。', }, - description: ` -| Category | Title | Description | -|-------------|-----------|-------------------------------------| -| notice | 通知公告 | 中国人事考试网 考试通知公告汇总 | -| performance | 成绩公布 | 中国人事考试网 考试成绩公布汇总 | -`, + description: `| Category | Title | Description | +| ----------- | -------- | ------------------------------- | +| notice | 通知公告 | 中国人事考试网 考试通知公告汇总 | +| performance | 成绩公布 | 中国人事考试网 考试成绩公布汇总 |`, handler, categories: ['study'], features: { @@ -121,12 +119,12 @@ export const route: Route = { { title: '中国人事考试网通知公告', source: ['www.cpta.com.cn/notice.html', 'www.cpta.com.cn'], - target: `/notice`, + target: '/notice', }, { title: '中国人事考试网成绩发布', source: ['www.cpta.com.cn/performance.html', 'www.cpta.com.cn'], - target: `/performance`, + target: '/performance', }, ], example: '/cpta/notice', diff --git a/lib/routes/cs/index.ts b/lib/routes/cs/index.ts index 4d28d018102a..c9fb5dccd9cd 100644 --- a/lib/routes/cs/index.ts +++ b/lib/routes/cs/index.ts @@ -65,6 +65,7 @@ export const route: Route = { | 中证快讯 7x24 | IPO 鉴真 | 公司能见度 | | ------------- | -------- | ---------- | | sylm/jsbd | yc/ipojz | yc/gsnjd | + `, handler, }; diff --git a/lib/routes/cuilingmag/index.ts b/lib/routes/cuilingmag/index.ts index c490c05e3ccd..47ba39a7fc08 100644 --- a/lib/routes/cuilingmag/index.ts +++ b/lib/routes/cuilingmag/index.ts @@ -139,18 +139,17 @@ export const route: Route = { example: '/cuilingmag', parameters: { category: '分类,默认为空,即全部,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [#哲学·文明](https://www.cuilingmag.com/category/philosophy_civilization),网址为 \`https://www.cuilingmag.com/category/philosophy_civilization\`。截取 \`https://www.cuilingmag.com/category\` 到末尾的部分 \`philosophy_civilization\` 作为参数填入,此时路由为 [\`/cuilingmag/philosophy_civilization\`](https://rsshub.app/cuilingmag/philosophy_civilization)。 +若订阅 [#哲学・文明](https://www.cuilingmag.com/category/philosophy_civilization),网址为 \`https://www.cuilingmag.com/category/philosophy_civilization\`。截取 \`https://www.cuilingmag.com/category\` 到末尾的部分 \`philosophy_civilization\` 作为参数填入,此时路由为 [\`/cuilingmag/philosophy_civilization\`](https://rsshub.app/cuilingmag/philosophy_civilization)。 ::: -| 分类 | ID | -| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| [哲学 · 文明](https://www.cuilingmag.com/category/philosophy_civilization) | [philosophy_civilization](https://rsshub.app/cuilingmag/philosophy_civilization) | -| [艺术 · 科技](https://www.cuilingmag.com/category/art_science) | [art_science](https://rsshub.app/cuilingmag/art_science) | -| [未来 · 生命](https://www.cuilingmag.com/category/future_life) | [future_life](https://rsshub.app/cuilingmag/future_life) | -| [行星智慧](https://www.cuilingmag.com/category/planetary_wisdom) | [planetary_wisdom](https://rsshub.app/cuilingmag/planetary_wisdom) | -| [数字治理](https://www.cuilingmag.com/category/digital_governance) | [digital_governance](https://rsshub.app/cuilingmag/digital_governance) | -| [Noema精选](https://www.cuilingmag.com/category/selected_noema) | [selected_noema](https://rsshub.app/cuilingmag/selected_noema) | - `, +| 分类 | ID | +| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | +| [哲学・文明](https://www.cuilingmag.com/category/philosophy_civilization) | [philosophy\\_civilization](https://rsshub.app/cuilingmag/philosophy_civilization) | +| [艺术・科技](https://www.cuilingmag.com/category/art_science) | [art\\_science](https://rsshub.app/cuilingmag/art_science) | +| [未来・生命](https://www.cuilingmag.com/category/future_life) | [future\\_life](https://rsshub.app/cuilingmag/future_life) | +| [行星智慧](https://www.cuilingmag.com/category/planetary_wisdom) | [planetary\\_wisdom](https://rsshub.app/cuilingmag/planetary_wisdom) | +| [数字治理](https://www.cuilingmag.com/category/digital_governance) | [digital\\_governance](https://rsshub.app/cuilingmag/digital_governance) | +| [Noema 精选](https://www.cuilingmag.com/category/selected_noema) | [selected\\_noema](https://rsshub.app/cuilingmag/selected_noema) |`, features: { requireConfig: false, diff --git a/lib/routes/cw/author.ts b/lib/routes/cw/author.ts index 8b9cfa23d626..2be67a126dfe 100644 --- a/lib/routes/cw/author.ts +++ b/lib/routes/cw/author.ts @@ -1,5 +1,5 @@ import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { baseUrl, parsePage } from './utils'; @@ -27,7 +27,7 @@ export const route: Route = { }; async function handler(ctx) { - const browser = await puppeteer(); + const browser = await playwright(); const { $, items } = await parsePage('author', browser, ctx); diff --git a/lib/routes/cw/master.ts b/lib/routes/cw/master.ts index e354e49a7051..eaae855f3f21 100644 --- a/lib/routes/cw/master.ts +++ b/lib/routes/cw/master.ts @@ -1,5 +1,5 @@ import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { baseUrl, parsePage } from './utils'; @@ -37,7 +37,7 @@ export const route: Route = { }; async function handler(ctx) { - const browser = await puppeteer(); + const browser = await playwright(); const { $, items } = await parsePage('master', browser, ctx); diff --git a/lib/routes/cw/sub.ts b/lib/routes/cw/sub.ts index 3ea82a68be5d..3f5abdb728bb 100644 --- a/lib/routes/cw/sub.ts +++ b/lib/routes/cw/sub.ts @@ -1,5 +1,5 @@ import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { baseUrl, parsePage } from './utils'; @@ -22,7 +22,7 @@ export const route: Route = { }; async function handler(ctx) { - const browser = await puppeteer(); + const browser = await playwright(); const { $, items } = await parsePage('sub', browser, ctx); diff --git a/lib/routes/cw/today.ts b/lib/routes/cw/today.ts index 118fc237c5d2..dd34056332ab 100644 --- a/lib/routes/cw/today.ts +++ b/lib/routes/cw/today.ts @@ -1,5 +1,5 @@ import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { baseUrl, parsePage } from './utils'; @@ -28,7 +28,7 @@ export const route: Route = { }; async function handler(ctx) { - const browser = await puppeteer(); + const browser = await playwright(); const { $, items } = await parsePage('today', browser, ctx); diff --git a/lib/routes/cw/utils.ts b/lib/routes/cw/utils.ts index 2093f3322d8d..f1f9a9859505 100644 --- a/lib/routes/cw/utils.ts +++ b/lib/routes/cw/utils.ts @@ -4,7 +4,7 @@ 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/puppeteer-utils'; +import { getCookies, setCookies } from '@/utils/playwright-utils'; let cookie; @@ -122,4 +122,4 @@ const parseItems = (list, browser, tryGet) => export { baseUrl, getCookie, parseItems, parseList, parsePage, pathMap }; -export { setCookies } from '@/utils/puppeteer-utils'; +export { setCookies } from '@/utils/playwright-utils'; diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts index b7e101aa30aa..92973c8a7dae 100644 --- a/lib/routes/daily/discussed.ts +++ b/lib/routes/daily/discussed.ts @@ -3,54 +3,51 @@ import { ViewType } from '@/types'; import { baseUrl, getData, getList, variables } from './utils.js'; -const query = ` - query MostDiscussedFeed( - $first: Int - $supportedTypes: [String!] = ["article","share","freeform"] - ) { - page: mostDiscussedFeed(first: $first, supportedTypes: $supportedTypes) { - ...FeedPostConnection +const query = /* GraphQL */ ` + query MostDiscussedFeed($first: Int, $supportedTypes: [String!] = ["article", "share", "freeform"]) { + page: mostDiscussedFeed(first: $first, supportedTypes: $supportedTypes) { + ...FeedPostConnection + } } - } - fragment FeedPostConnection on PostConnection { - edges { - node { - ...FeedPost - contentHtml - } + fragment FeedPostConnection on PostConnection { + edges { + node { + ...FeedPost + contentHtml + } + } } - } - fragment FeedPost on Post { - ...SharedPostInfo - } + fragment FeedPost on Post { + ...SharedPostInfo + } - fragment SharedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - summary - createdAt - numUpvotes - numComments - author { - ...UserShortInfo + fragment SharedPostInfo on Post { + id + title + image + readTime + permalink + commentsPermalink + summary + createdAt + numUpvotes + numComments + author { + ...UserShortInfo + } + tags } - tags - } - fragment UserShortInfo on User { - id - name - image - permalink - username - bio - } + fragment UserShortInfo on User { + id + name + image + permalink + username + bio + } `; export const route: Route = { diff --git a/lib/routes/daily/popular.ts b/lib/routes/daily/popular.ts index e9d673381545..8360dc059315 100644 --- a/lib/routes/daily/popular.ts +++ b/lib/routes/daily/popular.ts @@ -3,124 +3,110 @@ import { ViewType } from '@/types'; import { baseUrl, getData, getList, variables } from './utils.js'; -const query = ` - query AnonymousFeed( - $loggedIn: Boolean! = false - $first: Int - $after: String - $ranking: Ranking - $version: Int - $supportedTypes: [String!] = ["article","share","freeform","video:youtube","collection"] - ) { - page: anonymousFeed( - first: $first - after: $after - ranking: $ranking - version: $version - supportedTypes: $supportedTypes - ) { - ...FeedPostConnection +const query = /* GraphQL */ ` + query AnonymousFeed($loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $version: Int, $supportedTypes: [String!] = ["article", "share", "freeform", "video:youtube", "collection"]) { + page: anonymousFeed(first: $first, after: $after, ranking: $ranking, version: $version, supportedTypes: $supportedTypes) { + ...FeedPostConnection + } } - } - - fragment FeedPostConnection on PostConnection { - pageInfo { - hasNextPage - endCursor + + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + contentHtml + ...UserPost @include(if: $loggedIn) + } + } } - edges { - node { - ...FeedPost - contentHtml - ...UserPost @include(if: $loggedIn) - } + + fragment FeedPost on Post { + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + clickbaitTitleDetected + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - } - - fragment FeedPost on Post { - ...FeedPostInfo - sharedPost { - id - title - image - readTime - permalink - commentsPermalink - createdAt - type - tags - source { + + fragment FeedPostInfo on Post { id - handle - permalink + title image - } - slug - clickbaitTitleDetected - } - trending - feedMeta - collectionSources { - handle - image - } - numCollectionSources - updatedAt - slug - } - - - fragment FeedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - createdAt - commented - bookmarked - views - numUpvotes - numComments - summary - bookmark { - remindAt - } - author { - id - name - image - username - permalink - } - type - tags - source { - id - handle - name - permalink - image - type + readTime + permalink + commentsPermalink + createdAt + commented + bookmarked + views + numUpvotes + numComments + summary + bookmark { + remindAt + } + author { + id + name + image + username + permalink + } + type + tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug + clickbaitTitleDetected } - userState { - vote - flags { - feedbackDismiss - } + + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted } - slug - clickbaitTitleDetected - } - - fragment UserPost on Post { - read - upvoted - commented - bookmarked - downvoted - } `; export const route: Route = { diff --git a/lib/routes/daily/source.ts b/lib/routes/daily/source.ts index b764d552745b..0c9aba5a45e4 100644 --- a/lib/routes/daily/source.ts +++ b/lib/routes/daily/source.ts @@ -15,115 +15,110 @@ interface Source { type: string; } -const sourceFeedQuery = ` -query SourceFeed($source: ID!, $loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $supportedTypes: [String!]) { - page: sourceFeed( - source: $source - first: $first - after: $after - ranking: $ranking - supportedTypes: $supportedTypes - ) { - ...FeedPostConnection - } -} +const sourceFeedQuery = /* GraphQL */ ` + query SourceFeed($source: ID!, $loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $supportedTypes: [String!]) { + page: sourceFeed(source: $source, first: $first, after: $after, ranking: $ranking, supportedTypes: $supportedTypes) { + ...FeedPostConnection + } + } -fragment FeedPostConnection on PostConnection { - pageInfo { - hasNextPage - endCursor - } - edges { - node { - ...FeedPost - pinnedAt - contentHtml - ...UserPost @include(if: $loggedIn) + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + pinnedAt + contentHtml + ...UserPost @include(if: $loggedIn) + } + } } - } -} -fragment FeedPost on Post { - ...FeedPostInfo - sharedPost { - id - title - image - readTime - permalink - commentsPermalink - createdAt - type - tags - source { - id - handle - permalink - image + fragment FeedPost on Post { + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - slug - } - trending - feedMeta - collectionSources { - handle - image - } - numCollectionSources - updatedAt - slug -} -fragment FeedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - createdAt - commented - bookmarked - views - numUpvotes - numComments - summary - bookmark { - remindAt - } - author { - id - name - image - username - permalink - } - type - tags - source { - id - handle - name - permalink - image - type - } - userState { - vote - flags { - feedbackDismiss + fragment FeedPostInfo on Post { + id + title + image + readTime + permalink + commentsPermalink + createdAt + commented + bookmarked + views + numUpvotes + numComments + summary + bookmark { + remindAt + } + author { + id + name + image + username + permalink + } + type + tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug } - } - slug -} -fragment UserPost on Post { - read - upvoted - commented - bookmarked - downvoted -}`; + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted + } +`; export const route: Route = { path: '/source/:sourceId/:innerSharedContent?', diff --git a/lib/routes/daily/squads.ts b/lib/routes/daily/squads.ts index 13af461882a1..5bc199acaa9e 100644 --- a/lib/routes/daily/squads.ts +++ b/lib/routes/daily/squads.ts @@ -3,203 +3,188 @@ import { ViewType } from '@/types'; import { baseUrl, getData, getList, variables } from './utils.js'; -const sourceQuery = ` -query Source($handle: ID!) { - source(id: $handle) { - ...SquadBaseInfo - moderationPostCount - } - } - fragment SquadBaseInfo on Source { - ...SourceBaseInfo - referralUrl - createdAt - flags { - featured - totalPosts - totalViews - totalUpvotes - } - category { - id - title - slug - } - ...PrivilegedMembers - } - fragment SourceBaseInfo on Source { - id - active - handle - name - permalink - public - type - description - image - membersCount - currentMember { - ...CurrentMember - } - memberPostingRole - memberInviteRole - moderationRequired - } - fragment CurrentMember on SourceMember { - user { - id +const sourceQuery = /* GraphQL */ ` + query Source($handle: ID!) { + source(id: $handle) { + ...SquadBaseInfo + moderationPostCount + } } - permissions - role - referralToken - flags { - hideFeedPosts - collapsePinnedPosts + fragment SquadBaseInfo on Source { + ...SourceBaseInfo + referralUrl + createdAt + flags { + featured + totalPosts + totalViews + totalUpvotes + } + category { + id + title + slug + } + ...PrivilegedMembers } - } - fragment PrivilegedMembers on Source { - privilegedMembers { - user { + fragment SourceBaseInfo on Source { id + active + handle name - image permalink - username - bio - reputation - companies { - name - image + public + type + description + image + membersCount + currentMember { + ...CurrentMember } - contentPreference { - status + memberPostingRole + memberInviteRole + moderationRequired + } + fragment CurrentMember on SourceMember { + user { + id + } + permissions + role + referralToken + flags { + hideFeedPosts + collapsePinnedPosts + } + } + fragment PrivilegedMembers on Source { + privilegedMembers { + user { + id + name + image + permalink + username + bio + reputation + companies { + name + image + } + contentPreference { + status + } + } + role } - } - role } - } - `; -const query = ` - query SourceFeed( - $source: ID! - $loggedIn: Boolean! = false - $first: Int - $after: String - $ranking: Ranking - $supportedTypes: [String!] - ) { - page: sourceFeed( - source: $source - first: $first - after: $after - ranking: $ranking - supportedTypes: $supportedTypes - ) { - ...FeedPostConnection +const query = /* GraphQL */ ` + query SourceFeed($source: ID!, $loggedIn: Boolean! = false, $first: Int, $after: String, $ranking: Ranking, $supportedTypes: [String!]) { + page: sourceFeed(source: $source, first: $first, after: $after, ranking: $ranking, supportedTypes: $supportedTypes) { + ...FeedPostConnection + } } - } - - fragment FeedPostConnection on PostConnection { - pageInfo { - hasNextPage - endCursor + + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + pinnedAt + contentHtml + ...UserPost @include(if: $loggedIn) + } + } } - edges { - node { - ...FeedPost - pinnedAt contentHtml - ...UserPost @include(if: $loggedIn) - } + + fragment FeedPost on Post { + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + clickbaitTitleDetected + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - } - - fragment FeedPost on Post { - ...FeedPostInfo - sharedPost { - id - title - image - readTime - permalink - commentsPermalink - createdAt - type - tags - source { + + fragment FeedPostInfo on Post { id - handle - permalink + title image - } - slug - clickbaitTitleDetected - } - trending - feedMeta - collectionSources { - handle - image - } - numCollectionSources - updatedAt - slug - } - - fragment FeedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - createdAt - commented - bookmarked - views - numUpvotes - numComments - summary - bookmark { - remindAt - } - author { - id - name - image - username - permalink - } - type - tags - source { - id - handle - name - permalink - image - type - } - userState { - vote - flags { - feedbackDismiss - } + readTime + permalink + commentsPermalink + createdAt + commented + bookmarked + views + numUpvotes + numComments + summary + bookmark { + remindAt + } + author { + id + name + image + username + permalink + } + type + tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug + clickbaitTitleDetected } - slug - clickbaitTitleDetected - } - - - fragment UserPost on Post { - read - upvoted - commented - bookmarked - downvoted - } + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted + } `; export const route: Route = { diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts index 775d909dd18e..06885d298dd2 100644 --- a/lib/routes/daily/upvoted.ts +++ b/lib/routes/daily/upvoted.ts @@ -3,121 +3,110 @@ import { ViewType } from '@/types'; import { baseUrl, getData, getList, variables } from './utils.js'; -const query = ` - query MostUpvotedFeed( - $loggedIn: Boolean! = false - $first: Int - $after: String - $period: Int - $supportedTypes: [String!] = ["article","share","freeform","video:youtube","collection"] - $source: ID - $tag: String - ) { - page: mostUpvotedFeed(first: $first, after: $after, period: $period, supportedTypes: $supportedTypes, source: $source, tag: $tag) { - ...FeedPostConnection +const query = /* GraphQL */ ` + query MostUpvotedFeed($loggedIn: Boolean! = false, $first: Int, $after: String, $period: Int, $supportedTypes: [String!] = ["article", "share", "freeform", "video:youtube", "collection"], $source: ID, $tag: String) { + page: mostUpvotedFeed(first: $first, after: $after, period: $period, supportedTypes: $supportedTypes, source: $source, tag: $tag) { + ...FeedPostConnection + } } - } - - fragment FeedPostConnection on PostConnection { - pageInfo { - hasNextPage - endCursor + + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + contentHtml + ...UserPost @include(if: $loggedIn) + } + } } - edges { - node { - ...FeedPost - contentHtml - ...UserPost @include(if: $loggedIn) - } + + fragment FeedPost on Post { + ...FeedPostInfo + sharedPost { + id + title + image + readTime + permalink + commentsPermalink + createdAt + type + tags + source { + id + handle + permalink + image + } + slug + clickbaitTitleDetected + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - } - - fragment FeedPost on Post { - ...FeedPostInfo - sharedPost { - id - title - image - readTime - permalink - commentsPermalink - createdAt - type - tags - source { + + fragment FeedPostInfo on Post { id - handle - permalink + title image - } - slug - clickbaitTitleDetected - } - trending - feedMeta - collectionSources { - handle - image - } - numCollectionSources - updatedAt - slug - } - - fragment FeedPostInfo on Post { - id - title - image - readTime - permalink - commentsPermalink - createdAt - commented - bookmarked - views - numUpvotes - numComments - summary - bookmark { - remindAt - } - author { - id - name - image - username - permalink - } - type - tags - source { - id - handle - name - permalink - image - type - } - userState { - vote - flags { - feedbackDismiss - } + readTime + permalink + commentsPermalink + createdAt + commented + bookmarked + views + numUpvotes + numComments + summary + bookmark { + remindAt + } + author { + id + name + image + username + permalink + } + type + tags + source { + id + handle + name + permalink + image + type + } + userState { + vote + flags { + feedbackDismiss + } + } + slug + clickbaitTitleDetected } - slug - clickbaitTitleDetected - } - - - - fragment UserPost on Post { - read - upvoted - commented - bookmarked - downvoted - } + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted + } `; export const route: Route = { diff --git a/lib/routes/daily/user.ts b/lib/routes/daily/user.ts index 387702372c3f..cbffaed740d5 100644 --- a/lib/routes/daily/user.ts +++ b/lib/routes/daily/user.ts @@ -5,151 +5,134 @@ import ofetch from '@/utils/ofetch'; import { baseUrl, getBuildId, getData, getList } from './utils'; -const userPostQuery = ` - query AuthorFeed( - $loggedIn: Boolean! = false - $userId: ID! - $after: String - $first: Int - $supportedTypes: [String!] = [ - "article" - "share" - "freeform" - "video:youtube" - "collection" - ] - ) { - page: authorFeed( - author: $userId - after: $after - first: $first - ranking: TIME - supportedTypes: $supportedTypes - ) { - ...FeedPostConnection +const userPostQuery = /* GraphQL */ ` + query AuthorFeed($loggedIn: Boolean! = false, $userId: ID!, $after: String, $first: Int, $supportedTypes: [String!] = ["article", "share", "freeform", "video:youtube", "collection"]) { + page: authorFeed(author: $userId, after: $after, first: $first, ranking: TIME, supportedTypes: $supportedTypes) { + ...FeedPostConnection + } } - } - fragment FeedPostConnection on PostConnection { - pageInfo { - hasNextPage - endCursor + fragment FeedPostConnection on PostConnection { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...FeedPost + contentHtml + ...UserPost @include(if: $loggedIn) + } + } } - edges { - node { - ...FeedPost - contentHtml - ...UserPost @include(if: $loggedIn) - } + fragment FeedPost on Post { + ...SharedPostInfo + sharedPost { + ...SharedPostInfo + } + trending + feedMeta + collectionSources { + handle + image + } + numCollectionSources + updatedAt + slug } - } - fragment FeedPost on Post { - ...SharedPostInfo - sharedPost { - ...SharedPostInfo - } - trending - feedMeta - collectionSources { - handle - image - } - numCollectionSources - updatedAt - slug - } - fragment SharedPostInfo on Post { - id - title - titleHtml - image - readTime - permalink - commentsPermalink - summary - createdAt - private - upvoted - commented - bookmarked - views - numUpvotes - numComments - videoId - scout { - ...UserShortInfo - } - author { - ...UserShortInfo - } - type - tags - source { - ...SourceBaseInfo - } - downvoted - flags { - promoteToPublic - } - userState { - vote - flags { - feedbackDismiss - } + fragment SharedPostInfo on Post { + id + title + titleHtml + image + readTime + permalink + commentsPermalink + summary + createdAt + private + upvoted + commented + bookmarked + views + numUpvotes + numComments + videoId + scout { + ...UserShortInfo + } + author { + ...UserShortInfo + } + type + tags + source { + ...SourceBaseInfo + } + downvoted + flags { + promoteToPublic + } + userState { + vote + flags { + feedbackDismiss + } + } + slug } - slug - } - fragment SourceBaseInfo on Source { - id - active - handle - name - permalink - public - type - description - image - membersCount - privilegedMembers { - user { + fragment SourceBaseInfo on Source { id - } - role + active + handle + name + permalink + public + type + description + image + membersCount + privilegedMembers { + user { + id + } + role + } + currentMember { + ...CurrentMember + } + memberPostingRole + memberInviteRole } - currentMember { - ...CurrentMember + fragment CurrentMember on SourceMember { + user { + id + } + permissions + role + referralToken + flags { + hideFeedPosts + collapsePinnedPosts + } } - memberPostingRole - memberInviteRole - } - fragment CurrentMember on SourceMember { - user { - id + fragment UserShortInfo on User { + id + name + image + permalink + username + bio + createdAt + reputation } - permissions - role - referralToken - flags { - hideFeedPosts - collapsePinnedPosts + fragment UserPost on Post { + read + upvoted + commented + bookmarked + downvoted } - } - fragment UserShortInfo on User { - id - name - image - permalink - username - bio - createdAt - reputation - } - fragment UserPost on Post { - read - upvoted - commented - bookmarked - downvoted - }`; +`; export const route: Route = { path: '/user/:userId/:innerSharedContent?', diff --git a/lib/routes/daily/utils.tsx b/lib/routes/daily/utils.tsx index 0d872befda98..fb1fbfcf8274 100644 --- a/lib/routes/daily/utils.tsx +++ b/lib/routes/daily/utils.tsx @@ -8,7 +8,7 @@ import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; export const baseUrl = 'https://app.daily.dev'; -const gqlUrl = `https://api.daily.dev/graphql`; +const gqlUrl = 'https://api.daily.dev/graphql'; export const variables = { version: 54, loggedIn: false, diff --git a/lib/routes/dailypush/all.ts b/lib/routes/dailypush/all.ts index d15bb923de9a..6a39fe133178 100644 --- a/lib/routes/dailypush/all.ts +++ b/lib/routes/dailypush/all.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { BASE_URL, enhanceItemsWithSummaries, fetchPageHtml, parseArticles } from './utils'; @@ -42,7 +42,7 @@ async function handler(ctx) { const { sort = '' } = ctx.req.param(); const url = sort ? `${BASE_URL}/${sort}` : BASE_URL; - const browser = await puppeteer(); + const browser = await playwright(); try { const html = await fetchPageHtml(browser, url, 'article'); const $ = load(html); diff --git a/lib/routes/dailypush/tags.ts b/lib/routes/dailypush/tags.ts index da50a1556210..c1430d29ae47 100644 --- a/lib/routes/dailypush/tags.ts +++ b/lib/routes/dailypush/tags.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { BASE_URL, enhanceItemsWithSummaries, fetchPageHtml, parseArticles } from './utils'; @@ -43,7 +43,7 @@ async function handler(ctx) { const { tag, sort = 'trending' } = ctx.req.param(); const url = `${BASE_URL}/${tag}/${sort}`; - const browser = await puppeteer(); + const browser = await playwright(); try { const html = await fetchPageHtml(browser, url, 'article'); const $ = load(html); diff --git a/lib/routes/dailypush/utils.ts b/lib/routes/dailypush/utils.ts index 2854207eae8c..1fd24ec3ad85 100644 --- a/lib/routes/dailypush/utils.ts +++ b/lib/routes/dailypush/utils.ts @@ -1,11 +1,11 @@ import type { CheerioAPI } from 'cheerio'; import { load } from 'cheerio'; -import type { Browser, Page } from 'rebrowser-puppeteer'; import type { DataItem } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; import { parseRelativeDate } from '@/utils/parse-date'; +import type { Browser, Page } from '@/utils/playwright'; export const BASE_URL = 'https://www.dailypush.dev'; @@ -209,7 +209,7 @@ function extractPubDate(article: ReturnType): Date | undefined { */ function parseArticle(article: ReturnType, $: CheerioAPI, baseUrl: string): (DataItem & ArticleItem) | null { // Find the title link in h2 > a - const titleLink = article.find('h2 a[href^="http"]').first(); + const titleLink = article.find('h2 a[href^="http"]'); if (titleLink.length === 0) { return null; } @@ -222,11 +222,11 @@ function parseArticle(article: ReturnType, $: CheerioAPI, baseUrl: s } const author = extractAuthor(article); - const description = article.find('p.text-sm.text-muted-foreground').first().text().trim() || undefined; + const description = article.find('p.text-sm.text-muted-foreground').text().trim(); const categories = extractCategories(article, $); - const footer = article.find('.flex.items-center.justify-between.gap-4.flex-wrap').first(); - const summaryLink = footer.find('a[href*="/article/"]').first().attr('href'); + const footer = article.find('.flex.items-center.justify-between.gap-4.flex-wrap'); + const summaryLink = footer.find('a[href*="/article/"]').attr('href'); const dailyPushUrl = summaryLink ? `${baseUrl}${summaryLink}` : undefined; const pubDate = extractPubDate(article); @@ -271,7 +271,7 @@ export async function enhanceItemsWithSummaries(browser: Browser, items: Article try { const html = await fetchPageHtml(browser, item.dailyPushUrl!, 'p.font-ibm-plex-sans.leading-relaxed'); const $ = load(html); - const summary = $('p.font-ibm-plex-sans.leading-relaxed').first(); + const summary = $('p.font-ibm-plex-sans.leading-relaxed'); if (summary.length > 0 && summary.text().trim()) { item.description = summary.text().trim(); } diff --git a/lib/routes/damai/activity.tsx b/lib/routes/damai/activity.tsx index 24edf20463a8..6e2a41f70105 100644 --- a/lib/routes/damai/activity.tsx +++ b/lib/routes/damai/activity.tsx @@ -21,7 +21,7 @@ export const route: Route = { name: '票务更新', maintainers: ['hoilc', 'Konano'], handler, - description: `城市、分类名、子分类名,请参见[大麦网搜索页面](https://search.damai.cn/search.htm)`, + description: '城市、分类名、子分类名,请参见[大麦网搜索页面](https://search.damai.cn/search.htm)', }; async function handler(ctx) { diff --git a/lib/routes/dangdang/notice.ts b/lib/routes/dangdang/notice.ts index 014290fbd5a5..1de1063b21d6 100644 --- a/lib/routes/dangdang/notice.ts +++ b/lib/routes/dangdang/notice.ts @@ -32,9 +32,9 @@ export const route: Route = { handler, description: `| 类型 | type | | -------- | ---- | -| 全部 | 0 | -| 其他 | 1 | -| 规则变更 | 2 |`, +| 全部 | 0 | +| 其他 | 1 | +| 规则变更 | 2 |`, }; async function handler(ctx) { @@ -63,7 +63,7 @@ async function handler(ctx) { return { title: `当当开放平台 - ${typeMap[type] || typeMap[0]}`, - link: `https://open.dangdang.com/home/notice/message/1`, + link: 'https://open.dangdang.com/home/notice/message/1', item: result, }; } diff --git a/lib/routes/daum/potplayer.ts b/lib/routes/daum/potplayer.ts index 653c58004610..919afa2166b1 100644 --- a/lib/routes/daum/potplayer.ts +++ b/lib/routes/daum/potplayer.ts @@ -130,16 +130,15 @@ export const route: Route = { To subscribe to [Potplayer Update History](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html), where the source URL is \`https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/daum/potplayer/Eng\`](https://rsshub.app/daum/potplayer/Eng). ::: -| Language | Id | -| ---------------------------------------------------------------------------------- | -------------------------------------------- | -| [한국어](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/Update.html) | | -| [中文(简体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html) | [Chs](https://rsshub.app/daum/potplayer/Chs) | -| [中文(繁体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateCht.html) | [Cht](https://rsshub.app/daum/potplayer/Cht) | -| [ENGLISH](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | -| [Українська](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | -| [РУССКИЙ](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateRus.html) | [Eng](https://rsshub.app/daum/potplayer/Rus) | -| [Polski](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdatePol.html) | [Eng](https://rsshub.app/daum/potplayer/Pol) | -`, +| Language | Id | +| ----------------------------------------------------------------------------------- | -------------------------------------------- | +| [한국어](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/Update.html) | | +| [中文 (简体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html) | [Chs](https://rsshub.app/daum/potplayer/Chs) | +| [中文 (繁体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateCht.html) | [Cht](https://rsshub.app/daum/potplayer/Cht) | +| [ENGLISH](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | +| [Українська](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | +| [РУССКИЙ](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateRus.html) | [Eng](https://rsshub.app/daum/potplayer/Rus) | +| [Polski](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdatePol.html) | [Eng](https://rsshub.app/daum/potplayer/Pol) |`, categories: ['program-update'], features: { requireConfig: false, @@ -244,15 +243,14 @@ To subscribe to [Potplayer Update History](https://t1.daumcdn.net/potplayer/PotP 若订阅 [Potplayer Update History](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html),网址为 \`https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html\`,请截取 \`https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/Update\` 到末尾的部分 \`Chs\` 作为 \`lang\` 参数填入,此时目标路由为 [\`/daum/potplayer/Chs\`](https://rsshub.app/daum/potplayer/Chs)。 ::: -| Language | Id | -| ---------------------------------------------------------------------------------- | -------------------------------------------- | -| [한국어](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/Update.html) | | -| [中文(简体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html) | [Chs](https://rsshub.app/daum/potplayer/Chs) | -| [中文(繁体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateCht.html) | [Cht](https://rsshub.app/daum/potplayer/Cht) | -| [ENGLISH](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | -| [Українська](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | -| [РУССКИЙ](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateRus.html) | [Eng](https://rsshub.app/daum/potplayer/Rus) | -| [Polski](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdatePol.html) | [Eng](https://rsshub.app/daum/potplayer/Pol) | -`, +| Language | Id | +| ----------------------------------------------------------------------------------- | -------------------------------------------- | +| [한국어](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/Update.html) | | +| [中文 (简体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateChs.html) | [Chs](https://rsshub.app/daum/potplayer/Chs) | +| [中文 (繁体)](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateCht.html) | [Cht](https://rsshub.app/daum/potplayer/Cht) | +| [ENGLISH](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | +| [Українська](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateEng.html) | [Eng](https://rsshub.app/daum/potplayer/Eng) | +| [РУССКИЙ](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdateRus.html) | [Eng](https://rsshub.app/daum/potplayer/Rus) | +| [Polski](https://t1.daumcdn.net/potplayer/PotPlayer/v4/Update2/UpdatePol.html) | [Eng](https://rsshub.app/daum/potplayer/Pol) |`, }, }; diff --git a/lib/routes/dbaplus/new.ts b/lib/routes/dbaplus/new.ts index 2aa195e8a096..7e877f78a079 100644 --- a/lib/routes/dbaplus/new.ts +++ b/lib/routes/dbaplus/new.ts @@ -244,32 +244,31 @@ export const route: Route = {
    更多分类 - | [全部](https://dbaplus.cn/news-9-1.html) | [数据库](https://dbaplus.cn/news-153-1.html) | [运维](https://dbaplus.cn/news-134-1.html) | [大数据](https://dbaplus.cn/news-73-1.html) | [架构](https://dbaplus.cn/news-141-1.html) | - | ---------------------------------------- | -------------------------------------------- | ------------------------------------------ | ------------------------------------------- | ------------------------------------------ | - | [9](https://rsshub.app/dbaplus/news/9) | [153](https://rsshub.app/dbaplus/news/153) | [134](https://rsshub.app/dbaplus/news/134) | [73](https://rsshub.app/dbaplus/news/73) | [141](https://rsshub.app/dbaplus/news/141) | +| [全部](https://dbaplus.cn/news-9-1.html) | [数据库](https://dbaplus.cn/news-153-1.html) | [运维](https://dbaplus.cn/news-134-1.html) | [大数据](https://dbaplus.cn/news-73-1.html) | [架构](https://dbaplus.cn/news-141-1.html) | +| ---------------------------------------- | -------------------------------------------- | ------------------------------------------ | ------------------------------------------- | ------------------------------------------ | +| [9](https://rsshub.app/dbaplus/news/9) | [153](https://rsshub.app/dbaplus/news/153) | [134](https://rsshub.app/dbaplus/news/134) | [73](https://rsshub.app/dbaplus/news/73) | [141](https://rsshub.app/dbaplus/news/141) | - | [PaaS云](https://dbaplus.cn/news-72-1.html) | [职场生涯](https://dbaplus.cn/news-149-1.html) | [标准评估](https://dbaplus.cn/news-248-1.html) | [这里有毒](https://dbaplus.cn/news-21-1.html) | - | ------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | --------------------------------------------- | - | [72](https://rsshub.app/dbaplus/news/72) | [149](https://rsshub.app/dbaplus/news/149) | [248](https://rsshub.app/dbaplus/news/248) | [21](https://rsshub.app/dbaplus/news/21) | +| [PaaS 云](https://dbaplus.cn/news-72-1.html) | [职场生涯](https://dbaplus.cn/news-149-1.html) | [标准评估](https://dbaplus.cn/news-248-1.html) | [这里有毒](https://dbaplus.cn/news-21-1.html) | +| -------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | --------------------------------------------- | +| [72](https://rsshub.app/dbaplus/news/72) | [149](https://rsshub.app/dbaplus/news/149) | [248](https://rsshub.app/dbaplus/news/248) | [21](https://rsshub.app/dbaplus/news/21) | - #### [数据库](https://dbaplus.cn/news-153-1.html) +#### [数据库](https://dbaplus.cn/news-153-1.html) - | [国产数据库](https://dbaplus.cn/news-217-1.html) | [ORACLE](https://dbaplus.cn/news-10-1.html) | [MySQL](https://dbaplus.cn/news-11-1.html) | [SQL优化](https://dbaplus.cn/news-155-1.html) | [Newsletter](https://dbaplus.cn/news-156-1.html) | - | ------------------------------------------------ | ------------------------------------------- | ------------------------------------------ | --------------------------------------------- | ------------------------------------------------ | - | [217](https://rsshub.app/dbaplus/news/217) | [10](https://rsshub.app/dbaplus/news/10) | [11](https://rsshub.app/dbaplus/news/11) | [155](https://rsshub.app/dbaplus/news/155) | [156](https://rsshub.app/dbaplus/news/156) | +| [国产数据库](https://dbaplus.cn/news-217-1.html) | [ORACLE](https://dbaplus.cn/news-10-1.html) | [MySQL](https://dbaplus.cn/news-11-1.html) | [SQL 优化](https://dbaplus.cn/news-155-1.html) | [Newsletter](https://dbaplus.cn/news-156-1.html) | +| ------------------------------------------------ | ------------------------------------------- | ------------------------------------------ | ---------------------------------------------- | ------------------------------------------------ | +| [217](https://rsshub.app/dbaplus/news/217) | [10](https://rsshub.app/dbaplus/news/10) | [11](https://rsshub.app/dbaplus/news/11) | [155](https://rsshub.app/dbaplus/news/155) | [156](https://rsshub.app/dbaplus/news/156) | - | [其它](https://dbaplus.cn/news-154-1.html) | - | ------------------------------------------ | - | [154](https://rsshub.app/dbaplus/news/154) | +| [其它](https://dbaplus.cn/news-154-1.html) | +| ------------------------------------------ | +| [154](https://rsshub.app/dbaplus/news/154) | - #### [这里有毒](https://dbaplus.cn/news-21-1.html) +#### [这里有毒](https://dbaplus.cn/news-21-1.html) - | [最新活动](https://dbaplus.cn/news-152-1.html) | [往期干货](https://dbaplus.cn/news-148-1.html) | [特别策划](https://dbaplus.cn/news-150-1.html) | [荐书](https://dbaplus.cn/news-151-1.html) | - | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ------------------------------------------ | - | [152](https://rsshub.app/dbaplus/news/152) | [148](https://rsshub.app/dbaplus/news/148) | [150](https://rsshub.app/dbaplus/news/150) | [151](https://rsshub.app/dbaplus/news/151) | +| [最新活动](https://dbaplus.cn/news-152-1.html) | [往期干货](https://dbaplus.cn/news-148-1.html) | [特别策划](https://dbaplus.cn/news-150-1.html) | [荐书](https://dbaplus.cn/news-151-1.html) | +| ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ------------------------------------------ | +| [152](https://rsshub.app/dbaplus/news/152) | [148](https://rsshub.app/dbaplus/news/148) | [150](https://rsshub.app/dbaplus/news/150) | [151](https://rsshub.app/dbaplus/news/151) | -
    -`, +`, categories: ['programming'], features: { requireConfig: false, diff --git a/lib/routes/dcard/section.ts b/lib/routes/dcard/section.ts index 64337340fd4a..a32f14582405 100644 --- a/lib/routes/dcard/section.ts +++ b/lib/routes/dcard/section.ts @@ -1,7 +1,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import utils from './utils'; @@ -26,18 +26,18 @@ export const route: Route = { async function handler(ctx) { const { type = 'latest', section = 'posts' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; - const browser = await puppeteer(); + const browser = await playwright(); - let link = `https://www.dcard.tw/f`; - let api = `https://www.dcard.tw/service/api/v2`; - let title = `Dcard - `; + let link = 'https://www.dcard.tw/f'; + let api = 'https://www.dcard.tw/service/api/v2'; + let title = 'Dcard - '; if (section !== 'posts' && section !== 'popular' && section !== 'latest') { link += `/${section}`; api += `/forums/${section}`; title += `${section} - `; } - api += `/posts`; + api += '/posts'; if (type === 'popular') { link += '?latest=false'; api += '?popular=true'; diff --git a/lib/routes/ddosi/index.ts b/lib/routes/ddosi/index.ts index 5e7ec5b9f7af..7035be4d5082 100644 --- a/lib/routes/ddosi/index.ts +++ b/lib/routes/ddosi/index.ts @@ -50,7 +50,7 @@ async function handler() { }); return { - title: `雨苁`, + title: '雨苁', link: String(url), item: items, }; diff --git a/lib/routes/deadbydaylight/namespace.ts b/lib/routes/deadbydaylight/namespace.ts index 7333371e3183..8e89112b7c3e 100644 --- a/lib/routes/deadbydaylight/namespace.ts +++ b/lib/routes/deadbydaylight/namespace.ts @@ -3,9 +3,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'DeadbyDaylight', url: 'deadbydaylight.com', - description: ` - DeadbyDaylight Official - `, + description: 'DeadbyDaylight Official', zh: { name: '黎明杀机', }, diff --git a/lib/routes/dealstreetasia/home.ts b/lib/routes/dealstreetasia/home.ts index 5d732c18fa2d..e0959e5b0ef0 100644 --- a/lib/routes/dealstreetasia/home.ts +++ b/lib/routes/dealstreetasia/home.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; // An HTML parser with an API similar to jQuery import type { Route } from '@/types'; // import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; // Unified request library used -// import puppeteer from '@/utils/puppeteer'; +// import playwright from '@/utils/playwright'; // import { parseDate } from '@/utils/parse-date'; export const route: Route = { diff --git a/lib/routes/dealstreetasia/section.ts b/lib/routes/dealstreetasia/section.ts index 876e68a72fce..a035b897f870 100644 --- a/lib/routes/dealstreetasia/section.ts +++ b/lib/routes/dealstreetasia/section.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; // An HTML parser with an API similar to jQuery import type { Route } from '@/types'; // import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; // Unified request library used -// import puppeteer from '@/utils/puppeteer'; +// import playwright from '@/utils/playwright'; // import { parseDate } from '@/utils/parse-date'; export const route: Route = { diff --git a/lib/routes/dedao/articles.ts b/lib/routes/dedao/articles.ts index c0be9f871196..aa618daa4d5f 100644 --- a/lib/routes/dedao/articles.ts +++ b/lib/routes/dedao/articles.ts @@ -79,7 +79,7 @@ async function handler(ctx) { const headers = { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json;charset=UTF-8', - Referer: `https://m.igetget.com/share/course/free/detail?id=nb9L2q1e3OxKBPNsdoJrgN8P0Rwo6B`, + Referer: 'https://m.igetget.com/share/course/free/detail?id=nb9L2q1e3OxKBPNsdoJrgN8P0Rwo6B', Origin: 'https://m.igetget.com', }; const max_id = 0; diff --git a/lib/routes/dedao/index.ts b/lib/routes/dedao/index.ts index 42d1c1546ecd..773cf893b1d1 100644 --- a/lib/routes/dedao/index.ts +++ b/lib/routes/dedao/index.ts @@ -12,9 +12,9 @@ export const route: Route = { categories: ['new-media'], example: '/dedao', parameters: { category: '分类,见下表,默认为`news`' }, - description: `| 新闻 | 人物故事 | 视频 | -| ---- | ---- | ---- | -| news | figure | video |`, + description: `| 新闻 | 人物故事 | 视频 | +| ---- | -------- | ----- | +| news | figure | video |`, handler, }; diff --git a/lib/routes/deepl/blog.ts b/lib/routes/deepl/blog.ts index 0fb408d85bc3..99ab952f5a3e 100644 --- a/lib/routes/deepl/blog.ts +++ b/lib/routes/deepl/blog.ts @@ -252,8 +252,7 @@ To subscribe to [Blog](https://www.deepl.com/en/blog), where the source URL is \ | [Українська](https://www.deepl.com/uk/blog) | [uk](https://rsshub.app/deepl/blog/uk) | | [العربية](https://www.deepl.com/ar/blog) | [ar](https://rsshub.app/deepl/blog/ar) | - -`, +`, categories: ['new-media'], features: { requireConfig: false, @@ -414,7 +413,6 @@ To subscribe to [Blog](https://www.deepl.com/en/blog), where the source URL is \ | [Українська](https://www.deepl.com/uk/blog) | [uk](https://rsshub.app/deepl/blog/uk) | | [العربية](https://www.deepl.com/ar/blog) | [ar](https://rsshub.app/deepl/blog/ar) | - -`, +`, }, }; diff --git a/lib/routes/deeplearning/the-batch.ts b/lib/routes/deeplearning/the-batch.ts index c52be09194de..b82aedbae1f6 100644 --- a/lib/routes/deeplearning/the-batch.ts +++ b/lib/routes/deeplearning/the-batch.ts @@ -144,7 +144,7 @@ export const route: Route = { example: '/deeplearning/the-batch', parameters: { tag: 'Tag, Weekly Issues by default' }, description: `::: tip - If you subscribe to [Data Points](https://www.deeplearning.ai/the-batch/tag/data-points/),where the URL is \`https://www.deeplearning.ai/the-batch/tag/data-points/\`, extract the part \`https://www.deeplearning.ai/the-batch/tag\` to the end, which is \`data-points\`, and use it as the parameter to fill in. Therefore, the route will be [\`/deeplearning/the-batch/data-points\`](https://rsshub.app/deeplearning/the-batch/data-points). +If you subscribe to [Data Points](https://www.deeplearning.ai/the-batch/tag/data-points/),where the URL is \`https://www.deeplearning.ai/the-batch/tag/data-points/\`, extract the part \`https://www.deeplearning.ai/the-batch/tag\` to the end, which is \`data-points\`, and use it as the parameter to fill in. Therefore, the route will be [\`/deeplearning/the-batch/data-points\`](https://rsshub.app/deeplearning/the-batch/data-points). ::: @@ -173,8 +173,7 @@ export const route: Route = { | [DeepLearning.AI News](https://www.deeplearning.ai/the-batch/tag/deeplearning-ai-news/) | [deeplearning-ai-news](https://rsshub.app/deeplearning/the-batch/deeplearning-ai-news) | | [AI Careers](https://www.deeplearning.ai/the-batch/tag/ai-careers/) | [ai-careers](https://rsshub.app/deeplearning/the-batch/ai-careers) | | [Just For Fun](https://www.deeplearning.ai/the-batch/tag/just-for-fun/) | [just-for-fun](https://rsshub.app/deeplearning/the-batch/just-for-fun) | -| [Learning & Education](https://www.deeplearning.ai/the-batch/tag/learning-education/) | [learning-education](https://rsshub.app/deeplearning/the-batch/learning-education) | - `, +| [Learning & Education](https://www.deeplearning.ai/the-batch/tag/learning-education/) | [learning-education](https://rsshub.app/deeplearning/the-batch/learning-education) |`, categories: ['programming'], features: { diff --git a/lib/routes/dehenglaw/index.ts b/lib/routes/dehenglaw/index.ts index f0d63a65da9b..c5966d874ace 100644 --- a/lib/routes/dehenglaw/index.ts +++ b/lib/routes/dehenglaw/index.ts @@ -89,11 +89,12 @@ export const route: Route = { example: '/dehenglaw/CN/paper', parameters: { language: '语言,默认为中文,即 CN,可在对应分类页 URL 中找到,可选 CN 和 EN', category: '分类,默认为专业文章,即 paper,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [专业文章](https://dehenglaw.com/),网址为 \`https://www.dehenglaw.com/CN/paper/0008/000902.aspx\`。截取 \`https://dehenglaw.com/\` 到末尾 \`/0008/000902.aspx\` 的部分 \`CN/paper\` 作为参数填入,此时路由为 [\`/dehenglaw/CN/paper\`](https://rsshub.app/dehenglaw/CN/paper)。 +若订阅 [专业文章](https://dehenglaw.com/),网址为 \`https://www.dehenglaw.com/CN/paper/0008/000902.aspx\`。截取 \`https://dehenglaw.com/\` 到末尾 \`/0008/000902.aspx\` 的部分 \`CN/paper\` 作为参数填入,此时路由为 [\`/dehenglaw/CN/paper\`](https://rsshub.app/dehenglaw/CN/paper)。 | 专业文章 | 出版物 | 德恒论坛 | | -------- | ------- | -------- | | paper | publish | luntan | + :::`, categories: ['new-media'], diff --git a/lib/routes/denonbu/news.ts b/lib/routes/denonbu/news.ts index e1fc06161a38..e3617d38b926 100644 --- a/lib/routes/denonbu/news.ts +++ b/lib/routes/denonbu/news.ts @@ -15,25 +15,24 @@ export const route: Route = { area: 'The id of the area or category; values are as follows.', }, description: `**Area** -| ID | Group name/Area name | -| ------------- | ------------------------------------------------ | -| akiba | 外神田文芸高校 | -| harajuku | 神宮前参道學園 | -| azabu | 港白金女学院 | -| shibuya | 帝音国際学院 | -| kabuki | 真新宿GR学園 | -| deep-okubo | Bellemule(深大久保DJ&ダンスアカデミー) | -| deep-okubo-k | 輝きノスタルジア(深大久保DJ&ダンスアカデミー) | -| shinsaibashi | OKINI☆PARTY'S(心斎橋演芸高校) | -| ikebukuro | 池袋電音部(池袋空乗院高校) | -| neotokyo | 東京電脳(東京電脳学園) | -| neonakano | 中野電脳(中野電脳学園) | -| shimokitazawa | Ma'Scar'Piece(北沢音箱高校) | -**Category** -Working category IDs include \`news\` (the default), \`event\`, \`goods\`, \`comic\`, \`movie\`, \`music\` or \`livearchives\`. +| ID | Group name/Area name | +| ------------- | ------------------------------------------------- | +| akiba | 外神田文芸高校 | +| harajuku | 神宮前参道學園 | +| azabu | 港白金女学院 | +| shibuya | 帝音国際学院 | +| kabuki | 真新宿 GR 学園 | +| deep-okubo | Bellemule(深大久保 DJ&ダンスアカデミー) | +| deep-okubo-k | 輝きノスタルジア(深大久保 DJ&ダンスアカデミー) | +| shinsaibashi | OKINI☆PARTY'S(心斎橋演芸高校) | +| ikebukuro | 池袋電音部(池袋空乗院高校) | +| neotokyo | 東京電脳(東京電脳学園) | +| neonakano | 中野電脳(中野電脳学園) | +| shimokitazawa | Ma'Scar'Piece(北沢音箱高校) | -`, +**Category** +Working category IDs include \`news\` (the default), \`event\`, \`goods\`, \`comic\`, \`movie\`, \`music\` or \`livearchives\`.`, features: { requireConfig: false, requirePuppeteer: false, diff --git a/lib/routes/dev.to/guides.ts b/lib/routes/dev.to/guides.ts index 19a5dbcac6a9..b4ad5ed216e8 100644 --- a/lib/routes/dev.to/guides.ts +++ b/lib/routes/dev.to/guides.ts @@ -64,7 +64,7 @@ async function handler() { const authorAvatar = $article('.crayons-article__header__meta .radius-full').attr('src'); // Extract publication date const dateElement = $article('time[datetime]').first(); - const dateString = dateElement.attr('datetime') || undefined; + const dateString = dateElement.attr('datetime'); const pubDate = dateString ? parseDate(dateString) : undefined; // Extract tags const tags = $article('.spec__tags .crayons-tag') diff --git a/lib/routes/dewu/techblog.ts b/lib/routes/dewu/techblog.ts index 2aab390aeaf6..d90d606a3143 100644 --- a/lib/routes/dewu/techblog.ts +++ b/lib/routes/dewu/techblog.ts @@ -36,16 +36,16 @@ export const route: Route = { name: '技术博客', maintainers: ['zhenlohuang'], handler, - description: `| 分类 | ID | -| --- | --- | -| 大前端 | 1 | -| Java | 2 | -| 音视频 | 3 | -| 测试 | 4 | -| Golang | 5 | -| AI&数据 | 6 | -| 运维&稳定生产 | 7 | -| 技术思考 | 8 |`, + description: `| 分类 | ID | +| --------------- | -- | +| 大前端 | 1 | +| Java | 2 | +| 音视频 | 3 | +| 测试 | 4 | +| Golang | 5 | +| AI & 数据 | 6 | +| 运维 & 稳定生产 | 7 | +| 技术思考 | 8 |`, }; async function handler(ctx) { diff --git a/lib/routes/dgtle/article.ts b/lib/routes/dgtle/article.ts index 1ec82a3568c1..f88ee9b9fa55 100644 --- a/lib/routes/dgtle/article.ts +++ b/lib/routes/dgtle/article.ts @@ -165,35 +165,34 @@ export const route: Route = { ], }, }, - description: `:::tip + description: `::: tip 订阅 [数码](https://www.dgtle.com/article),其对应分类 ID 为 \`20\`,此时路由为 [\`/dgtle/article/20\`](https://rsshub.app/dgtle/article/20)。 :::
    更多分类 - | [全部](https://www.dgtle.com/article) | [数码](https://www.dgtle.com/article) | [手机](https://www.dgtle.com/article) | [平板](https://www.dgtle.com/article) | [笔电](https://www.dgtle.com/article) | - | --------------------------------------- | ----------------------------------------- | ----------------------------------------- | --------------------------------------- | ----------------------------------------- | - | [0](https://rsshub.app/dgtle/article/0) | [20](https://rsshub.app/dgtle/article/20) | [18](https://rsshub.app/dgtle/article/18) | [4](https://rsshub.app/dgtle/article/4) | [17](https://rsshub.app/dgtle/article/17) | +| [全部](https://www.dgtle.com/article) | [数码](https://www.dgtle.com/article) | [手机](https://www.dgtle.com/article) | [平板](https://www.dgtle.com/article) | [笔电](https://www.dgtle.com/article) | +| --------------------------------------- | ----------------------------------------- | ----------------------------------------- | --------------------------------------- | ----------------------------------------- | +| [0](https://rsshub.app/dgtle/article/0) | [20](https://rsshub.app/dgtle/article/20) | [18](https://rsshub.app/dgtle/article/18) | [4](https://rsshub.app/dgtle/article/4) | [17](https://rsshub.app/dgtle/article/17) | - | [影音](https://www.dgtle.com/article) | [汽车](https://www.dgtle.com/article) | [视频](https://www.dgtle.com/article) | [摄影](https://www.dgtle.com/article) | [露营](https://www.dgtle.com/article) | - | --------------------------------------- | ------------------------------------------- | ------------------------------------------- | ----------------------------------------- | ------------------------------------------- | - | [5](https://rsshub.app/dgtle/article/5) | [401](https://rsshub.app/dgtle/article/401) | [395](https://rsshub.app/dgtle/article/395) | [22](https://rsshub.app/dgtle/article/22) | [405](https://rsshub.app/dgtle/article/405) | +| [影音](https://www.dgtle.com/article) | [汽车](https://www.dgtle.com/article) | [视频](https://www.dgtle.com/article) | [摄影](https://www.dgtle.com/article) | [露营](https://www.dgtle.com/article) | +| --------------------------------------- | ------------------------------------------- | ------------------------------------------- | ----------------------------------------- | ------------------------------------------- | +| [5](https://rsshub.app/dgtle/article/5) | [401](https://rsshub.app/dgtle/article/401) | [395](https://rsshub.app/dgtle/article/395) | [22](https://rsshub.app/dgtle/article/22) | [405](https://rsshub.app/dgtle/article/405) | - | [家装](https://www.dgtle.com/article) | [活动](https://www.dgtle.com/article) | [生活](https://www.dgtle.com/article) | [旅行](https://www.dgtle.com/article) | [骑行](https://www.dgtle.com/article) | - | ------------------------------------------- | ------------------------------------------- | ----------------------------------------- | ------------------------------------------- | ------------------------------------------- | - | [402](https://rsshub.app/dgtle/article/402) | [138](https://rsshub.app/dgtle/article/138) | [34](https://rsshub.app/dgtle/article/34) | [137](https://rsshub.app/dgtle/article/137) | [412](https://rsshub.app/dgtle/article/412) | +| [家装](https://www.dgtle.com/article) | [活动](https://www.dgtle.com/article) | [生活](https://www.dgtle.com/article) | [旅行](https://www.dgtle.com/article) | [骑行](https://www.dgtle.com/article) | +| ------------------------------------------- | ------------------------------------------- | ----------------------------------------- | ------------------------------------------- | ------------------------------------------- | +| [402](https://rsshub.app/dgtle/article/402) | [138](https://rsshub.app/dgtle/article/138) | [34](https://rsshub.app/dgtle/article/34) | [137](https://rsshub.app/dgtle/article/137) | [412](https://rsshub.app/dgtle/article/412) | - | [游戏](https://www.dgtle.com/article) | [宠物](https://www.dgtle.com/article) | [时尚](https://www.dgtle.com/article) | [运动](https://www.dgtle.com/article) | [应用](https://www.dgtle.com/article) | - | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | - | [411](https://rsshub.app/dgtle/article/411) | [407](https://rsshub.app/dgtle/article/407) | [406](https://rsshub.app/dgtle/article/406) | [403](https://rsshub.app/dgtle/article/403) | [135](https://rsshub.app/dgtle/article/135) | +| [游戏](https://www.dgtle.com/article) | [宠物](https://www.dgtle.com/article) | [时尚](https://www.dgtle.com/article) | [运动](https://www.dgtle.com/article) | [应用](https://www.dgtle.com/article) | +| ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | ------------------------------------------- | +| [411](https://rsshub.app/dgtle/article/411) | [407](https://rsshub.app/dgtle/article/407) | [406](https://rsshub.app/dgtle/article/406) | [403](https://rsshub.app/dgtle/article/403) | [135](https://rsshub.app/dgtle/article/135) | - | [玩物](https://www.dgtle.com/article) | [周边](https://www.dgtle.com/article) | [文具](https://www.dgtle.com/article) | [官方](https://www.dgtle.com/article) | - | ----------------------------------------- | ----------------------------------------- | --------------------------------------- | ------------------------------------------- | - | [75](https://rsshub.app/dgtle/article/75) | [19](https://rsshub.app/dgtle/article/19) | [7](https://rsshub.app/dgtle/article/7) | [400](https://rsshub.app/dgtle/article/400) | +| [玩物](https://www.dgtle.com/article) | [周边](https://www.dgtle.com/article) | [文具](https://www.dgtle.com/article) | [官方](https://www.dgtle.com/article) | +| ----------------------------------------- | ----------------------------------------- | --------------------------------------- | ------------------------------------------- | +| [75](https://rsshub.app/dgtle/article/75) | [19](https://rsshub.app/dgtle/article/19) | [7](https://rsshub.app/dgtle/article/7) | [400](https://rsshub.app/dgtle/article/400) | -
    -`, +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/dgtle/news.ts b/lib/routes/dgtle/news.ts index b30934d49de6..de5a2a7872a3 100644 --- a/lib/routes/dgtle/news.ts +++ b/lib/routes/dgtle/news.ts @@ -67,14 +67,13 @@ export const route: Route = { ], }, }, - description: `:::tip + description: `::: tip 订阅 [最新](https://www.dgtle.com/news),其对应分类 ID 为 \`0\`,此时路由为 [\`/dgtle/news/0\`](https://rsshub.app/dgtle/news/0)。 ::: | 最新 | 直播 | 资讯 | 每日一言 | | ---- | ---- | ---- | -------- | -| 0 | 395 | 396 | 388 | -`, +| 0 | 395 | 396 | 388 |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/dgtle/tag.ts b/lib/routes/dgtle/tag.ts index 6a8cc9ddcd35..19fe571c530e 100644 --- a/lib/routes/dgtle/tag.ts +++ b/lib/routes/dgtle/tag.ts @@ -28,7 +28,7 @@ export const handler = async (ctx: Context): Promise => { const items: DataItem[] = ProcessFeedItems(limit, response.data.dataList, $); - const title: string | undefined = $(`div.tags-detail-top-1 h2`).text(); + const title: string | undefined = $('div.tags-detail-top-1 h2').text(); return { title: `${$('title').text().trim().split(/\s/)[0]}${title ? ` - ${title}` : id}`, @@ -54,10 +54,9 @@ export const route: Route = { description: '标签 ID,可在对应标签页 URL 中找到', }, }, - description: `:::tip + description: `::: tip 订阅 [#手机讨论区](https://www.dgtle.com/tag-394-1.html),其源网址为 \`https://www.dgtle.com/tag-394-1.html\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/dgtle/tag/394\`](https://rsshub.app/dgtle/tag/394)。 -::: -`, +:::`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/dgut/jwb.ts b/lib/routes/dgut/jwb.ts index dedbc368f67b..2f31cf7606f4 100644 --- a/lib/routes/dgut/jwb.ts +++ b/lib/routes/dgut/jwb.ts @@ -26,9 +26,9 @@ export const route: Route = { }, ], name: '教务部通知公告', - description: `| 教学动态 | 教务通知 | 教研通知 | 实践通知 | 产业学院 | 通识教育 |"杨振宁"班|招生信息 |采购公告 | -| ------- | ------- | ---------| --------| --------| ----------|---------|------- |--------| -| jxdt | jwtz | jytz | sjtz | cyxy | tsjy | yznb | zsxx | cggg |`, + description: `| 教学动态 | 教务通知 | 教研通知 | 实践通知 | 产业学院 | 通识教育 | "杨振宁" 班 | 招生信息 | 采购公告 | +| -------- | -------- | -------- | -------- | -------- | -------- | ----------- | -------- | -------- | +| jxdt | jwtz | jytz | sjtz | cyxy | tsjy | yznb | zsxx | cggg |`, handler, }; @@ -59,7 +59,7 @@ async function handler(ctx) { return { ...item, - description: $('div.v_news_content').first().html() || undefined, + description: $('div.v_news_content').html(), }; }) ) diff --git a/lib/routes/dhu/jiaowu/news.ts b/lib/routes/dhu/jiaowu/news.ts index 898278ac8c93..593213564fb2 100644 --- a/lib/routes/dhu/jiaowu/news.ts +++ b/lib/routes/dhu/jiaowu/news.ts @@ -30,8 +30,8 @@ export const route: Route = { maintainers: ['KiraKiseki'], handler, description: `| 学生专栏 | 教师专栏 | 选课专栏(仅选课期间开放) | 辅修专业 | -| -------- | -------- | -------- | -------- | -| student | teacher | class | fxzy |`, +| -------- | -------- | -------------------------- | -------- | +| student | teacher | class | fxzy |`, }; async function handler(ctx) { diff --git a/lib/routes/diariofruticola/filtro.ts b/lib/routes/diariofruticola/filtro.ts index b52d57794688..61fb2c861115 100644 --- a/lib/routes/diariofruticola/filtro.ts +++ b/lib/routes/diariofruticola/filtro.ts @@ -103,8 +103,7 @@ export const route: Route = { }, description: `::: tip If you subscribe to [Cerezas](https://www.diariofruticola.cl/filtro/cerezas/71/),where the URL is \`https://www.diariofruticola.cl/filtro/cerezas/71/\`, extract the part \`https://diariofruticola.cl/filtro\` to the end, which is \`/\`, and use it as the parameter to fill in. Therefore, the route will be [\`/diariofruticola/filtro/cerezas/71\`](https://rsshub.app/diariofruticola/filtro/cerezas/71). -::: -`, +:::`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/diershoubing/news.tsx b/lib/routes/diershoubing/news.tsx index 0a5b62f7d30f..765c2c830649 100644 --- a/lib/routes/diershoubing/news.tsx +++ b/lib/routes/diershoubing/news.tsx @@ -69,9 +69,9 @@ async function handler(ctx) { }); return { - title: `二柄APP`, - link: `https://www.diershoubing.com`, - description: `二柄APP新闻`, + title: '二柄APP', + link: 'https://www.diershoubing.com', + description: '二柄APP新闻', item: items, }; } diff --git a/lib/routes/digg/community.tsx b/lib/routes/digg/community.tsx index b88d528d88eb..8f05e8ab217b 100644 --- a/lib/routes/digg/community.tsx +++ b/lib/routes/digg/community.tsx @@ -135,91 +135,93 @@ async function handler(ctx) { } = await ofetch(graphqlUrl, { method: 'POST', body: { - query: `query CommunityQuery($id: ID, $slug: String) { - community(where: { _id_EQ: $id, slug_EQ: $slug }) { - ...CommunityFragment - topContributors { - account { - _id - avatarUrl - avatarImage { - ...ImageFragment - } - username - } - score - } - topGemFinders { - account { - _id - avatarUrl - avatarImage { - ...ImageFragment - } - username - } - score - } - } -} -fragment AuthorFragment on Account { - _id - username - avatarUrl - avatarImage { - ...ImageFragment - } - badges { - name - iconUrl - } - blockStatus - roles { - name - iconUrl - } -} -fragment CommunityFragment on Community { - _id - name - slug - description - iconUrl - guidelinesPM - descriptionPM - iconImage { - ...ImageFragment - } - bannerUrl - bannerDesktopImage { - ...ImageFragment - } - bannerMobileImage { - ...ImageFragment - } - founder { - ...AuthorFragment - bio - } - manager { - ...AuthorFragment - } - memberCount - postCount - isJoinedByAccount - isPinnedByAccount - isJoinedByDefault - createdDate - editedDate - deletedDate -} -fragment ImageFragment on Image { - alt - height - width - url - blurhash -}`, + query: /* GraphQL */ ` + query CommunityQuery($id: ID, $slug: String) { + community(where: { _id_EQ: $id, slug_EQ: $slug }) { + ...CommunityFragment + topContributors { + account { + _id + avatarUrl + avatarImage { + ...ImageFragment + } + username + } + score + } + topGemFinders { + account { + _id + avatarUrl + avatarImage { + ...ImageFragment + } + username + } + score + } + } + } + fragment AuthorFragment on Account { + _id + username + avatarUrl + avatarImage { + ...ImageFragment + } + badges { + name + iconUrl + } + blockStatus + roles { + name + iconUrl + } + } + fragment CommunityFragment on Community { + _id + name + slug + description + iconUrl + guidelinesPM + descriptionPM + iconImage { + ...ImageFragment + } + bannerUrl + bannerDesktopImage { + ...ImageFragment + } + bannerMobileImage { + ...ImageFragment + } + founder { + ...AuthorFragment + bio + } + manager { + ...AuthorFragment + } + memberCount + postCount + isJoinedByAccount + isPinnedByAccount + isJoinedByDefault + createdDate + editedDate + deletedDate + } + fragment ImageFragment on Image { + alt + height + width + url + blurhash + } + `, variables: { slug: community }, }, }); @@ -232,148 +234,145 @@ fragment ImageFragment on Image { } = await ofetch(graphqlUrl, { method: 'POST', body: { - query: `query PostsQuery( - $first: Int - $after: String - $where: PostWhere - $sort: PostSort -) { - posts(first: $first, after: $after, where: $where, sort: $sort) { - edges { - node { - ...PostsNodeFragment - } - } - pageInfo { - ...PageInfoFragment - } - } -} -fragment AuthorFragment on Account { - _id - username - avatarUrl - avatarImage { - ...ImageFragment - } - badges { - name - iconUrl - } - blockStatus - roles { - name - iconUrl - } -} -fragment CommunityFragment on Community { - _id - name - slug - description - iconUrl - guidelinesPM - descriptionPM - iconImage { - ...ImageFragment - } - bannerUrl - bannerDesktopImage { - ...ImageFragment - } - bannerMobileImage { - ...ImageFragment - } - founder { - ...AuthorFragment - bio - } - manager { - ...AuthorFragment - } - memberCount - postCount - isJoinedByAccount - isPinnedByAccount - isJoinedByDefault - createdDate - editedDate - deletedDate -} -fragment ImageFragment on Image { - alt - height - width - url - blurhash -} -fragment ModerationReasonFragment on ModerationRemovalReason { - id - key - description - message - type - createdDate - editedDate - deletedDate -} -fragment PageInfoFragment on PageInfo { - endCursor - hasNextPage - hasPreviousPage - startCursor -} -fragment PostsNodeFragment on Post { - _id - title - isSavedByAccount - moderationStatus - isDuggByAccount - voteDirectionByAccount - upvoteCount - downvoteCount - score - reportByAccount - slug - type - externalContent { - url - headline - subHeadline - imageUrl - iconUrl - } - commentCount - shareCount - textPreview - contextCards { - tldr { - text - } - } - community { - ...CommunityFragment - } - attachments { - __typename - ... on Image { - ...ImageFragment - } - } - author { - ...AuthorFragment - } - createdDate - editedDate - deletedDate - nsfw - text - pm - moderatedDate - moderationReason { - ...ModerationReasonFragment - } -}`, + query: /* GraphQL */ ` + query PostsQuery($first: Int, $after: String, $where: PostWhere, $sort: PostSort) { + posts(first: $first, after: $after, where: $where, sort: $sort) { + edges { + node { + ...PostsNodeFragment + } + } + pageInfo { + ...PageInfoFragment + } + } + } + fragment AuthorFragment on Account { + _id + username + avatarUrl + avatarImage { + ...ImageFragment + } + badges { + name + iconUrl + } + blockStatus + roles { + name + iconUrl + } + } + fragment CommunityFragment on Community { + _id + name + slug + description + iconUrl + guidelinesPM + descriptionPM + iconImage { + ...ImageFragment + } + bannerUrl + bannerDesktopImage { + ...ImageFragment + } + bannerMobileImage { + ...ImageFragment + } + founder { + ...AuthorFragment + bio + } + manager { + ...AuthorFragment + } + memberCount + postCount + isJoinedByAccount + isPinnedByAccount + isJoinedByDefault + createdDate + editedDate + deletedDate + } + fragment ImageFragment on Image { + alt + height + width + url + blurhash + } + fragment ModerationReasonFragment on ModerationRemovalReason { + id + key + description + message + type + createdDate + editedDate + deletedDate + } + fragment PageInfoFragment on PageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + fragment PostsNodeFragment on Post { + _id + title + isSavedByAccount + moderationStatus + isDuggByAccount + voteDirectionByAccount + upvoteCount + downvoteCount + score + reportByAccount + slug + type + externalContent { + url + headline + subHeadline + imageUrl + iconUrl + } + commentCount + shareCount + textPreview + contextCards { + tldr { + text + } + } + community { + ...CommunityFragment + } + attachments { + __typename + ... on Image { + ...ImageFragment + } + } + author { + ...AuthorFragment + } + createdDate + editedDate + deletedDate + nsfw + text + pm + moderatedDate + moderationReason { + ...ModerationReasonFragment + } + } + `, variables: { first: limit, where: { community: { slug_EQ: community } }, sort: 'RECENT' }, }, }); diff --git a/lib/routes/digitalpolicyalert/activity-tracker.ts b/lib/routes/digitalpolicyalert/activity-tracker.ts index 0d4529925321..a5554746e256 100644 --- a/lib/routes/digitalpolicyalert/activity-tracker.ts +++ b/lib/routes/digitalpolicyalert/activity-tracker.ts @@ -113,8 +113,7 @@ export const route: Route = { }, description: `::: tip To subscribe to [Activity Tracker - International trade](https://digitalpolicyalert.org/activity-tracker?policy=1), where the source URL is \`https://digitalpolicyalert.org/activity-tracker?policy=1\`, extract the certain parts from this URL to be used as parameters, resulting in the route as [\`/digitalpolicyalert/activity-tracker/policy=1\`](https://rsshub.app/digitalpolicyalert/activity-tracker/policy=1). -::: -`, +:::`, categories: ['other'], features: { requireConfig: false, diff --git a/lib/routes/discourse/official.ts b/lib/routes/discourse/official.ts index 4df45e28f366..830262c8eb77 100644 --- a/lib/routes/discourse/official.ts +++ b/lib/routes/discourse/official.ts @@ -16,7 +16,7 @@ export const route: Route = { requireConfig: [ { name: 'DISCOURSE_CONFIG_*', - description: `Configure the Discourse environment variables referring to [https://docs.rsshub.app/deploy/config#discourse](https://docs.rsshub.app/deploy/config#discourse).`, + description: 'Configure the Discourse environment variables referring to [https://docs.rsshub.app/deploy/config#discourse](https://docs.rsshub.app/deploy/config#discourse).', }, ], requirePuppeteer: false, diff --git a/lib/routes/dockerhub/tag.ts b/lib/routes/dockerhub/tag.ts index 44325f3ed7ad..5218540f954d 100644 --- a/lib/routes/dockerhub/tag.ts +++ b/lib/routes/dockerhub/tag.ts @@ -21,7 +21,7 @@ export const route: Route = { maintainers: ['pseudoyu'], handler, description: `::: warning - Use \`library\` as the \`owner\` for official images, such as [https://rsshub.app/dockerhub/tag/library/mysql](https://rsshub.app/dockerhub/tag/library/mysql) +Use \`library\` as the \`owner\` for official images, such as :::`, }; diff --git a/lib/routes/domp4/detail.ts b/lib/routes/domp4/detail.ts index c2c0949301fc..ddeaf4515008 100644 --- a/lib/routes/domp4/detail.ts +++ b/lib/routes/domp4/detail.ts @@ -97,7 +97,7 @@ export const route: Route = { 新增 \`second\` 参数,用于选择下载地址二(地址二不可用或者不填都默认地址一),用法: \`/domp4/detail/LBTANI22222I?second=1\`。 -域名频繁更换,目前使用 www.xlmp4.com +域名频繁更换,目前使用 [www.xlmp4.com](http://www.xlmp4.com) :::`, }; diff --git a/lib/routes/domp4/latest-movie-bt.ts b/lib/routes/domp4/latest-movie-bt.ts index 70e9b9268adb..1adc9cb5bd9e 100644 --- a/lib/routes/domp4/latest-movie-bt.ts +++ b/lib/routes/domp4/latest-movie-bt.ts @@ -8,7 +8,7 @@ import { getItemList as detailItemList } from './detail'; import { ensureDomain } from './utils'; function getItemList($) { - const list = $(`#vod .list-group-item`) + const list = $('#vod .list-group-item') .toArray() .map((item) => { item = $(item); diff --git a/lib/routes/domp4/namespace.ts b/lib/routes/domp4/namespace.ts index 0b1f2771c957..109c621fb903 100644 --- a/lib/routes/domp4/namespace.ts +++ b/lib/routes/domp4/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: 'DoMP4 影视', url: 'www.xlmp4.com', description: `::: tip - 域名频繁更换,目前使用 www.xlmp4.com +域名频繁更换,目前使用 [www.xlmp4.com](http://www.xlmp4.com) :::`, lang: 'zh-CN', }; diff --git a/lib/routes/dongqiudi/daily.ts b/lib/routes/dongqiudi/daily.ts index 0b83dc5b4530..5c0611eaf04c 100644 --- a/lib/routes/dongqiudi/daily.ts +++ b/lib/routes/dongqiudi/daily.ts @@ -2,7 +2,7 @@ import type { Route } from '@/types'; export const route: Route = { path: '/daily', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/daily', radar: [ { diff --git a/lib/routes/dongqiudi/namespace.ts b/lib/routes/dongqiudi/namespace.ts index 129dcc779877..0ae79def4db1 100644 --- a/lib/routes/dongqiudi/namespace.ts +++ b/lib/routes/dongqiudi/namespace.ts @@ -3,8 +3,11 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '懂球帝', url: 'm.dongqiudi.com', + categories: ['sport'], description: `::: tip -- 可以通过头条新闻 + 参数过滤的形式获得早报、专题等内容。 + +- 可以通过头条新闻 + 参数过滤的形式获得早报、专题等内容。 + :::`, lang: 'zh-CN', }; diff --git a/lib/routes/dongqiudi/player-news.ts b/lib/routes/dongqiudi/player-news.ts index da2ae5d5a34a..0074ff5e0f0f 100644 --- a/lib/routes/dongqiudi/player-news.ts +++ b/lib/routes/dongqiudi/player-news.ts @@ -4,7 +4,7 @@ import utils from './utils'; export const route: Route = { path: '/player_news/:id', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/player_news/50000339', parameters: { id: '球员 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中通过其队伍找到' }, radar: [ diff --git a/lib/routes/dongqiudi/result.ts b/lib/routes/dongqiudi/result.ts index 13b8b9125091..1b424d07a66b 100644 --- a/lib/routes/dongqiudi/result.ts +++ b/lib/routes/dongqiudi/result.ts @@ -6,7 +6,7 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/result/:team', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/result/50001755', parameters: { team: '球队 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中找到' }, radar: [ diff --git a/lib/routes/dongqiudi/special.ts b/lib/routes/dongqiudi/special.ts index fa570669f7ce..206051771fa9 100644 --- a/lib/routes/dongqiudi/special.ts +++ b/lib/routes/dongqiudi/special.ts @@ -7,7 +7,7 @@ import utils from './utils'; export const route: Route = { path: '/special/:id', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/special/41', parameters: { id: '专题 id, 可自行通过 https://www.dongqiudi.com/special/+数字匹配' }, radar: [ diff --git a/lib/routes/dongqiudi/team-news.ts b/lib/routes/dongqiudi/team-news.ts index a0853f9a41a0..f5000b5795bb 100644 --- a/lib/routes/dongqiudi/team-news.ts +++ b/lib/routes/dongqiudi/team-news.ts @@ -4,7 +4,7 @@ import utils from './utils'; export const route: Route = { path: '/team_news/:team', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/team_news/50001755', parameters: { team: '球队 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中找到' }, radar: [ diff --git a/lib/routes/dongqiudi/top-news.ts b/lib/routes/dongqiudi/top-news.ts index 4df9f909208d..98b9a7cf8e95 100644 --- a/lib/routes/dongqiudi/top-news.ts +++ b/lib/routes/dongqiudi/top-news.ts @@ -7,7 +7,7 @@ import utils from './utils'; export const route: Route = { path: '/top_news/:id?', - categories: ['new-media'], + categories: ['sport'], example: '/dongqiudi/top_news/1', parameters: { id: '类别 id,不填默认头条新闻' }, features: { diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index 6294b06b8f83..c927dd76828b 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -65,7 +65,7 @@ const ProcessImg = (content) => { const ProcessFeed = async (ctx, type, id) => { const link = `https://www.dongqiudi.com/${type}/${id}.html`; - const apiUrl = `https://api.dongqiudi.com/v3/archive/app/channel/feeds`; + const apiUrl = 'https://api.dongqiudi.com/v3/archive/app/channel/feeds'; const { data: response } = await got(link); let name; diff --git a/lib/routes/douban/book/latest.ts b/lib/routes/douban/book/latest.ts index 8a2264d22c9f..e3bc6486241b 100644 --- a/lib/routes/douban/book/latest.ts +++ b/lib/routes/douban/book/latest.ts @@ -20,9 +20,9 @@ export const route: Route = { }, name: '新书速递', maintainers: ['fengkx', 'lyqluis'], - description: `| 文学 | 小说 | 历史文化 | 社会纪实 | 科学新知 | 艺术设计 | 商业经管 | 绘本漫画 | -| ------------ | ------- | -------- | --------- | -------- | -------- | -------- | -------- | -| prose_poetry | fiction | history | biography | science | art | business | comics |`, + description: `| 文学 | 小说 | 历史文化 | 社会纪实 | 科学新知 | 艺术设计 | 商业经管 | 绘本漫画 | +| ------------- | ------- | -------- | --------- | -------- | -------- | -------- | -------- | +| prose\\_poetry | fiction | history | biography | science | art | business | comics |`, handler, }; diff --git a/lib/routes/douban/other/classification.ts b/lib/routes/douban/other/classification.ts index 4723fd732ead..56b0c2f36dce 100644 --- a/lib/routes/douban/other/classification.ts +++ b/lib/routes/douban/other/classification.ts @@ -38,7 +38,7 @@ async function handler(ctx) { return { title: `豆瓣电影分类${score ? `超过 ${score} 分的` : ''}影视`, - link: `https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=`, + link: 'https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=', item: movies .map((item) => { const itemScore = Number.parseFloat(item.rate) || 0; diff --git a/lib/routes/douban/other/list.ts b/lib/routes/douban/other/list.ts index 40bc5613becf..d048144f8ada 100644 --- a/lib/routes/douban/other/list.ts +++ b/lib/routes/douban/other/list.ts @@ -28,24 +28,24 @@ export const route: Route = { handler, description: `| 榜单 / 集合 | 路由 | | ------------------ | ----------------------------- | -| 实时热门书影音 | subject_real_time_hotest | -| 影院热映 | movie_showing | -| 实时热门电影 | movie_real_time_hotest | -| 实时热门电视 | tv_real_time_hotest | -| 一周口碑电影榜 | movie_weekly_best | -| 华语口碑剧集榜 | tv_chinese_best_weekly | -| 全球口碑剧集榜 | tv_global_best_weekly | -| 国内口碑综艺榜 | show_chinese_best_weekly | -| 国外口碑综艺榜 | show_global_best_weekly | -| 热播新剧国产剧 | tv_domestic | -| 热播新剧欧美剧 | tv_american | -| 热播新剧日剧 | tv_japanese | -| 热播新剧韩剧 | tv_korean | -| 热播新剧动画 | tv_animation | -| 虚构类小说热门榜 | book_fiction_hot_weekly | -| 非虚构类小说热门榜 | book_nonfiction_hot_weekly | -| 热门单曲榜 | music_single | -| 华语新碟榜 | music_chinese | +| 实时热门书影音 | subject\\_real\\_time\\_hotest | +| 影院热映 | movie\\_showing | +| 实时热门电影 | movie\\_real\\_time\\_hotest | +| 实时热门电视 | tv\\_real\\_time\\_hotest | +| 一周口碑电影榜 | movie\\_weekly\\_best | +| 华语口碑剧集榜 | tv\\_chinese\\_best\\_weekly | +| 全球口碑剧集榜 | tv\\_global\\_best\\_weekly | +| 国内口碑综艺榜 | show\\_chinese\\_best\\_weekly | +| 国外口碑综艺榜 | show\\_global\\_best\\_weekly | +| 热播新剧国产剧 | tv\\_domestic | +| 热播新剧欧美剧 | tv\\_american | +| 热播新剧日剧 | tv\\_japanese | +| 热播新剧韩剧 | tv\\_korean | +| 热播新剧动画 | tv\\_animation | +| 虚构类小说热门榜 | book\\_fiction\\_hot\\_weekly | +| 非虚构类小说热门榜 | book\\_nonfiction\\_hot\\_weekly | +| 热门单曲榜 | music\\_single | +| 华语新碟榜 | music\\_chinese | | ... | ... | | 额外参数 | 含义 | 接受的值 | 默认值 | @@ -53,13 +53,13 @@ export const route: Route = { | playable | 仅看有可播放片源的影片 | 0/1 | 0 | | score | 筛选评分 | 0.0-10.0 | 0 | - 用例:\`/douban/list/tv_korean/playable=1&score=8\` +用例:\`/douban/list/tv_korean/playable=1&score=8\` - > 上面的榜单 / 集合并没有列举完整。 - > - > 如何找到榜单对应的路由参数: - > 在豆瓣手机 APP 中,对应地榜单页面右上角,点击分享链接。链接路径 \`subject_collection\` 后的路径就是路由参数 \`type\`。 - > 如:小说热门榜的分享链接为:\`https://m.douban.com/subject_collection/ECDIHUN4A\`,其对应本 RSS 路由的 \`type\` 为 \`ECDIHUN4A\`,对应的订阅链接路由:[\`/douban/list/ECDIHUN4A\`](https://rsshub.app/douban/list/ECDIHUN4A)`, +> 上面的榜单 / 集合并没有列举完整。 +> +> 如何找到榜单对应的路由参数: +> 在豆瓣手机 APP 中,对应地榜单页面右上角,点击分享链接。链接路径 \`subject_collection\` 后的路径就是路由参数 \`type\`。 +> 如:小说热门榜的分享链接为:\`https://m.douban.com/subject_collection/ECDIHUN4A\`,其对应本 RSS 路由的 \`type\` 为 \`ECDIHUN4A\`,对应的订阅链接路由:[\`/douban/list/ECDIHUN4A\`](https://rsshub.app/douban/list/ECDIHUN4A)`, }; async function handler(ctx) { diff --git a/lib/routes/douban/other/playing.ts b/lib/routes/douban/other/playing.ts index 779a40b8cf9d..944c4b8a4f33 100644 --- a/lib/routes/douban/other/playing.ts +++ b/lib/routes/douban/other/playing.ts @@ -25,13 +25,13 @@ async function handler(ctx) { const score = Number.parseFloat(ctx.req.param('score')) || 0; const response = await got({ method: 'get', - url: `https://movie.douban.com/cinema/nowplaying/beijing`, + url: 'https://movie.douban.com/cinema/nowplaying/beijing', }); const $ = load(response.data); return { title: `正在上映的${score ? `超过 ${score} 分的` : ''}电影`, - link: `https://movie.douban.com/cinema/nowplaying/`, + link: 'https://movie.douban.com/cinema/nowplaying/', item: $('.list-item') .toArray() .map((i) => { diff --git a/lib/routes/douban/other/recommended.ts b/lib/routes/douban/other/recommended.ts index 61f859716741..7c72be44a8b0 100644 --- a/lib/routes/douban/other/recommended.ts +++ b/lib/routes/douban/other/recommended.ts @@ -25,10 +25,10 @@ export const route: Route = { | playable | 仅看有可播放片源的影片 | 0/1 | 0 | | score | 筛选评分 | 0-10 | 0 | - 用例:\`/douban/recommended/tv/playable=0&score=8\` +用例:\`/douban/recommended/tv/playable=0&score=8\` ::: tip - 整合了 /douban/list/ 路由,省去每月手动更新 id 参数,因为当月推荐剧集片单中,会有还未播出 / 开评分剧集、海外平台播出剧集,请自行考虑是否使用额外参数。 +整合了 /douban/list/ 路由,省去每月手动更新 id 参数,因为当月推荐剧集片单中,会有还未播出 / 开评分剧集、海外平台播出剧集,请自行考虑是否使用额外参数。 :::`, }; diff --git a/lib/routes/douban/other/weekly-best.tsx b/lib/routes/douban/other/weekly-best.tsx index 9775f5082735..6156a74ca983 100644 --- a/lib/routes/douban/other/weekly-best.tsx +++ b/lib/routes/douban/other/weekly-best.tsx @@ -21,7 +21,7 @@ export const route: Route = { handler, description: `| 一周口碑电影榜 | 华语口碑剧集榜 | | ------------------- | ------------------------- | -| movie_weekly_best | tv_chinese_best_weekly |`, +| movie\\_weekly\\_best | tv\\_chinese\\_best\\_weekly |`, }; async function handler(ctx) { diff --git a/lib/routes/douban/people/status.ts b/lib/routes/douban/people/status.ts index 50e335f8c00c..f5a225d4fee4 100644 --- a/lib/routes/douban/people/status.ts +++ b/lib/routes/douban/people/status.ts @@ -16,11 +16,12 @@ export const route: Route = { name: '用户广播', maintainers: ['alfredcai'], handler, - description: ` -::: tip -- **目前只支持整数型 id** -- 字母型的 id,可以通过头像图片链接来找到其整数型 id,图片命名规则\`ul[userid]-*.jpg\`或\`u[userid]-*.jpg\`,即取文件名中间的数字 -- 例如:用户 id: \`MovieL\`他的头像图片链接:\`https://img1.doubanio.com/icon/ul1128221-98.jpg\`他的整数型 id: \`1128221\` + description: `::: tip + +- **目前只支持整数型 id** +- 字母型的 id,可以通过头像图片链接来找到其整数型 id,图片命名规则\`ul[userid]-*.jpg\`或\`u[userid]-*.jpg\`,即取文件名中间的数字 +- 例如:用户 id: \`MovieL\`他的头像图片链接:\`https://img1.doubanio.com/icon/ul1128221-98.jpg\`他的整数型 id: \`1128221\` + ::: 对于豆瓣用户广播内容,在 \`routeParams\` 参数中以 query string 格式设置如下选项可以控制输出的样式 @@ -41,16 +42,16 @@ export const route: Route = { | heightOfPics | 广播配图高(生效取决于阅读器) | 不指定 / 数字 | 不指定 | | sizeOfAuthorAvatar | 作者头像大小 | 数字 | 48 | - 指定更多与默认值不同的参数选项可以改善 RSS 的可读性,如 +指定更多与默认值不同的参数选项可以改善 RSS 的可读性,如 - [https://rsshub.app/douban/people/113894409/status/readable=1&authorNameBold=1&showAuthorInTitle=1&showAuthorInDesc=1&showAuthorAvatarInDesc=1&showEmojiForRetweet=1&showRetweetTextInTitle=1&addLinkForPics=1&showTimestampInDescription=1&showComments=1&widthOfPics=100](https://rsshub.app/douban/people/113894409/status/readable=1&authorNameBold=1&showAuthorInTitle=1&showAuthorInDesc=1&showAuthorAvatarInDesc=1&showEmojiForRetweet=1&showRetweetTextInTitle=1&addLinkForPics=1&showTimestampInDescription=1&showComments=1&widthOfPics=100) + - 的效果为 +的效果为 豆瓣读书的可读豆瓣广播 RSS`, }; -const headers = { Referer: `https://m.douban.com/` }; +const headers = { Referer: 'https://m.douban.com/' }; function tryFixStatus(status) { let result = { isFixSuccess: true, why: '' }; @@ -222,20 +223,20 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { activityInDesc += ``; } if (authorNameBold) { - activityInDesc += ``; + activityInDesc += ''; } activityInDesc += status.reshared_status.author.name; if (authorNameBold) { - activityInDesc += ``; + activityInDesc += ''; } if (readable) { - activityInDesc += ``; + activityInDesc += ''; } - activityInDesc += ` 的广播`; + activityInDesc += ' 的广播'; activityInTitle = `转发 ${status.reshared_status.author.name} 的广播`; } else { - activityInDesc = `转发广播`; - activityInTitle = `转发广播`; + activityInDesc = '转发广播'; + activityInTitle = '转发广播'; } } else { activityInDesc = status.activity; @@ -251,16 +252,16 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { usernameAndAvatar += ``; } if (authorNameBold) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } usernameAndAvatar += status.author.name; if (authorNameBold) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } if (readable) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } - usernameAndAvatar += ` `; + usernameAndAvatar += ' '; description += usernameAndAvatar + activityInDesc + (showColonInDesc ? ': ' : ''); } @@ -299,7 +300,7 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { } if (status.images && status.images.length) { - description += readable ? `
    ` : `
    `; + description += readable ? '
    ' : '
    '; // 一些RSS Reader会识别所有标签作为内含图片显示,我们不想要头像也作为内含图片之一 // 让所有配图在description的最前面再次出现一次,但宽高设为0 @@ -320,7 +321,7 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { } if (status.video_info) { - description += readable ? `
    ` : `
    `; + description += readable ? '
    ' : '
    '; const videoCover = status.video_info.cover_url; const videoSrc = status.video_info.video_url; if (videoSrc) { @@ -350,16 +351,16 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { usernameAndAvatar += ``; } if (authorNameBold) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } usernameAndAvatar += status.parent_status.author.name; if (authorNameBold) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } if (readable) { - usernameAndAvatar += ``; + usernameAndAvatar += ''; } - usernameAndAvatar += `: `; + usernameAndAvatar += ': '; description += usernameAndAvatar + status.parent_status.text; if (showRetweetTextInTitle) { title += status.parent_status.author.name + ': ' + status.parent_status.text; @@ -376,8 +377,8 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { if (status.card) { if (description) { description += readable - ? `
    ` - : `
    `; + ? '
    ' + : '
    '; } if (!status.card.images_block && status.card.image) { description += ``; @@ -408,7 +409,7 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { } description += cardContents.join('
    '); if (readable) { - description += `
    `; + description += '
    '; } if (status.card.images_block) { const imageUrls: Array = []; @@ -421,7 +422,7 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { // video_card if (status.video_card) { - description += readable ? `
    ` : `
    `; + description += readable ? '
    ' : '
    '; const videoCover = status.video_card.video_info && status.video_card.video_info.cover_url; const videoSrc = status.video_card.video_info && status.video_card.video_info.video_url; @@ -431,13 +432,13 @@ function getContentByActivity(ctx, item, params = {}, picsPrefixes = []) { description += `${videoSrc ? `` : ''}
    ${status.video_card.title ? `${status.video_card.title}` : ''}`; if (readable) { - description += `
    `; + description += '
    '; } } // reshared_status if (status.reshared_status) { - description += readable ? `
    ` : `
    `; + description += readable ? '
    ' : '
    '; if (showRetweetTextInTitle) { title += ' | '; diff --git a/lib/routes/douban/tv/coming.ts b/lib/routes/douban/tv/coming.ts index 493606ae707d..1b4581383b28 100644 --- a/lib/routes/douban/tv/coming.ts +++ b/lib/routes/douban/tv/coming.ts @@ -134,10 +134,10 @@ export const route: Route = { | sortBy | 排序方式 | hot/time | hot | | count | 请求上游返回数量 | 正整数 | 10 | - 用例:\`/douban/tv/coming/hot/10\` +用例:\`/douban/tv/coming/hot/10\` ::: tip - 服务端请求固定使用 \`sortby=hot\` 拉取数据,再按 \`sortBy\` 参数在本地重排;条目数量可通过 \`count\` 调整,仍可叠加 RSSHub 通用参数 \`limit\`。 +服务端请求固定使用 \`sortby=hot\` 拉取数据,再按 \`sortBy\` 参数在本地重排;条目数量可通过 \`count\` 调整,仍可叠加 RSSHub 通用参数 \`limit\`。 :::`, }; diff --git a/lib/routes/douyin/hashtag.ts b/lib/routes/douyin/hashtag.ts index 9c0db5e78500..47f3f01fa9e3 100644 --- a/lib/routes/douyin/hashtag.ts +++ b/lib/routes/douyin/hashtag.ts @@ -3,7 +3,7 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { fallback, queryToBoolean } from '@/utils/readable-social'; import { getOriginAvatar, proxyVideo, resolveUrl, templates } from './utils'; @@ -47,7 +47,7 @@ async function handler(ctx) { const tagData = await cache.tryGet( `douyin:hashtag:${cid}`, async () => { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); let awemeList = ''; diff --git a/lib/routes/douyin/live.ts b/lib/routes/douyin/live.ts index 014192f77499..c1d3f7a438b4 100644 --- a/lib/routes/douyin/live.ts +++ b/lib/routes/douyin/live.ts @@ -3,7 +3,7 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { getOriginAvatar } from './utils'; @@ -42,7 +42,7 @@ async function handler(ctx) { `douyin:live:${rid}`, async () => { let roomInfo; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); diff --git a/lib/routes/douyin/namespace.ts b/lib/routes/douyin/namespace.ts index e746775247e4..8741abbbb93c 100644 --- a/lib/routes/douyin/namespace.ts +++ b/lib/routes/douyin/namespace.ts @@ -4,12 +4,12 @@ export const namespace: Namespace = { name: '抖音直播', url: 'douyin.com', description: `::: warning -反爬严格,需要启用 puppeteer。\ -抖音的视频 CDN 会验证 Referer,意味着许多阅读器都无法直接播放内嵌视频,以下是一些变通解决方案: +反爬严格,需要启用 Playwright。抖音的视频 CDN 会验证 Referer,意味着许多阅读器都无法直接播放内嵌视频,以下是一些变通解决方案: + +1. 启用内嵌视频 (\`embed=1\`), 参考 [通用参数 -> 多媒体处理](/parameter#多媒体处理) 配置 \`multimedia_hotlink_template\` **或** \`wrap_multimedia_in_iframe\`。 +2. 关闭内嵌视频 (\`embed=0\`),手动点击 \`视频直链\` 超链接,一般情况下均可成功播放视频。若仍然出现 HTTP 403,请复制 URL 以后到浏览器打开。 +3. 点击原文链接打开抖音网页版的视频详情页播放视频。 -1. 启用内嵌视频 (\`embed=1\`), 参考 [通用参数 -> 多媒体处理](/parameter#多媒体处理) 配置 \`multimedia_hotlink_template\` **或** \`wrap_multimedia_in_iframe\`。 -2. 关闭内嵌视频 (\`embed=0\`),手动点击 \`视频直链\` 超链接,一般情况下均可成功播放视频。若仍然出现 HTTP 403,请复制 URL 以后到浏览器打开。 -3. 点击原文链接打开抖音网页版的视频详情页播放视频。 ::: 额外参数 diff --git a/lib/routes/douyin/user.ts b/lib/routes/douyin/user.ts index 179e1d5f73bd..bbfbb8edc50f 100644 --- a/lib/routes/douyin/user.ts +++ b/lib/routes/douyin/user.ts @@ -4,7 +4,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; import { fallback, queryToBoolean } from '@/utils/readable-social'; import type { PostData } from './types'; @@ -50,7 +50,7 @@ async function handler(ctx) { `douyin:user:${uid}`, async () => { let postData; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); diff --git a/lib/routes/duozhi/index.ts b/lib/routes/duozhi/index.ts index 82275da22952..8f4358529458 100644 --- a/lib/routes/duozhi/index.ts +++ b/lib/routes/duozhi/index.ts @@ -235,25 +235,23 @@ export const route: Route = { ], }, }, - description: `:::tip + description: `::: tip 订阅 [行业](http://www.duozhi.com/industry/),其源网址为 \`http://www.duozhi.com/industry/\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/duozhi/industry\`](http://rsshub.app/duozhi/industry)。 ::: - | [行业](http://www.duozhi.com/industry/) | [多知商学院](http://www.duozhi.com/DBS/) | [OpenTalk](http://www.duozhi.com/opentalk/) | - | ---------------------------------------------- | ---------------------------------------- | ---------------------------------------------- | - | [industry](https://rsshub.app/duozhi/industry) | [DBS](https://rsshub.app/duozhi/DBS) | [opentalk](https://rsshub.app/duozhi/opentalk) | +| [行业](http://www.duozhi.com/industry/) | [多知商学院](http://www.duozhi.com/DBS/) | [OpenTalk](http://www.duozhi.com/opentalk/) | +| ---------------------------------------------- | ---------------------------------------- | ---------------------------------------------- | +| [industry](https://rsshub.app/duozhi/industry) | [DBS](https://rsshub.app/duozhi/DBS) | [opentalk](https://rsshub.app/duozhi/opentalk) | - #### [行业](http://www.duozhi.com/industry/) +#### [行业](http://www.duozhi.com/industry/) - | [观察](http://www.duozhi.com/industry/insight/) | [早幼教](http://www.duozhi.com/industry/preschool/) | [家庭教育](http://www.duozhi.com/industry/jiatingjiaoyu/) | [K12](http://www.duozhi.com/industry/K12/) | [素质教育](http://www.duozhi.com/industry/qualityedu/) | - | -------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------- | - | [industry/insight](https://rsshub.app/duozhi/industry/insight) | [industry/preschool](https://rsshub.app/duozhi/industry/preschool) | [industry/jiatingjiaoyu](https://rsshub.app/duozhi/industry/jiatingjiaoyu) | [industry/K12](https://rsshub.app/duozhi/industry/K12) | [industry/qualityedu](https://rsshub.app/duozhi/industry/qualityedu) | +| [观察](http://www.duozhi.com/industry/insight/) | [早幼教](http://www.duozhi.com/industry/preschool/) | [家庭教育](http://www.duozhi.com/industry/jiatingjiaoyu/) | [K12](http://www.duozhi.com/industry/K12/) | [素质教育](http://www.duozhi.com/industry/qualityedu/) | +| -------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------- | +| [industry/insight](https://rsshub.app/duozhi/industry/insight) | [industry/preschool](https://rsshub.app/duozhi/industry/preschool) | [industry/jiatingjiaoyu](https://rsshub.app/duozhi/industry/jiatingjiaoyu) | [industry/K12](https://rsshub.app/duozhi/industry/K12) | [industry/qualityedu](https://rsshub.app/duozhi/industry/qualityedu) | - | [职教/大学生](http://www.duozhi.com/industry/adult/) | [教育信息化](http://www.duozhi.com/industry/EduInformatization/) | [财报](http://www.duozhi.com/industry/earnings/) | [民办学校](http://www.duozhi.com/industry/privateschools/) | [留学](http://www.duozhi.com/industry/overseas/) | - | ---------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------- | - | [industry/adult](https://rsshub.app/duozhi/industry/adult) | [industry/EduInformatization](https://rsshub.app/duozhi/industry/EduInformatization) | [industry/earnings](https://rsshub.app/duozhi/industry/earnings) | [industry/privateschools](https://rsshub.app/duozhi/industry/privateschools) | [industry/overseas](https://rsshub.app/duozhi/industry/overseas) | - -`, +| [职教 / 大学生](http://www.duozhi.com/industry/adult/) | [教育信息化](http://www.duozhi.com/industry/EduInformatization/) | [财报](http://www.duozhi.com/industry/earnings/) | [民办学校](http://www.duozhi.com/industry/privateschools/) | [留学](http://www.duozhi.com/industry/overseas/) | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| [industry/adult](https://rsshub.app/duozhi/industry/adult) | [industry/EduInformatization](https://rsshub.app/duozhi/industry/EduInformatization) | [industry/earnings](https://rsshub.app/duozhi/industry/earnings) | [industry/privateschools](https://rsshub.app/duozhi/industry/privateschools) | [industry/overseas](https://rsshub.app/duozhi/industry/overseas) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/dw/news.ts b/lib/routes/dw/news.ts index 5777d82d23d4..bf7ea1924119 100644 --- a/lib/routes/dw/news.ts +++ b/lib/routes/dw/news.ts @@ -24,12 +24,10 @@ export const route: Route = { name: 'News', maintainers: ['quiniapiezoelectricity'], handler, - description: ` -::: tip + description: `::: tip Parameters can be obtained from the official website, for instance: -For the site https://www.dw.com/de/deutschland/s-12321 the language code would be \`de\` and the category ID would be \`s-1432\`. -::: -`, +For the site the language code would be \`de\` and the category ID would be \`s-1432\`. +:::`, radar: [ { source: ['www.dw.com/:lang/:name/:id'], @@ -38,7 +36,7 @@ For the site https://www.dw.com/de/deutschland/s-12321 the language code would b ], }; -const defaultUrl = `https://www.dw.com/graph-api/en/content/navigation/9097`; +const defaultUrl = 'https://www.dw.com/graph-api/en/content/navigation/9097'; const typenames = new Set(['Article', 'Liveblog', 'Video']); async function handler(ctx) { diff --git a/lib/routes/dw/rss.ts b/lib/routes/dw/rss.ts index 66dba4ed246c..34990c13ae2e 100644 --- a/lib/routes/dw/rss.ts +++ b/lib/routes/dw/rss.ts @@ -23,10 +23,8 @@ export const route: Route = { name: 'RSS', maintainers: ['quiniapiezoelectricity'], handler, - description: ` -For a full list of RSS Feed Channels in English, please refer to [DW RSS Feeds](https://corporate.dw.com/en/rss-feeds/a-68693346). -RSS Feed Channels in other languages are also available, for example: \`rss-chi-all\` renders the RSS feed in Chinese and \`rss-de-all\` for the RSS Feed in German -`, + description: `For a full list of RSS Feed Channels in English, please refer to [DW RSS Feeds](https://corporate.dw.com/en/rss-feeds/a-68693346). +RSS Feed Channels in other languages are also available, for example: \`rss-chi-all\` renders the RSS feed in Chinese and \`rss-de-all\` for the RSS Feed in German`, }; async function handler(ctx) { diff --git a/lib/routes/dx2025/index.ts b/lib/routes/dx2025/index.ts index d0ce740f97b1..0ca42a773380 100644 --- a/lib/routes/dx2025/index.ts +++ b/lib/routes/dx2025/index.ts @@ -26,7 +26,7 @@ export const route: Route = { | -------------------- | ---------------- | ------ | ---- | | industry-observation | industry-reports | policy | data | - 行业分类 +行业分类 | 行业 | 行业名称 | | -------------------- | ----------------------------------------------------------------- | diff --git a/lib/routes/dykszx/news.ts b/lib/routes/dykszx/news.ts index fbe8753fccd5..d9f2273e3379 100644 --- a/lib/routes/dykszx/news.ts +++ b/lib/routes/dykszx/news.ts @@ -83,7 +83,7 @@ export const route: Route = { maintainers: ['zytomorrow'], handler, url: 'www.dykszx.com', - description: `| 新闻中心 | 公务员考试 | 事业单位 | (职)业资格、职称考试 | 其他 | -| :------: | :------: | :------: |:------: |:------: | -| all | gwy | sydw | zyzc | other |`, + description: `| 新闻中心 | 公务员考试 | 事业单位 | (职)业资格、职称考试 | 其他 | +| :------: | :--------: | :------: | :--------------------: | :---: | +| all | gwy | sydw | zyzc | other |`, }; diff --git a/lib/routes/earthquake/ceic.ts b/lib/routes/earthquake/ceic.ts index 7c482c05f01e..ff980d1e9739 100644 --- a/lib/routes/earthquake/ceic.ts +++ b/lib/routes/earthquake/ceic.ts @@ -36,7 +36,7 @@ export const route: Route = { | 9 | 最近一年 5.0 级以上地震信息 | | 0 | 最近一年 6.0 级以上地震信息 | - 可通过全局过滤参数订阅您感兴趣的地区.`, +可通过全局过滤参数订阅您感兴趣的地区.`, }; async function handler(ctx) { diff --git a/lib/routes/earthquake/index.ts b/lib/routes/earthquake/index.ts index b9e116de8446..3dfc0b7dfeb3 100644 --- a/lib/routes/earthquake/index.ts +++ b/lib/routes/earthquake/index.ts @@ -26,7 +26,7 @@ export const route: Route = { maintainers: ['LogicJake'], handler, url: 'www.cea.gov.cn/cea/xwzx/zqsd/index.html', - description: `可通过全局过滤参数订阅您感兴趣的地区.`, + description: '可通过全局过滤参数订阅您感兴趣的地区.', }; async function handler(ctx) { diff --git a/lib/routes/eastday/sh.ts b/lib/routes/eastday/sh.ts index 8fdae1aaa9df..7c875f2ddfb1 100644 --- a/lib/routes/eastday/sh.ts +++ b/lib/routes/eastday/sh.ts @@ -36,7 +36,7 @@ async function handler() { const response = await got({ method: 'get', - url: `https://apin.eastday.com/apiplus/special/specialnewslistbyurl?specialUrl=1632798465040016&skipCount=0&limitCount=20`, + url: 'https://apin.eastday.com/apiplus/special/specialnewslistbyurl?specialUrl=1632798465040016&skipCount=0&limitCount=20', }); const result = await Promise.all( @@ -69,7 +69,7 @@ async function handler() { ); return { - title: `东方网-上海`, + title: '东方网-上海', link: `${domain}/wap/sh.html`, item: result, }; diff --git a/lib/routes/eastmoney/search/index.ts b/lib/routes/eastmoney/search/index.ts index ba2ee96920e5..7a64cc95206c 100644 --- a/lib/routes/eastmoney/search/index.ts +++ b/lib/routes/eastmoney/search/index.ts @@ -46,7 +46,7 @@ async function handler(ctx) { }; const cb = `jQuery${('3.5.1' + Math.random()).replaceAll(/\D/g, '')}_${Date.now()}`; - const url = `https://search-api-web.eastmoney.com/search/jsonp`; + const url = 'https://search-api-web.eastmoney.com/search/jsonp'; const response = await got(url, { searchParams: { diff --git a/lib/routes/ecnu/cxcy.ts b/lib/routes/ecnu/cxcy.ts index f5b682a99189..0f5ef60fe78e 100644 --- a/lib/routes/ecnu/cxcy.ts +++ b/lib/routes/ecnu/cxcy.ts @@ -19,9 +19,9 @@ export const route: Route = { ], name: '本科创新创业教育网', maintainers: ['FrozenStarrrr', 'ChiyoYuki', 'ECNU-minus'], - description: `| 通知公告 | 新闻动态 | 学科竞赛 | 常用资源 | -| ------------ | ------------ | ------------ | ------------ | -| announcement | news | contest | resources |`, + description: `| 通知公告 | 新闻动态 | 学科竞赛 | 常用资源 | +| ------------ | -------- | -------- | --------- | +| announcement | news | contest | resources |`, handler: async (ctx) => { const fragList = { announcement: { diff --git a/lib/routes/ehentai/namespace.ts b/lib/routes/ehentai/namespace.ts index 6d99c96e3ba5..428937453461 100644 --- a/lib/routes/ehentai/namespace.ts +++ b/lib/routes/ehentai/namespace.ts @@ -7,6 +7,6 @@ export const namespace: Namespace = { | Key | Meaning | Accepted keys | Default value | | ------------ | ------------------------------------------------------------------------------- | -------------- | ------------- | | bittorrent | Whether include a link to the latest torrent | 0/1/true/false | false | -| embed_thumb | Whether the cover image is embedded in the RSS feed rather than given as a link | 0/1/true/false | false |`, +| embed\\_thumb | Whether the cover image is embedded in the RSS feed rather than given as a link | 0/1/true/false | false |`, lang: 'en', }; diff --git a/lib/routes/ekantipur/issue.ts b/lib/routes/ekantipur/issue.ts index b3107fb93722..d9cb36e4c457 100644 --- a/lib/routes/ekantipur/issue.ts +++ b/lib/routes/ekantipur/issue.ts @@ -29,9 +29,9 @@ export const route: Route = { handler, description: `Channels: -| समाचार | अर्थ / वाणिज्य | विचार | खेलकुद | उपत्यका | मनोरञ्जन | फोटोफिचर | फिचर | विश्व | ब्लग | -| ---- | -------- | ------- | ------ | -------- | ------------- | -------------- | ------- | ----- | ---- | -| news | business | opinion | sports | national | entertainment | photo_feature | feature | world | blog |`, +| समाचार | अर्थ / वाणिज्य | विचार | खेलकुद | उपत्यका | मनोरञ्जन | फोटोफिचर | फिचर | विश्व | ब्लग | +| ---- | ---------- | ------- | ------ | -------- | ------------- | -------------- | ------- | ----- | ---- | +| news | business | opinion | sports | national | entertainment | photo\\_feature | feature | world | blog |`, }; async function handler(ctx) { diff --git a/lib/routes/elamigos/index.ts b/lib/routes/elamigos/index.ts index 7ee9715444e0..faa942b0a13d 100644 --- a/lib/routes/elamigos/index.ts +++ b/lib/routes/elamigos/index.ts @@ -54,7 +54,7 @@ function toNeutralDate(input: string, appendDay: boolean = false): Date { } function extractGames($: any, limit: number, baseUrl: string): Array<{ title: string; link: string; pubDate: string | null }> { - const dateRegex = /^\d{2}\.\d{2}\.\d{4}$/; + const dateRegex = /^\d{2}\.\d{2}\.\d{4}/; const games: Array<{ title: string; link: string; pubDate: string | null }> = []; let arrivedAtGameSection = false; @@ -68,14 +68,15 @@ function extractGames($: any, limit: number, baseUrl: string): Array<{ title: st if (tagName === 'h1') { const text = $elem.text().trim(); - if (!dateRegex.test(text)) { + const match = text.match(dateRegex); + if (!match) { return; } 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() === '') { - game.pubDate = text; + game.pubDate = match[0]; } } } else if ((tagName === 'h3' || tagName === 'h5') && arrivedAtGameSection) { diff --git a/lib/routes/elasticsearch-cn/index.ts b/lib/routes/elasticsearch-cn/index.ts index 4a23f11502e1..574c841d9ae7 100644 --- a/lib/routes/elasticsearch-cn/index.ts +++ b/lib/routes/elasticsearch-cn/index.ts @@ -30,7 +30,7 @@ export const route: Route = { handler, description: `如 [Elasticsearch 最新](https://elasticsearch.cn/category-2) 的 URL 为 \`https://elasticsearch.cn/category-2\`,则分类参数处填写 \`category-2\`,最后得到路由地址 [\`/elasticsearch-cn/category-2\`](https://rsshub.app/elasticsearch-cn/category-2)。 - 又如 [求职招聘 30 天热门](https://elasticsearch.cn/sort_type-hot____category-12__day-30) 的 URL 为 \`https://elasticsearch.cn/sort_type-hot____category-12__day-30\`,则分类参数处填写 \`sort_type-hot____category-12__day-30\`,最后得到路由地址 [\`/elasticsearch-cn/sort_type-hot____category-12__day-30\`](https://rsshub.app/elasticsearch-cn/sort_type-hot____category-12__day-30)。`, +又如 [求职招聘 30 天热门](https://elasticsearch.cn/sort_type-hot____category-12__day-30) 的 URL 为 \`https://elasticsearch.cn/sort_type-hot____category-12__day-30\`,则分类参数处填写 \`sort_type-hot____category-12__day-30\`,最后得到路由地址 [\`/elasticsearch-cn/sort_type-hot____category-12__day-30\`](https://rsshub.app/elasticsearch-cn/sort_type-hot____category-12__day-30)。`, }; async function handler(ctx) { diff --git a/lib/routes/eleduck/posts.ts b/lib/routes/eleduck/posts.ts index 2f992e8bc527..2585f83e974b 100644 --- a/lib/routes/eleduck/posts.ts +++ b/lib/routes/eleduck/posts.ts @@ -5,7 +5,7 @@ import got from '@/utils/got'; const getCateName = async (cid = 0) => { const key = 'eleduck-categories'; const cates = await cache.tryGet(key, async () => { - const res = await got(`https://svc.eleduck.com/api/v1/categories`); + const res = await got('https://svc.eleduck.com/api/v1/categories'); const map = {}; for (const item of res.data.categories) { map[item.id] = item.name; diff --git a/lib/routes/embassy/namespace.ts b/lib/routes/embassy/namespace.ts index 13c5e8f236a5..c15d7d1ff873 100644 --- a/lib/routes/embassy/namespace.ts +++ b/lib/routes/embassy/namespace.ts @@ -7,33 +7,33 @@ export const namespace: Namespace = { 加拿大 \`CA\` -- 大使馆: \`/embassy/ca\` +- 大使馆: \`/embassy/ca\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | -------- | ---------------------- | | 蒙特利尔 | \`/embassy/ca/montreal\` | -* * * +*** 德国 \`DE\` -- 大使馆: \`/embassy/de\` +- 大使馆: \`/embassy/de\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ------ | -------------------- | | 慕尼黑 | \`/embassy/de/munich\` | -* * * +*** 法国 \`FR\` -- 大使馆: \`/embassy/fr\` +- 大使馆: \`/embassy/fr\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ---------- | ------------------------ | @@ -41,13 +41,13 @@ export const namespace: Namespace = { | 斯特拉斯堡 | \`/embassy/fr/strasbourg\` | | 里昂 | \`/embassy/fr/lyon\` | -* * * +*** 日本 \`JP\` -- 大使馆: \`/embassy/jp\` +- 大使馆: \`/embassy/jp\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ------ | ---------------------- | @@ -58,13 +58,13 @@ export const namespace: Namespace = { | 札幌 | \`/embassy/jp/sapporo\` | | 新潟 | \`/embassy/jp/niigata\` | -* * * +*** 韩国 \`KR\` -- 大使馆: \`/embassy/kr\` +- 大使馆: \`/embassy/kr\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ---- | --------------------- | @@ -72,25 +72,25 @@ export const namespace: Namespace = { | 济州 | \`/embassy/kr/jeju\` | | 光州 | \`/embassy/kr/gwangju\` | -* * * +*** 马来西亚 \`MY\` -- 大使馆: \`/embassy/my\` +- 大使馆: \`/embassy/my\` -* * * +*** 新加坡 \`SG\` -- 大使馆: \`/embassy/sg\` +- 大使馆: \`/embassy/sg\` -* * * +*** 美国 \`US\` -- 大使馆: \`/embassy/us\` +- 大使馆: \`/embassy/us\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ------ | -------------------------- | @@ -98,13 +98,13 @@ export const namespace: Namespace = { | 芝加哥 | \`/embassy/us/chicago\` | | 旧金山 | \`/embassy/us/sanfrancisco\` | -* * * +*** 英国 \`UK\` -- 大使馆: \`/embassy/uk\` +- 大使馆: \`/embassy/uk\` -- 领事馆城市列表: +- 领事馆城市列表: | 城市 | 路由 | | ---------- | ------------------------ | diff --git a/lib/routes/engineering/tag.ts b/lib/routes/engineering/tag.ts index cf23dc6e123d..580c6316acd7 100644 --- a/lib/routes/engineering/tag.ts +++ b/lib/routes/engineering/tag.ts @@ -24,7 +24,7 @@ export const route: Route = { name: 'Tag', maintainers: ['suhang-only'], handler, - description: `| JSON | Javascript | Java | Apache | AWS | SQL | React | Golang | + description: `| JSON | Javascript | Java | Apache | AWS | SQL | React | Golang | | ---- | ---------- | ---- | ------ | --- | --- | ----- | ------ | | json | javascript | java | apache | aws | sql | react | golang |`, }; diff --git a/lib/routes/eshukan/academic.ts b/lib/routes/eshukan/academic.ts index 480cd3040b45..b9771dbd5182 100644 --- a/lib/routes/eshukan/academic.ts +++ b/lib/routes/eshukan/academic.ts @@ -104,9 +104,8 @@ export const route: Route = { example: '/eshukan/academic/1', parameters: { category: '栏目 id,默认为 `1`,即期刊动态,可在对应栏目页 URL 中找到' }, description: `::: tip - 若订阅 [期刊动态](https://www.eshukan.com/academic/index.aspx?cid=1),网址为 \`https://www.eshukan.com/academic/index.aspx?cid=1\`。截取 \`https://www.eshukan.com/academic/index.aspx?cid=\` 到末尾的部分 \`1\` 作为参数填入,此时路由为 [\`/eshukan/academic/1\`](https://rsshub.app/eshukan/academic/1)。 -::: - `, +若订阅 [期刊动态](https://www.eshukan.com/academic/index.aspx?cid=1),网址为 \`https://www.eshukan.com/academic/index.aspx?cid=1\`。截取 \`https://www.eshukan.com/academic/index.aspx?cid=\` 到末尾的部分 \`1\` 作为参数填入,此时路由为 [\`/eshukan/academic/1\`](https://rsshub.app/eshukan/academic/1)。 +:::`, categories: ['study'], features: { diff --git a/lib/routes/espn/news.tsx b/lib/routes/espn/news.tsx index 489224451cb9..f97edced933a 100644 --- a/lib/routes/espn/news.tsx +++ b/lib/routes/espn/news.tsx @@ -60,16 +60,17 @@ export const route: Route = { categories: ['traditional-media'], parameters: { sport: 'sport category, can be nba, nfl, mlb, nhl etc.' }, description: `Get the news feed of the sport you love on ESPN. -| Sport | sport | Sport | sport | -|----------------------|---------|----------------|---------| -| 🏀 NBA | nba | 🎾 Tennis | tennis | -| 🏀 WNBA | wnba | ⛳️ Golf | golf | -| 🏈 NFL | nfl | 🏏 Cricket | cricket | -| ⚾️ MLB | mlb | ⚽️ Soccer | soccer | -| 🏒 NHL | nhl | 🏎️ F1 | f1 | -| ⛹️ College Basketball | ncb | 🥊 MMA | mma | -| 🏟️️ College Football | ncf | 🏈 UFL | ufl | -| 🏉 Rugby | rugby | 🃏 Poker | poker |`, + +| Sport | sport | Sport | sport | +| --------------------- | ----- | ---------- | ------- | +| 🏀 NBA | nba | 🎾 Tennis | tennis | +| 🏀 WNBA | wnba | ⛳️ Golf | golf | +| 🏈 NFL | nfl | 🏏 Cricket | cricket | +| ⚾️ MLB | mlb | ⚽️ Soccer | soccer | +| 🏒 NHL | nhl | 🏎️ F1 | f1 | +| ⛹️ College Basketball | ncb | 🥊 MMA | mma | +| 🏟️️ College Football | ncf | 🏈 UFL | ufl | +| 🏉 Rugby | rugby | 🃏 Poker | poker |`, radar: [ { source: ['espn.com/:sport*'], diff --git a/lib/routes/europechinese/latest.ts b/lib/routes/europechinese/latest.ts index 4ac16222a1f8..5fa748a4cd21 100644 --- a/lib/routes/europechinese/latest.ts +++ b/lib/routes/europechinese/latest.ts @@ -29,7 +29,7 @@ export const route: Route = { }; async function handler() { - const url = `https://europechinese.blogspot.com/`; + const url = 'https://europechinese.blogspot.com/'; const { data: response } = await got(url); const $ = load(response); const list = $('h3.post-title'); @@ -60,7 +60,7 @@ async function handler() { ); return { - title: `歐洲動態(國際)| 最新`, + title: '歐洲動態(國際)| 最新', link: url, item: out, }; diff --git a/lib/routes/expats/czech-news.ts b/lib/routes/expats/czech-news.ts index 766d38a12269..340a8805f9a0 100644 --- a/lib/routes/expats/czech-news.ts +++ b/lib/routes/expats/czech-news.ts @@ -185,8 +185,7 @@ To subscribe to [Daily News](https://www.expats.cz/czech-news/daily-news), where | [Economy](https://www.expats.cz/czech-news/economy) | [economy](https://rsshub.app/expats/czech-news/economy) | | [Language](https://www.expats.cz/czech-news/language) | [language](https://rsshub.app/expats/czech-news/language) | - -`, +`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/fanbox/utils.tsx b/lib/routes/fanbox/utils.tsx index 09478a3bed31..f0d6d6713031 100644 --- a/lib/routes/fanbox/utils.tsx +++ b/lib/routes/fanbox/utils.tsx @@ -42,8 +42,8 @@ function passageConv(p) { p.styles.map((s) => { switch (s.type) { case 'bold': - seg[s.offset] = `` + seg[s.offset]; - seg[s.offset + s.length - 1] += ``; + seg[s.offset] = '' + seg[s.offset]; + seg[s.offset + s.length - 1] += ''; break; default: } @@ -53,7 +53,7 @@ function passageConv(p) { if (p.links) { p.links.map((l) => { seg[l.offset] = `` + seg[l.offset]; - seg[l.offset + l.length - 1] += ``; + seg[l.offset + l.length - 1] += ''; return l; }); } @@ -171,7 +171,7 @@ export function parseItem(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; return { - title: item.title || `No title`, + title: item.title || 'No title', description: await parseDetail(postDetail.body), pubDate: parseDate(item.updatedDatetime), link: `https://${item.creatorId}.fanbox.cc/posts/${item.id}`, diff --git a/lib/routes/fangchan/list.tsx b/lib/routes/fangchan/list.tsx index a36ddfae4cf9..83fb1a882f4e 100644 --- a/lib/routes/fangchan/list.tsx +++ b/lib/routes/fangchan/list.tsx @@ -177,8 +177,7 @@ export const route: Route = { | [数据研究](https://www.fangchan.com/datalist) | [行业测评](https://www.fangchan.com/industrylist) | [政策法规](https://www.fangchan.com/policylist) | | ----------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------- | -| [datalist](https://rsshub.app/fangchan/list/datalist) | [industrylist](https://rsshub.app/fangchan/list/industrylist) | [policylist](https://rsshub.app/fangchan/list/policylist) | -`, +| [datalist](https://rsshub.app/fangchan/list/datalist) | [industrylist](https://rsshub.app/fangchan/list/industrylist) | [policylist](https://rsshub.app/fangchan/list/policylist) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/fantia/search.ts b/lib/routes/fantia/search.ts index fe28e5381450..9fcae796d9b5 100644 --- a/lib/routes/fantia/search.ts +++ b/lib/routes/fantia/search.ts @@ -81,7 +81,13 @@ export const route: Route = { keyword: 'Keyword, empty by default', }, features: { - requireConfig: false, + requireConfig: [ + { + name: 'FANTIA_COOKIE', + optional: true, + description: 'The `cookie` after login can be obtained by viewing the request header in the console, If not filled in will cause some posts that require login to read to get exceptions', + }, + ], requirePuppeteer: false, antiCrawler: false, supportBT: false, @@ -98,7 +104,7 @@ export const route: Route = { | ------------ | ----- | -------- | ------------ | | fanclubs | posts | products | commissions | - Category +Category | 分类 | 分类名 | | ---------------------- | ---------- | @@ -124,19 +130,19 @@ export const route: Route = { | ショップ | shop | | その他 | other | - Ranking period +Ranking period | デイリー | ウィークリー | マンスリー | 全期間 | | -------- | ------------ | ---------- | ------ | | daily | weekly | monthly | all | - Sorting +Sorting | 更新の新しい順 | 更新の古い順 | 投稿の新しい順 | 投稿の古い順 | お気に入り数順 | | -------------- | ------------ | -------------- | ------------ | -------------- | -| updater | update_old | newer | create_old | popular | +| updater | update\\_old | newer | create\\_old | popular | - Rating +Rating | すべて | 一般のみ | R18 のみ | | ------ | -------- | -------- | diff --git a/lib/routes/fantia/user.ts b/lib/routes/fantia/user.ts index 4847145fee72..5244b8954cfc 100644 --- a/lib/routes/fantia/user.ts +++ b/lib/routes/fantia/user.ts @@ -2,7 +2,7 @@ import { config } from '@/config'; import type { Route } from '@/types'; import { ViewType } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; export const route: Route = { @@ -12,7 +12,13 @@ export const route: Route = { example: '/fantia/user/3498', parameters: { id: 'User id, can be found in user profile URL' }, features: { - requireConfig: false, + requireConfig: [ + { + name: 'FANTIA_COOKIE', + optional: true, + description: 'The `cookie` after login can be obtained by viewing the request header in the console, If not filled in will cause some posts that require login to read to get exceptions', + }, + ], requirePuppeteer: false, antiCrawler: false, supportBT: false, @@ -34,36 +40,32 @@ async function handler(ctx) { const rootUrl = 'https://fantia.jp'; const userUrl = `${rootUrl}/api/v1/fanclubs/${ctx.req.param('id')}`; - const initalResponse = await got({ - method: 'get', - url: rootUrl, + const initalResponse = await ofetch(rootUrl, { headers: { Cookie: config.fantia.cookies ?? '', }, }); - const csrfToken = initalResponse.data.match(/name="csrf-token" content="(.*?)"\s?\/>/)[1]; + const csrfToken = initalResponse.match(/name="csrf-token" content="(.*?)"\s?\/>/)[1]; - const response = await got({ - method: 'get', - url: userUrl, + const response = await ofetch(userUrl, { headers: { Cookie: config.fantia.cookies ?? '', }, }); - const list = response.data.fanclub.recent_posts.map((item) => ({ + const fanClub = response.fanclub; + + const list = response.fanclub.recent_posts.map((item) => ({ title: item.title, link: `${rootUrl}/api/v1/posts/${item.id}`, - description: `

    ${item.comment}

    `, + description: item.comment ? `

    ${item.comment}

    ` : '', pubDate: parseDate(item.posted_at), })); const items = await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { - const contentResponse = await got({ - method: 'get', - url: item.link, + const contentResponse = await ofetch(item.link, { headers: { Cookie: config.fantia.cookies ?? '', 'X-CSRF-Token': csrfToken, @@ -73,7 +75,7 @@ async function handler(ctx) { }, }); item.link = item.link.replace('api/v1/', ''); - item.description += ``; + item.description += ``; return item; }) @@ -81,8 +83,10 @@ async function handler(ctx) { ); return { - title: `Fantia - ${response.data.fanclub.fanclub_name_with_creator_name}`, + title: `Fantia - ${fanClub.fanclub_name_with_creator_name}`, + description: fanClub.comment?.replaceAll('\r\n', ' ')?.trim(), link: `${rootUrl}/fanclubs/${ctx.req.param('id')}`, + image: fanClub.icon.original ?? fanClub.icon.main, item: items, }; } diff --git a/lib/routes/fashionnetwork/index.ts b/lib/routes/fashionnetwork/index.ts index 4317381b9efb..24ff84a64f22 100644 --- a/lib/routes/fashionnetwork/index.ts +++ b/lib/routes/fashionnetwork/index.ts @@ -112,7 +112,7 @@ export const route: Route = { example: '/fashionnetwork/cn/lists/0', parameters: { category: '分类,默认为 0,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [独家新闻](https://fashionnetwork.cn),网址为 \`https://fashionnetwork.cn/lists/13.html\`。截取 \`https://fashionnetwork.cn/\` 到末尾 \`.html\` 的部分 \`13\` 作为参数填入,此时路由为 [\`/fashionnetwork/cn/lists/13\`](https://rsshub.app/fashionnetwork/cn/lists/13)。 +若订阅 [独家新闻](https://fashionnetwork.cn),网址为 \`https://fashionnetwork.cn/lists/13.html\`。截取 \`https://fashionnetwork.cn/\` 到末尾 \`.html\` 的部分 \`13\` 作为参数填入,此时路由为 [\`/fashionnetwork/cn/lists/13\`](https://rsshub.app/fashionnetwork/cn/lists/13)。 ::: | 分类 | ID | @@ -124,8 +124,7 @@ export const route: Route = { | [产业](https://fashionnetwork.cn/lists/5) | [5](https://rsshub.app/fashionnetwork/cn/lists/5) | | [创新研究](https://fashionnetwork.cn/lists/6) | [6](https://rsshub.app/fashionnetwork/cn/lists/6) | | [人事变动](https://fashionnetwork.cn/lists/12) | [12](https://rsshub.app/fashionnetwork/cn/lists/12) | -| [新闻资讯](https://fashionnetwork.cn/lists/11) | [11](https://rsshub.app/fashionnetwork/cn/lists/11) | - `, +| [新闻资讯](https://fashionnetwork.cn/lists/11) | [11](https://rsshub.app/fashionnetwork/cn/lists/11) |`, categories: ['new-media'], features: { diff --git a/lib/routes/fcbayern/namespace.ts b/lib/routes/fcbayern/namespace.ts new file mode 100644 index 000000000000..f2de778d8195 --- /dev/null +++ b/lib/routes/fcbayern/namespace.ts @@ -0,0 +1,8 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'FC Bayern München', + url: 'fcbayern.com', + categories: ['sport'], + lang: 'en', +}; diff --git a/lib/routes/fcbayern/news-cn.ts b/lib/routes/fcbayern/news-cn.ts new file mode 100644 index 000000000000..2fd69ce1ef54 --- /dev/null +++ b/lib/routes/fcbayern/news-cn.ts @@ -0,0 +1,36 @@ +import type { Data } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +export const cnNewsHander = async (limit: number) => { + const response = await ofetch('https://www.fcbayern.cn/api2018/news/list', { + method: 'POST', + headers: { + accept: 'application/json', + 'content-type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + page: '1', + pageSize: String(limit), + totalPage: '1', + tagId: '', + year: '-1', + month: '-1', + type: '1', + }), + }); + + const items = response.data.map((item) => ({ + title: item.title, + link: item.url, + pubDate: timezone(parseDate(item.time), 8), + image: item.pic?.split('?')[0], + })); + + return { + title: '拜仁慕尼黑俱乐部中文官方网站 - 新闻', + link: 'https://www.fcbayern.cn/news', + item: items, + } satisfies Data; +}; diff --git a/lib/routes/fcbayern/news.ts b/lib/routes/fcbayern/news.ts new file mode 100644 index 000000000000..5565654cdc11 --- /dev/null +++ b/lib/routes/fcbayern/news.ts @@ -0,0 +1,207 @@ +import type { Context } from 'hono'; + +import type { Route } from '@/types'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; + +import { cnNewsHander } from './news-cn'; + +const languages = { + en: 'fcbayern.com-en-gb', + es: 'fcbayern.com-es-es', + de: 'fcbayern.com-de-de', +}; + +const query = /* GraphQL */ ` + query Web_NewsSearch($channelId: String!, $query: String!, $filterTags: [String!], $filterSections: [String!], $filterTypes: [String!], $count: Int!, $offset: Int, $excludeHeroStageResultFromPageId: String) { + newsSearch( + channelId: $channelId + query: $query + filterTags: $filterTags + filterSections: $filterSections + filterTypes: $filterTypes + count: $count + offset: $offset + excludeHeroStageResultFromPageId: $excludeHeroStageResultFromPageId + ) { + total + offset + count + results { + ...Teaser + __typename + } + __typename + } + } + fragment Teaser on Teaser { + __typename + id + pageType + teaserTitle + teaserShortTitle + teaserSubtitle + teaserText + tag + publicationDate + teaserImage { + ...ImageFragment + __typename + } + teaserLink { + ...Link + __typename + } + teamEmblems { + ...TeamEmblems + __typename + } + teaserAppendix { + ... on GalleryTeaserAppendix { + galleryImages { + ...ImageFragment + __typename + } + __typename + } + ... on VideoTeaserAppendix { + documentId + kalturaId + __typename + } + __typename + } + markers + adMarker + channelId + } + fragment Link on Link { + __typename + newTabOrWindow + title + target { + ... on ExternalLink { + url + __typename + } + ... on InternalLink { + id + path + channelId + channelPath + differentChannelDomain + locale + pageType + __typename + } + __typename + } + } + fragment ImageFragment on ImageFragment { + __typename + url + alt + caption + copyright + origWidth + origHeight + aspectRatioImageOverrides { + ...AspectRatioImageOverride + __typename + } + } + fragment AspectRatioImageOverride on AspectRatioImageOverride { + aspectRatio + url + __typename + } + fragment TeamEmblems on TeamEmblems { + ownTeam + emblem { + ...ImageFragment + __typename + } + __typename + } +`; + +export const route: Route = { + path: '/news/:language?', + categories: ['sport'], + example: '/fcbayern/news', + parameters: { + language: { + description: 'Language', + options: [ + { value: 'en', label: 'English' }, + { value: 'es', label: 'Español' }, + { value: 'de', label: 'Deutsch' }, + { value: 'zh', label: '中文' }, + ], + default: 'en', + }, + }, + radar: [ + { + source: ['fcbayern.com/:language/news', 'fcbayern.com/:language'], + target: '/news/:language', + }, + { + source: ['www.fcbayern.cn/news', 'www.fcbayern.cn'], + target: '/news/zh', + }, + ], + name: 'News', + maintainers: ['TonyRL'], + handler, + url: 'fcbayern.com', +}; + +async function handler(ctx: Context) { + const { language = 'en' } = ctx.req.param(); + const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')!, 10) : 20; + + if (language === 'zh') { + return cnNewsHander(limit); + } + + const baseUrl = 'https://fcbayern.com'; + const channelId = languages[language] ?? languages.en; + + const response = await ofetch(`${baseUrl}/graphql`, { + method: 'POST', + query: { + client: 'fcbwebsite', + }, + body: { + operationName: 'Web_NewsSearch', + variables: { + query: '', + filterTags: ['tag_club', 'tag_team_women', 'tag_team_campus', 'tag_article_news', 'tag_competitions_bundesliga', 'tag_competitions_champions-league', 'tag_column_saebener-stories', 'tag_club_magazin-51'], + offset: 0, + count: limit, + channelId, + filterSections: ['section_professionals', 'section_club', 'section_women', 'section_campus', 'section_aroundtheworld'], + filterTypes: ['fcbhippo:ArticleDocument', 'fcbhippo:ImageGalleryDocument', 'fcbhippo:TeaserDocument'], + }, + query, + }, + }); + + const items = response.data.newsSearch.results.map((item) => ({ + title: item.teaserTitle, + description: item.teaserText, + link: `${baseUrl}${item.teaserLink.target.path}`, + pubDate: item.publicationDate ? parseDate(item.publicationDate) : undefined, + image: item.teaserImage?.url, + category: item.tag ? [item.tag] : undefined, + })); + + return { + title: 'FC Bayern München - News', + link: `${baseUrl}/${language}/news`, + language, + image: `${baseUrl}/favicon.ico`, + item: items, + }; +} diff --git a/lib/routes/ff14/ff14-global.ts b/lib/routes/ff14/ff14-global.ts index b9fff3d68126..f5fb82b0214e 100644 --- a/lib/routes/ff14/ff14-global.ts +++ b/lib/routes/ff14/ff14-global.ts @@ -28,7 +28,7 @@ export const route: Route = { | ------------ | ------ | ------ | ------- | ----- | | na | eu | fr | de | jp | - Category +Category | all | topics | notices | maintenance | updates | status | developers | | --- | ------ | ------- | ----------- | ------- | ------ | ---------- |`, diff --git a/lib/routes/fffdm/manhua/manhua.tsx b/lib/routes/fffdm/manhua/manhua.tsx index f4a2ca88e3e3..b346dc2c4a97 100644 --- a/lib/routes/fffdm/manhua/manhua.tsx +++ b/lib/routes/fffdm/manhua/manhua.tsx @@ -47,7 +47,7 @@ async function handler(ctx) { const id = ctx.req.param('id'); const count = ctx.req.query('limit') || 99999; const cdnNum = ctx.req.param('cdn') || 5; - const cdn = !Number.isNaN(Number.parseInt(cdnNum)) && 1 <= Number.parseInt(cdnNum) && Number.parseInt(cdnNum) <= 5 ? `https://p${cdnNum}.fzacg.com` : `https://p5.fzacg.com`; + const cdn = !Number.isNaN(Number.parseInt(cdnNum)) && 1 <= Number.parseInt(cdnNum) && Number.parseInt(cdnNum) <= 5 ? `https://p${cdnNum}.fzacg.com` : 'https://p5.fzacg.com'; // 获取漫画清单 const response = await got(`${host}/api/manhua/${id}`); diff --git a/lib/routes/finology/category.ts b/lib/routes/finology/category.ts index 2e50180fb128..e654b035ad0e 100644 --- a/lib/routes/finology/category.ts +++ b/lib/routes/finology/category.ts @@ -19,6 +19,7 @@ export const route: Route = { maintainers: ['Rjnishant530'], handler, description: `::: info Category + | Category | Link | | --------------------- | ------------------ | | **Business** | business | @@ -36,6 +37,7 @@ export const route: Route = { | Financial Ratios | stock-ratios | | Investor's Psychology | behavioral-finance | | Mutual Funds | mutual-fund | + :::`, }; @@ -44,7 +46,7 @@ async function handler(ctx: Context) { const extra = { description: (topic: string) => `Articles for your research and knowledge under ${topic}`, date: true, - selector: `div.card`, + selector: 'div.card', }; return await commonHandler('https://insider.finology.in', `/${category}`, extra); } diff --git a/lib/routes/finology/most-viewed.ts b/lib/routes/finology/most-viewed.ts index f89b9b4d464b..db90aada6b70 100644 --- a/lib/routes/finology/most-viewed.ts +++ b/lib/routes/finology/most-viewed.ts @@ -22,7 +22,7 @@ async function handler() { const extra = { description: (topic: string) => `Check out the most talked-about articles among our readers! ${topic}`, date: false, - selector: `div.card`, + selector: 'div.card', }; return await commonHandler('https://insider.finology.in', '/most-viewed', extra); } diff --git a/lib/routes/finology/tag.ts b/lib/routes/finology/tag.ts index 418cb39943b5..bc1dd8e76e80 100644 --- a/lib/routes/finology/tag.ts +++ b/lib/routes/finology/tag.ts @@ -19,6 +19,7 @@ export const route: Route = { handler, url: 'insider.finology.in/tag', description: `::: info Topic + | Topic | Link | | ------------------------ | ------------------------ | | Investment Decisions | investment-decisions | @@ -49,6 +50,7 @@ export const route: Route = { | World News | world-news | | Technology | technology | | Regulatory Bodies | regulatory-bodies | + :::`, }; @@ -57,7 +59,7 @@ async function handler(ctx: Context) { const extra = { description: (topic: string) => `Everything that Insider has to offer about ${topic} for you to read and learn.`, date: true, - selector: `div.card`, + selector: 'div.card', }; return await commonHandler('https://insider.finology.in', `/tag/${topic}`, extra); } diff --git a/lib/routes/finviz/news.ts b/lib/routes/finviz/news.ts index 52271de15b00..1fd5a5299883 100644 --- a/lib/routes/finviz/news.ts +++ b/lib/routes/finviz/news.ts @@ -41,7 +41,7 @@ export const route: Route = { handler, url: 'finviz.com/news.ashx', description: `| News | Blogs | -| ---- | ---- | +| ---- | ----- | | news | blogs |`, }; diff --git a/lib/routes/fjdaily/index.ts b/lib/routes/fjdaily/index.ts new file mode 100644 index 000000000000..629012e34d00 --- /dev/null +++ b/lib/routes/fjdaily/index.ts @@ -0,0 +1,210 @@ +import type { CheerioAPI } from 'cheerio'; +import { load } from 'cheerio'; +import type { Element } from 'domhandler'; + +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 ROOT_URL = 'https://fjrb.fjdaily.com'; + +const removeComments = (element) => { + element + .contents() + .filter((_, node) => node.type === 'comment') + .remove(); + element + .find('*') + .contents() + .filter((_, node) => node.type === 'comment') + .remove(); +}; + +const getNormalizedHtml = (element) => { + removeComments(element); + element.find('img, video, audio, source').removeAttr('referrerpolicy'); + + return element.html()?.trim(); +}; + +const hasMeaningfulContent = (element) => { + const text = element.text().replaceAll(/\s+/g, ''); + const media = element.find('img, video, audio, source').length; + + return text.length > 0 || media > 0; +}; + +const getDescription = (element) => { + const normalizedHtml = getNormalizedHtml(element); + + if (!normalizedHtml || !hasMeaningfulContent(element)) { + return; + } + + return normalizedHtml; +}; + +const getNodeSrc = (node: Element) => node.attribs?.src; + +const getAttachmentDescription = (attachment) => getDescription(attachment); + +const getNewMediaHtml = (detail: CheerioAPI, attachment, mainMediaSources: Set) => + attachment + .find('img, video, audio, source') + .toArray() + .filter((item: Element) => { + const src = getNodeSrc(item); + return src && !mainMediaSources.has(src); + }) + .map((item) => detail(item).prop('outerHTML')) + .filter(Boolean) + .join(''); + +const mergeDescription = (detail: CheerioAPI, mainDescription: string | undefined, attachment, mainMediaSources: Set) => { + if (!mainDescription) { + return getAttachmentDescription(attachment); + } + + const newMediaHtml = getNewMediaHtml(detail, attachment, mainMediaSources); + return newMediaHtml ? `${newMediaHtml}${mainDescription}` : mainDescription; +}; + +const getItemDescription = (detail: CheerioAPI) => { + const mainContent = detail('#content'); + const attachment = detail('.attachment'); + const mainDescription = getDescription(mainContent); + const mainMediaSources = new Set( + mainContent + .find('img, video, audio, source') + .toArray() + .map((item: Element) => getNodeSrc(item)) + .filter(Boolean) + ); + + return mergeDescription(detail, mainDescription, attachment, mainMediaSources); +}; + +const getIssueDate = async (date: string | undefined) => { + if (date) { + if (!/^\d{8}$/.test(date)) { + throw new Error('Invalid date format. Expected YYYYMMDD, for example `20260316`. '); + } + + return { + yearMonth: date.slice(0, 6), + day: date.slice(6, 8), + }; + } + + const indexResponse = await got(`${ROOT_URL}/pc/col/index.html`); + const $ = load(indexResponse.data); + const latestPath = $('#list li:first-child a').attr('href'); + + if (!latestPath) { + throw new Error('Failed to locate the latest Fujian Daily edition.'); + } + + const [, yearMonth, day] = latestPath.match(/(\d{6})\/(\d{2})\/node_\d+\.html/) ?? []; + + if (!yearMonth || !day) { + throw new Error('Failed to parse the latest Fujian Daily edition date.'); + } + + return { + yearMonth, + day, + }; +}; + +export const route: Route = { + path: '/:date?', + categories: ['traditional-media'], + example: '/fjdaily/20260316', + parameters: { date: '日期,格式为 `YYYYMMDD`,留空时抓取当天全部版面,例如 `20260316`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: [ + { + source: ['fjrb.fjdaily.com/pc/col/index.html'], + target: '/', + }, + { + source: ['fjrb.fjdaily.com/pc/col/:yearmonth/:day/node_:id.html'], + }, + ], + name: '电子报', + maintainers: ['DakoWang'], + handler, + description: '留空时抓取最新一期全部版面,也可以通过日期参数抓取指定日期的全部版面内容。', +}; + +async function handler(ctx) { + const date = ctx.req.param('date'); + const { yearMonth, day } = await getIssueDate(date); + const padUrl = `${ROOT_URL}/pad/col/${yearMonth}/${day}/node_01.html`; + const pageUrl = `${ROOT_URL}/pc/col/${yearMonth}/${day}/node_01.html`; + + const pageResponse = await got(padUrl); + const content = load(pageResponse.data); + + let currentCategory = ''; + const list: DataItem[] = content('#catalog li') + .toArray() + .map((item) => { + const element = content(item); + if (element.hasClass('verson')) { + currentCategory = element.text().replaceAll(/\s+/g, ' ').trim(); + return; + } + + const a = element.find('a').first(); + const href = a.attr('href'); + if (!href) { + return; + } + + return { + title: a.text().replaceAll(/\s+/g, ' ').trim(), + link: new URL(href, padUrl).toString().replace('/pad/', '/pc/'), + category: [currentCategory.replace(/^\d+版\s*/, '')], + }; + }) + .filter((item): item is DataItem => item !== undefined); + + if (list.length === 0) { + throw new Error(`No articles were found for ${yearMonth}${day}.`); + } + + const items = await Promise.all( + list.map((item) => + 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 description = getItemDescription(detail); + + return { + ...item, + author: author || undefined, + description: description || undefined, + pubDate: pubDate ? timezone(parseDate(pubDate), 8) : undefined, + }; + }) + ) + ); + + return { + title: `福建日报 - ${yearMonth.slice(0, 4)}-${yearMonth.slice(4, 6)}-${day}`, + link: pageUrl, + item: items, + }; +} diff --git a/lib/routes/fjdaily/namespace.ts b/lib/routes/fjdaily/namespace.ts new file mode 100644 index 000000000000..bc9ad19258fd --- /dev/null +++ b/lib/routes/fjdaily/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '福建日报', + url: 'fjdaily.com', + lang: 'zh-CN', +}; diff --git a/lib/routes/flyert/forum.ts b/lib/routes/flyert/forum.ts index 670a9d57ffcc..dfba714b8711 100644 --- a/lib/routes/flyert/forum.ts +++ b/lib/routes/flyert/forum.ts @@ -73,9 +73,8 @@ export const route: Route = { example: '/flyert/forum', parameters: { params: '参数,默认为空,可在对应分类页 URL 中找到' }, description: `::: tip - 若订阅 [酒店集团优惠](https://www.flyert.com.cn/forum.php?mod=forumdisplay&sum=all&fid=all&catid=322&filter=sortid&sortid=144&searchsort=1&youhui_type=19),网址为 \`https://www.flyert.com.cn/forum.php?mod=forumdisplay&sum=all&fid=all&catid=322&filter=sortid&sortid=144&searchsort=1&youhui_type=19\`。截取 \`https://www.flyert.com.cn/forum.php?\` 到末尾的部分 \`mod=forumdisplay&sum=all&fid=all&catid=322&filter=sortid&sortid=144&searchsort=1&youhui_type=19\` **进行 UrlEncode 编码** 后作为参数填入,此时路由为 [\`/flyert/forum/mod%3Dforumdisplay%26sum%3Dall%26fid%3Dall%26catid%3D322%26filter%3Dsortid%26sortid%3D144%26searchsort%3D1%26youhui_type%3D226\`](https://rsshub.app/flyert/forum/mod%3Dforumdisplay%26sum%3Dall%26fid%3Dall%26catid%3D322%26filter%3Dsortid%26sortid%3D144%26searchsort%3D1%26youhui_type%3D226)。 -::: - `, +若订阅 [酒店集团优惠](https://www.flyert.com.cn/forum.php?mod=forumdisplay\\&sum=all\\&fid=all\\&catid=322\\&filter=sortid\\&sortid=144\\&searchsort=1\\&youhui_type=19),网址为 \`https://www.flyert.com.cn/forum.php?mod=forumdisplay&sum=all&fid=all&catid=322&filter=sortid&sortid=144&searchsort=1&youhui_type=19\`。截取 \`https://www.flyert.com.cn/forum.php?\` 到末尾的部分 \`mod=forumdisplay&sum=all&fid=all&catid=322&filter=sortid&sortid=144&searchsort=1&youhui_type=19\` **进行 UrlEncode 编码** 后作为参数填入,此时路由为 [\`/flyert/forum/mod%3Dforumdisplay%26sum%3Dall%26fid%3Dall%26catid%3D322%26filter%3Dsortid%26sortid%3D144%26searchsort%3D1%26youhui_type%3D226\`](https://rsshub.app/flyert/forum/mod%3Dforumdisplay%26sum%3Dall%26fid%3Dall%26catid%3D322%26filter%3Dsortid%26sortid%3D144%26searchsort%3D1%26youhui_type%3D226)。 +:::`, categories: ['bbs'], features: { diff --git a/lib/routes/followin/index.ts b/lib/routes/followin/index.ts index 25ff74ab31a0..92bb89a11c7c 100644 --- a/lib/routes/followin/index.ts +++ b/lib/routes/followin/index.ts @@ -54,7 +54,7 @@ export const route: Route = { | ------- | ------ | ---- | ----- | --- | ------ | -------- | --------- | ------ | | 1 | 9 | 13 | 14 | 3 | 5 | 6 | 8 | 11 | - Language +Language | English | 简体中文 | 繁體中文 | Tiếng Việt | | ------- | -------- | -------- | ---------- | diff --git a/lib/routes/foodtalks/namespace.ts b/lib/routes/foodtalks/namespace.ts index 1f98d28b8ae7..2b19d47768b6 100644 --- a/lib/routes/foodtalks/namespace.ts +++ b/lib/routes/foodtalks/namespace.ts @@ -5,5 +5,5 @@ export const namespace: Namespace = { url: 'www.foodtalks.cn', categories: ['new-media'], lang: 'zh-CN', - description: 'FoodTalks全球食品资讯网是一个提供食品饮料行业新闻、资讯、分析和商业资源的领先在线平台。它涵盖行业趋势、市场动态、产品创新、投融资信息以及企业新闻,连接行业内的专业人士、企业和消费者。', + description: 'FoodTalks 全球食品资讯网是一个提供食品饮料行业新闻、资讯、分析和商业资源的领先在线平台。它涵盖行业趋势、市场动态、产品创新、投融资信息以及企业新闻,连接行业内的专业人士、企业和消费者。', }; diff --git a/lib/routes/foresightnews/index.ts b/lib/routes/foresightnews/index.ts index 3c9cbad9beb5..d298f0c78986 100644 --- a/lib/routes/foresightnews/index.ts +++ b/lib/routes/foresightnews/index.ts @@ -21,7 +21,7 @@ export const route: Route = { async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; - const apiUrl = new URL(`v2/feed`, apiRootUrl).href; + const apiUrl = new URL('v2/feed', apiRootUrl).href; const { items } = await processItems(apiUrl, limit); diff --git a/lib/routes/fortnite/news.ts b/lib/routes/fortnite/news.ts index 057831f577ad..a64faa80dc4d 100644 --- a/lib/routes/fortnite/news.ts +++ b/lib/routes/fortnite/news.ts @@ -2,7 +2,7 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/news/:options?', @@ -20,7 +20,7 @@ export const route: Route = { 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) + 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 | | ----------------- | ------- | -------- | ------ | ------ | ------ | @@ -39,9 +39,8 @@ async function handler(ctx) { const link = `${rootUrl}/${path}?lang=${language}`; const apiUrl = `https://www.fortnite.com/api/blog/getPosts?category=&postsPerPage=0&offset=0&locale=${language}&rootPageSlug=blog`; - // using puppeteer instead instead of got - // whitch may be blocked by anti-crawling script with response code 403 - const browser = await puppeteer(); + // Use Playwright instead of got, which may be blocked by anti-crawling scripts with response code 403. + const browser = await playwright(); const page = await browser.newPage(); // intercept all requests @@ -51,22 +50,34 @@ async function handler(ctx) { request.resourceType() === 'document' ? request.continue() : request.abort(); }); - // get json data in response event handler - let data; - page.on('response', async (res) => { - data = await res.json(); - }); - - // log manually (necessary for puppeteer) + // log manually (necessary for Playwright) logger.http(`Requesting ${apiUrl}`); - await page.goto(apiUrl, { - waitUntil: 'networkidle0', // if use 'domcontentloaded', `await page.content()` is necessary - }); + let data; + try { + const response = await page.goto(apiUrl, { + waitUntil: 'networkidle0', + }); + if (!response) { + throw new Error(`No response received from ${apiUrl}`); + } + 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 contentType = response.headers()['content-type']; + if (!contentType?.includes('application/json')) { + throw new Error(`Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'}`); + } - await page.close(); - await browser.close(); + data = await response.json(); + } finally { + await page.close(); + await browser.close(); + } const { blogList: list } = data; + const items = await Promise.all( list.map((item) => cache.tryGet(item.link, () => ({ diff --git a/lib/routes/freebuf/index.ts b/lib/routes/freebuf/index.ts index e3c0b0721d90..0e1edc5555ca 100644 --- a/lib/routes/freebuf/index.ts +++ b/lib/routes/freebuf/index.ts @@ -24,7 +24,7 @@ export const route: Route = { maintainers: ['trganda'], handler, description: `::: tip - Freebuf 的文章页面带有反爬虫机制,所以目前无法获取文章的完整内容。 +Freebuf 的文章页面带有反爬虫机制,所以目前无法获取文章的完整内容。 :::`, }; diff --git a/lib/routes/ft/myft.ts b/lib/routes/ft/myft.ts index 236b2b8f53eb..96b57d864d1d 100644 --- a/lib/routes/ft/myft.ts +++ b/lib/routes/ft/myft.ts @@ -22,8 +22,10 @@ export const route: Route = { maintainers: ['HenryQW'], handler, description: `::: tip - - Visit ft.com -> myFT -> Contact Preferences to enable personal RSS feed, see [help.ft.com](https://help.ft.com/faq/email-alerts-and-contact-preferences/what-is-myft-rss-feed/) - - Obtain the key from the personal RSS address, it looks like \`12345678-abcd-4036-82db-vdv20db024b8\` + +- Visit ft.com -> myFT -> Contact Preferences to enable personal RSS feed, see [help.ft.com](https://help.ft.com/faq/email-alerts-and-contact-preferences/what-is-myft-rss-feed/) +- Obtain the key from the personal RSS address, it looks like \`12345678-abcd-4036-82db-vdv20db024b8\` + :::`, }; @@ -69,9 +71,9 @@ async function handler(ctx) { ); return { - title: `FT.com - myFT`, + title: 'FT.com - myFT', link, - description: `FT.com - myFT`, + description: 'FT.com - myFT', item: items, }; } diff --git a/lib/routes/ftchinese/channel.ts b/lib/routes/ftchinese/channel.ts index e2f1a88cdc9f..ec85633d07e0 100644 --- a/lib/routes/ftchinese/channel.ts +++ b/lib/routes/ftchinese/channel.ts @@ -19,15 +19,17 @@ export const route: Route = { maintainers: ['HenryQW', 'xyqfer'], handler, description: `::: tip - - 不支持付费文章。 + +- 不支持付费文章。 + ::: - 通过提取文章全文,以提供比官方源更佳的阅读体验。 +通过提取文章全文,以提供比官方源更佳的阅读体验。 - 支持所有频道,频道名称见 [官方频道 RSS](http://www.ftchinese.com/channel/rss.html). +支持所有频道,频道名称见 [官方频道 RSS](http://www.ftchinese.com/channel/rss.html). - - 频道为单一路径,如 \`http://www.ftchinese.com/rss/news\` 则为 \`/ftchinese/simplified/news\`. - - 频道包含多重路径,如 \`http://www.ftchinese.com/rss/column/007000002\` 则替换 \`/\` 为 \`-\` \`/ftchinese/simplified/column-007000002\`.`, +- 频道为单一路径,如 \`http://www.ftchinese.com/rss/news\` 则为 \`/ftchinese/simplified/news\`. +- 频道包含多重路径,如 \`http://www.ftchinese.com/rss/column/007000002\` 则替换 \`/\` 为 \`-\` \`/ftchinese/simplified/column-007000002\`.`, }; async function handler(ctx) { diff --git a/lib/routes/fuliba/latest.ts b/lib/routes/fuliba/latest.ts index de117786aa26..77207a7f3c13 100644 --- a/lib/routes/fuliba/latest.ts +++ b/lib/routes/fuliba/latest.ts @@ -27,7 +27,7 @@ export const route: Route = { }; async function handler(ctx) { - const { data: response } = await got(`https://fuliba2023.net/wp-json/wp/v2/posts`, { + const { data: response } = await got('https://fuliba2023.net/wp-json/wp/v2/posts', { searchParams: { per_page: ctx.req.query('limit') ?? 100, _embed: 1, @@ -44,7 +44,7 @@ async function handler(ctx) { return { title: '福利吧', - link: `https://fuliba2023.net`, + link: 'https://fuliba2023.net', item: items, }; } diff --git a/lib/routes/furaffinity/browse.ts b/lib/routes/furaffinity/browse.ts index 8ab993012802..d43f311232d0 100644 --- a/lib/routes/furaffinity/browse.ts +++ b/lib/routes/furaffinity/browse.ts @@ -53,7 +53,7 @@ async function handler(ctx) { return { title: 'Fur Affinity | Browse', link: 'https://www.furaffinity.net/browse/', - description: `Fur Affinity Browsing Artwork`, + description: 'Fur Affinity Browsing Artwork', item: items, }; } diff --git a/lib/routes/furaffinity/home.ts b/lib/routes/furaffinity/home.ts index 63cc0fab75bb..b67c4fb539a1 100644 --- a/lib/routes/furaffinity/home.ts +++ b/lib/routes/furaffinity/home.ts @@ -75,7 +75,7 @@ async function handler(ctx) { return { title: 'Fur Affinity | Home', link: 'https://www.furaffinity.net/', - description: `Fur Affinity Index`, + description: 'Fur Affinity Index', item: items, }; } diff --git a/lib/routes/furaffinity/search.ts b/lib/routes/furaffinity/search.ts index 2fabc24d97ef..ca7d032dca9a 100644 --- a/lib/routes/furaffinity/search.ts +++ b/lib/routes/furaffinity/search.ts @@ -30,14 +30,14 @@ export const route: Route = { ], handler, description: `Additional search parameters -| Parameter | Description | Default | Options | -|-----------------|----------------------|-----------|----------------------------------------------------------------| -| order_by | Sort by | relevancy | relevancy, date, popularity | -| order_direction | Sort order | desc | desc, asc | -| range | Date range | all | all, 1day, 3days, 7days, 30days, 90days, 1year, 3years, 5years | -| pattern | Query match pattern | extended | all, any, extended | -| type | Category of artworks | all | art, flash, photo, music, story, poetry | -`, + +| Parameter | Description | Default | Options | +| ---------------- | -------------------- | --------- | -------------------------------------------------------------- | +| order\\_by | Sort by | relevancy | relevancy, date, popularity | +| order\\_direction | Sort order | desc | desc, asc | +| range | Date range | all | all, 1day, 3days, 7days, 30days, 90days, 1year, 3years, 5years | +| pattern | Query match pattern | extended | all, any, extended | +| type | Category of artworks | all | art, flash, photo, music, story, poetry |`, }; async function handler(ctx) { @@ -67,7 +67,7 @@ async function handler(ctx) { allowEmpty: true, title: 'Fur Affinity | Search', link: `https://www.furaffinity.net/Search/?q=${query}`, - description: `Fur Affinity Search`, + description: 'Fur Affinity Search', item: items, }; } diff --git a/lib/routes/furaffinity/status.ts b/lib/routes/furaffinity/status.ts index ac5deb083cdc..c37a4796b708 100644 --- a/lib/routes/furaffinity/status.ts +++ b/lib/routes/furaffinity/status.ts @@ -53,7 +53,7 @@ async function handler() { return { title: 'Fur Affinity | Status', link: 'https://www.furaffinity.net/', - description: `Fur Affinity Status`, + description: 'Fur Affinity Status', item: items, }; } diff --git a/lib/routes/furaffinity/user.ts b/lib/routes/furaffinity/user.ts index 60c7ce54404c..eca11bec8622 100644 --- a/lib/routes/furaffinity/user.ts +++ b/lib/routes/furaffinity/user.ts @@ -91,7 +91,7 @@ async function handler(ctx) { throw new Error(`Unknown type: ${x}`); } } - contact_result += `
    `; + contact_result += '
    '; } } diff --git a/lib/routes/galxe/index.ts b/lib/routes/galxe/index.ts index 90110bd75d3a..469bf3fc2444 100644 --- a/lib/routes/galxe/index.ts +++ b/lib/routes/galxe/index.ts @@ -45,7 +45,7 @@ async function handler(ctx) { listType: 'Newest', }, }, - query: ` + query: /* GraphQL */ ` query BrowseSpaceCampaigns($id: Int, $alias: String, $campaignInput: ListCampaignInput!) { space(id: $id, alias: $alias) { id diff --git a/lib/routes/gameapps/index.tsx b/lib/routes/gameapps/index.tsx index 1b2b25c3502e..b8ab6ae25062 100644 --- a/lib/routes/gameapps/index.tsx +++ b/lib/routes/gameapps/index.tsx @@ -11,6 +11,7 @@ import parser from '@/utils/rss-parser'; export const route: Route = { path: '/', example: '/gameapps', + categories: ['game'], radar: [ { source: ['gameapps.hk/'], @@ -36,7 +37,7 @@ async function handler() { }); const $ = load(response); - item.title = $('meta[property="og:title"]').attr('content') ?? $('.news-title h1').text(); + item.title = ($('meta[property="og:title"]').attr('content') ?? $('.news-title h1').text()).replace(' - 香港手機遊戲網 GameApps.hk', ''); item.category = $('.tags-wrap .tag-item') .toArray() .map((el) => $(el).text().trim().replace(/^#/, '')); @@ -49,7 +50,7 @@ async function handler() { delete item.isoDate; const intro = $('div.introduction.media.news-intro div.media-body').html()?.trim(); - const desc = $('.article-content').html()?.trim(); + const desc = $('.article-content, .news-content').html()?.trim(); item.description = renderToString( <> {intro ? raw(intro) : null} diff --git a/lib/routes/gamebase/news.tsx b/lib/routes/gamebase/news.tsx index 14003396002e..95c824c4eda8 100644 --- a/lib/routes/gamebase/news.tsx +++ b/lib/routes/gamebase/news.tsx @@ -148,8 +148,7 @@ export const route: Route = { ::: | newslist | r18list | -| -------- | ------- | -`, +| -------- | ------- |`, categories: ['game'], features: { requireConfig: false, @@ -190,7 +189,6 @@ export const route: Route = { ::: | newslist | r18list | -| -------- | ------- | -`, +| -------- | ------- |`, }, }; diff --git a/lib/routes/gamersecret/index.ts b/lib/routes/gamersecret/index.ts index 4fb5df94f5f7..6e6cbab45364 100644 --- a/lib/routes/gamersecret/index.ts +++ b/lib/routes/gamersecret/index.ts @@ -31,7 +31,7 @@ export const route: Route = { | ----------- | -- | ----------- | -------- | ---- | ------ | | latest-news | pc | playstation | nintendo | xbox | moblie | - Or +Or | GENERAL | GENERAL EN | MOBILE | MOBILE EN | | ---------------- | ------------------ | --------------- | ----------------- | diff --git a/lib/routes/gcores/articles.ts b/lib/routes/gcores/articles.ts index 8322f4ea163d..88bcfa0dc0cd 100644 --- a/lib/routes/gcores/articles.ts +++ b/lib/routes/gcores/articles.ts @@ -9,7 +9,7 @@ export const handler = async (ctx: Context): Promise => { const limit: number = Number.parseInt(ctx.req.query('limit') ?? '30', 10); const targetUrl: string = new URL('articles', baseUrl).href; - const apiUrl: string = new URL(`gapi/v1/articles`, baseUrl).href; + const apiUrl: string = new URL('gapi/v1/articles', baseUrl).href; const query = { 'page[limit]': limit, diff --git a/lib/routes/gcores/categories.ts b/lib/routes/gcores/categories.ts index 61785b68a620..9c7075b1653a 100644 --- a/lib/routes/gcores/categories.ts +++ b/lib/routes/gcores/categories.ts @@ -74,8 +74,7 @@ export const route: Route = { | 全部 | 播客 | 文章 | 资讯 | 视频 | | ---- | ------ | -------- | ---- | ------ | -| | radios | articles | news | videos | -`, +| | radios | articles | news | videos |`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gcores/collections.ts b/lib/routes/gcores/collections.ts index 47cf12c67bf9..838994437ac8 100644 --- a/lib/routes/gcores/collections.ts +++ b/lib/routes/gcores/collections.ts @@ -74,8 +74,7 @@ export const route: Route = { | 全部 | 播客 | 文章 | 资讯 | 视频 | | ---- | ------ | -------- | ---- | ------ | -| | radios | articles | news | videos | -`, +| | radios | articles | news | videos |`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gcores/tags.ts b/lib/routes/gcores/tags.ts index 28ec4cdc6f54..38b93e7c7982 100644 --- a/lib/routes/gcores/tags.ts +++ b/lib/routes/gcores/tags.ts @@ -74,8 +74,7 @@ export const route: Route = { | 全部 | 播客 | 文章 | 资讯 | 视频 | | ---- | ------ | -------- | ---- | ------ | -| | radios | articles | news | videos | -`, +| | radios | articles | news | videos |`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gcores/topics.ts b/lib/routes/gcores/topics.ts index 37944068f083..106fee564c12 100644 --- a/lib/routes/gcores/topics.ts +++ b/lib/routes/gcores/topics.ts @@ -35,8 +35,7 @@ export const route: Route = { }, description: `::: tip 若订阅 [我的年度总结](https://www.gcores.com/topics/581),网址为 \`https://www.gcores.com/topics/581\`,请截取 \`https://www.gcores.com/topics/\` 到末尾的部分 \`581\` 作为 \`id\` 参数填入,此时目标路由为 [\`/gcores/topics/581/recommend\`](https://rsshub.app/gcores/topics/581/recommend)。 -::: -`, +:::`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gcores/user-radios.ts b/lib/routes/gcores/user-radios.ts index a7b1238dd167..dca1a047cf41 100644 --- a/lib/routes/gcores/user-radios.ts +++ b/lib/routes/gcores/user-radios.ts @@ -163,8 +163,7 @@ export const route: Route = { }, description: `::: tip 若订阅用户 [这样重这样轻](https://www.gcores.com/users/31418) 发布的播客,网址为 \`https://www.gcores.com/users/31418\`,请截取 \`https://www.gcores.com/users/\` 之后的部分 \`31418\` 作为 \`id\` 参数填入,此时目标路由为 [\`/gcores/users/31418/radios\`](https://rsshub.app/gcores/users/31418/radios)。 -::: -`, +:::`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gcores/user-talks.ts b/lib/routes/gcores/user-talks.ts index 4dde7cbb4056..f893cce8f447 100644 --- a/lib/routes/gcores/user-talks.ts +++ b/lib/routes/gcores/user-talks.ts @@ -120,8 +120,7 @@ export const route: Route = { }, description: `::: tip 若订阅用户 [这样重这样轻](https://www.gcores.com/users/31418/talks) 的动态,网址为 \`https://www.gcores.com/users/31418/talks\`,请截取 \`https://www.gcores.com/users/\` 到 \`/talks\` 之间的部分 \`31418\` 作为 \`id\` 参数填入,此时目标路由为 [\`/gcores/users/31418/talks\`](https://rsshub.app/gcores/users/31418/talks)。 -::: -`, +:::`, categories: ['game'], features: { requireConfig: false, diff --git a/lib/routes/gdut/oa-news.ts b/lib/routes/gdut/oa-news.ts index f05d7fbe3a13..a190200fa2d7 100644 --- a/lib/routes/gdut/oa-news.ts +++ b/lib/routes/gdut/oa-news.ts @@ -70,15 +70,14 @@ export const route: Route = { url: 'oas.gdut.edu.cn/seeyon', description: `学校可能会因为 IP 来源非学校而做出一定的限制,建议在校内网络环境下使用 RSS 阅读器订阅。 -| 类型 | 参数 | 可能需要校内访问 | -| ---- | ---- | ---------------- | -| 部处简讯 | department | 是 | -| 学院简讯 | academy | 是 | -| 校内通知 | notice | 是 | -| 公示公告 | announcement | 是 | -| 招标结果 | tender_result | 否 | -| 招标公告 | tender_invite | 否 | -`, +| 类型 | 参数 | 可能需要校内访问 | +| -------- | -------------- | ---------------- | +| 部处简讯 | department | 是 | +| 学院简讯 | academy | 是 | +| 校内通知 | notice | 是 | +| 公示公告 | announcement | 是 | +| 招标结果 | tender\\_result | 否 | +| 招标公告 | tender\\_invite | 否 |`, }; async function handler(ctx) { @@ -209,9 +208,9 @@ async function handler(ctx) { ); return { - title: `广东工业大学通知公文网 - ` + type.name, + title: '广东工业大学通知公文网 - ' + type.name, link: site, - description: `广东工业大学通知公文网`, + description: '广东工业大学通知公文网', item: results, }; } diff --git a/lib/routes/geekpark/index.ts b/lib/routes/geekpark/index.ts index d6522aa897d7..e752fec41733 100644 --- a/lib/routes/geekpark/index.ts +++ b/lib/routes/geekpark/index.ts @@ -135,20 +135,19 @@ export const route: Route = { example: '/geekpark', parameters: { column: '栏目 id,默认为空,即首页资讯,可在对应栏目页 URL 中找到' }, description: `::: tip - 若订阅 [综合报道](https://www.geekpark.net/column/179),网址为 \`https://www.geekpark.net/column/179\`。截取 \`https://www.geekpark.net/column/\` 到末尾的部分 \`179\` 作为参数填入,此时路由为 [\`/geekpark/179\`](https://rsshub.app/geekpark/179)。 +若订阅 [综合报道](https://www.geekpark.net/column/179),网址为 \`https://www.geekpark.net/column/179\`。截取 \`https://www.geekpark.net/column/\` 到末尾的部分 \`179\` 作为参数填入,此时路由为 [\`/geekpark/179\`](https://rsshub.app/geekpark/179)。 ::: | 栏目 | ID | | ------------------------------------------------------------ | -------------------------------------- | | [综合报道](https://www.geekpark.net/column/179) | [179](https://rsshub.app/geekpark/179) | -| [AI新浪潮观察](https://www.geekpark.net/column/304) | [304](https://rsshub.app/geekpark/304) | +| [AI 新浪潮观察](https://www.geekpark.net/column/304) | [304](https://rsshub.app/geekpark/304) | | [新造车观察](https://www.geekpark.net/column/305) | [305](https://rsshub.app/geekpark/305) | | [财报解读](https://www.geekpark.net/column/271) | [271](https://rsshub.app/geekpark/271) | -| [底稿对话CEO系列](https://www.geekpark.net/column/308) | [308](https://rsshub.app/geekpark/308) | +| [底稿对话 CEO 系列](https://www.geekpark.net/column/308) | [308](https://rsshub.app/geekpark/308) | | [Geek Insight 特稿系列](https://www.geekpark.net/column/306) | [306](https://rsshub.app/geekpark/306) | | [心科技](https://www.geekpark.net/column/307) | [307](https://rsshub.app/geekpark/307) | -| [行业资讯](https://www.geekpark.net/column/2) | [2](https://rsshub.app/geekpark/2) | - `, +| [行业资讯](https://www.geekpark.net/column/2) | [2](https://rsshub.app/geekpark/2) |`, categories: ['new-media'], features: { diff --git a/lib/routes/gelbooru/post.ts b/lib/routes/gelbooru/post.ts index 35f985d80567..b943b45da9aa 100644 --- a/lib/routes/gelbooru/post.ts +++ b/lib/routes/gelbooru/post.ts @@ -48,18 +48,17 @@ export const route: Route = { ], name: '标签查询', maintainers: ['magicFeirl'], - description: ` -- 默认查询: \`/gelbooru/post\` 功能等同查询 Gelbooru 网站最新的投稿 + description: `- 默认查询: \`/gelbooru/post\` 功能等同查询 Gelbooru 网站最新的投稿 - 单标签查询: \`/gelbooru/post/1girl\` 查询 \`1girl\` 的最新投稿 - 多标签查询: \`/gelbooru/post/1girl school_uniform rating:general\` - 指定为原图: \`/gelbooru/post/1girl school_uniform rating:general/orig\` -- 更多例子: 请参考 Gelbooru 官方 wiki https://gelbooru.com/index.php?page=wiki&s=&s=view&id=25921 +- 更多例子:请参考 Gelbooru 官方 wiki **可选的 URL 参数** -- limit 页面返回数据量,默认 40,可选 1 ~ 100 -e.g.: \`/gelbooru/post?limit=20&\` -`, +- limit 页面返回数据量,默认 40,可选 1 \\~ 100 + +e.g.: \`/gelbooru/post?limit=20&\``, handler, }; diff --git a/lib/routes/gelonghui/home.ts b/lib/routes/gelonghui/home.ts index 9f3a2aa9785c..b846ce062949 100644 --- a/lib/routes/gelonghui/home.ts +++ b/lib/routes/gelonghui/home.ts @@ -36,7 +36,7 @@ export const route: Route = { handler, description: `| 推荐 | 股票 | 基金 | 新股 | 研报 | | --------------- | ----- | ---- | ---------- | -------- | -| web_home_page | stock | fund | new_stock | research |`, +| web\\_home\\_page | stock | fund | new\\_stock | research |`, }; async function handler(ctx) { diff --git a/lib/routes/gelonghui/hot-article.ts b/lib/routes/gelonghui/hot-article.ts index 099491b2ea5a..752c8c655ea0 100644 --- a/lib/routes/gelonghui/hot-article.ts +++ b/lib/routes/gelonghui/hot-article.ts @@ -43,7 +43,7 @@ export const route: Route = { async function handler(ctx) { const type = ctx.req.param('type') === 'week' ? 1 : 0; - const baseUrl = `https://www.gelonghui.com`; + const baseUrl = 'https://www.gelonghui.com'; const { data: response } = await got(baseUrl); const $ = load(response); diff --git a/lib/routes/gelonghui/keyword.ts b/lib/routes/gelonghui/keyword.ts index ad1070cc5056..90a4f6b8954e 100644 --- a/lib/routes/gelonghui/keyword.ts +++ b/lib/routes/gelonghui/keyword.ts @@ -27,7 +27,7 @@ export const route: Route = { async function handler(ctx) { const keyword = ctx.req.param('keyword'); - const currentUrl = `https://www.gelonghui.com/api/post/search/v4`; + const currentUrl = 'https://www.gelonghui.com/api/post/search/v4'; const { data } = await got(currentUrl, { searchParams: { keyword, diff --git a/lib/routes/genossenschaften/index.ts b/lib/routes/genossenschaften/index.ts index f9ad489e170c..ac726a0d9fad 100644 --- a/lib/routes/genossenschaften/index.ts +++ b/lib/routes/genossenschaften/index.ts @@ -14,8 +14,7 @@ export const route: Route = { path: '*', maintainers: ['sk22'], categories: ['other'], - description: ` -Note that all parameters are optional and many can be specified multiple times + description: `Note that all parameters are optional and many can be specified multiple times (e.g. \`district=wien-1-innere-stadt&district=wien-2-leopoldstadt\`). Only returns the first page of search results, allowing you to keep track of @@ -23,7 +22,7 @@ newly added apartments. If you're looking for an apartment, make sure to also look through the other pages on the website. ::: tip -To get your query URL, go to https://genossenschaften.immo and apply all +To get your query URL, go to and apply all desired filters. If you want to filter by (all districts of a) federal state (e.g. \`/immobilien/regionen/wien/\`), please open the district selector and de- and re-select any district, so that the region in the URL gets replaced diff --git a/lib/routes/gesiba/index.ts b/lib/routes/gesiba/index.ts index 164cc5a7318b..cebbc8b86171 100644 --- a/lib/routes/gesiba/index.ts +++ b/lib/routes/gesiba/index.ts @@ -9,7 +9,7 @@ const FEED_LANGUAGE = 'de' as const; const FEED_LOGO = 'https://www.gesiba.at/assets/img/gesiba-logo.png'; const BASE_URL = 'https://www.gesiba.at' as const; const MAGIC_QUERY_PARAMS = - `p=actions/sprig-core/components/render&sprig%3AsiteId=0347ff5aeebc536543e7e865c4ed9dd97a9eb81ef054d47105ba6c4ca1da10801&sprig%3Aid=37ff8c3b5f5f7ad3bca87140e3fb8094cc656fcdc5d705c964065a830717c906component-vvyfgj&sprig%3Acomponent=e0737af02d4f2e1586c10610b098b6f75b51b994ddbd89cafd13ef07dc6da9ca&sprig%3Atemplate=3b669582a22c2742c4b713143ea4663ddba00812852f876074de96ad2fc04c24_components%2F_objectList&sprig%3Avariables%5BbaseUrl%5D=0c66aec55b6b038f0c9eb2ddea75d44d0c52b6fbc93960847d53f9d0af3f6162%2Fimmobilien%2Fwohnungen` as const; + 'p=actions/sprig-core/components/render&sprig%3AsiteId=0347ff5aeebc536543e7e865c4ed9dd97a9eb81ef054d47105ba6c4ca1da10801&sprig%3Aid=37ff8c3b5f5f7ad3bca87140e3fb8094cc656fcdc5d705c964065a830717c906component-vvyfgj&sprig%3Acomponent=e0737af02d4f2e1586c10610b098b6f75b51b994ddbd89cafd13ef07dc6da9ca&sprig%3Atemplate=3b669582a22c2742c4b713143ea4663ddba00812852f876074de96ad2fc04c24_components%2F_objectList&sprig%3Avariables%5BbaseUrl%5D=0c66aec55b6b038f0c9eb2ddea75d44d0c52b6fbc93960847d53f9d0af3f6162%2Fimmobilien%2Fwohnungen' as const; // https://www.gesiba.at/index.php?p=actions/sprig-core/components/render&verfuegbar=alle&size-from=&size-to=&rooms-from=&rooms-to=&betreuung=&sprig%3AsiteId=0347ff5aeebc536543e7e865c4ed9dd97a9eb81ef054d47105ba6c4ca1da10801&sprig%3Aid=37ff8c3b5f5f7ad3bca87140e3fb8094cc656fcdc5d705c964065a830717c906component-vvyfgj&sprig%3Acomponent=e0737af02d4f2e1586c10610b098b6f75b51b994ddbd89cafd13ef07dc6da9ca&sprig%3Atemplate=3b669582a22c2742c4b713143ea4663ddba00812852f876074de96ad2fc04c24_components%2F_objectList&sprig%3Avariables%5BbaseUrl%5D=0c66aec55b6b038f0c9eb2ddea75d44d0c52b6fbc93960847d53f9d0af3f6162%2Fimmobilien%2Fwohnungen @@ -19,11 +19,9 @@ export const route: Route = { path: '*', maintainers: ['sk22'], categories: ['other'], - description: ` -Note that, on https://www.gesiba.at/immobilien/wohnungen, filters are added to + description: `Note that, on , filters are added to 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 -`, +like \`&plz[]=1100&plz[]=1120\`, if multiple values are passed to one parameter`, async handler(ctx) { let params = getSubPath(ctx).slice(1); diff --git a/lib/routes/github/activity.ts b/lib/routes/github/activity.ts index 935f7c24951d..abb4ed5e9c17 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?.replaceAll(/href="\/(.+?)"/g, 'href="https://github.com/$1"') ?? '', { 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 61446b743aa1..d99a41df6e93 100644 --- a/lib/routes/github/advisor.ts +++ b/lib/routes/github/advisor.ts @@ -26,26 +26,25 @@ export const route: Route = { name: 'Github Advisory Database RSS', maintainers: ['sd0ric4'], handler, - description: ` -| Type | Description | Explanation | -| --- | --- | --- | -| reviewed | Reviewed | 已审核 | -| unreviewed | Unreviewed | 未审核 | + description: `| Type | Description | Explanation | +| ---------- | ----------- | ----------- | +| reviewed | Reviewed | 已审核 | +| unreviewed | Unreviewed | 未审核 | -| Category | Description | Explanation | -| --- | --- | --- | -| composer | Composer | PHP 依赖管理工具 | -| go | Go | Go 语言包管理工具 | -| maven | Maven | Java 项目管理工具 | -| npm | NPM | Node.js 包管理工具 | -| nuget | NuGet | .NET 包管理工具 | -| pip | Pip | Python 包管理工具 | -| pub | Pub | Dart 包管理工具 | -| rubygems | RubyGems | Ruby 包管理工具 | -| rust | Rust | Rust 包管理工具 | -| erlang | Erlang | Erlang 包管理工具 | -| actions | Actions | GitHub Actions | -| swift | Swift | Swift 包管理工具 |`, +| Category | Description | Explanation | +| -------- | ----------- | ------------------ | +| composer | Composer | PHP 依赖管理工具 | +| go | Go | Go 语言包管理工具 | +| maven | Maven | Java 项目管理工具 | +| npm | NPM | Node.js 包管理工具 | +| nuget | NuGet | .NET 包管理工具 | +| pip | Pip | Python 包管理工具 | +| pub | Pub | Dart 包管理工具 | +| rubygems | RubyGems | Ruby 包管理工具 | +| rust | Rust | Rust 包管理工具 | +| erlang | Erlang | Erlang 包管理工具 | +| actions | Actions | GitHub Actions | +| swift | Swift | Swift 包管理工具 |`, }; async function handler(ctx) { @@ -53,7 +52,7 @@ async function handler(ctx) { const apiRootUrl = 'https://github.com/advisories'; const apiUrl = `${apiRootUrl}?query=type%3A${type}+ecosystem%3A${category}`; - const currentUrl = `https://github.com/advisories`; + const currentUrl = 'https://github.com/advisories'; const response = await got({ method: 'get', diff --git a/lib/routes/github/eventapi.ts b/lib/routes/github/eventapi.ts index 4caaafdbc9cf..50290c0d8c7b 100644 --- a/lib/routes/github/eventapi.ts +++ b/lib/routes/github/eventapi.ts @@ -48,7 +48,7 @@ function formatEventItem(event: any) { description = `PR: ${link}`; } else { link = `https://github.com/${repo.name}`; - description = `PR: Unknown`; + description = 'PR: Unknown'; } break; case 'PullRequestReviewCommentEvent': @@ -117,7 +117,7 @@ function formatEventItem(event: any) { for (const page of payload.pages ?? []) { description += `
  • Page ${page.page_name} ${page.action} ${page.summary ? `: ${page.summary}` : ''}
  • `; } - description += ``; + description += ''; link = `https://github.com/${repo.name}`; break; case 'DiscussionEvent': diff --git a/lib/routes/github/follower.ts b/lib/routes/github/follower.ts index 47941fb95b16..b9503abc0409 100644 --- a/lib/routes/github/follower.ts +++ b/lib/routes/github/follower.ts @@ -42,7 +42,7 @@ async function handler(ctx) { Authorization: `bearer ${config.github.access_token}`, }, json: { - query: ` + query: /* GraphQL */ ` { user(login: "${user}") { followers(first: 10) { diff --git a/lib/routes/github/namespace.ts b/lib/routes/github/namespace.ts index bff74c58d19c..bbee407a586f 100644 --- a/lib/routes/github/namespace.ts +++ b/lib/routes/github/namespace.ts @@ -6,11 +6,12 @@ export const namespace: Namespace = { description: `::: tip GitHub provides some official RSS feeds: -- Repo releases: \`https://github.com/:owner/:repo/releases.atom\` -- Repo commits: \`https://github.com/:owner/:repo/commits.atom\` -- User activities: \`https://github.com/:user.atom\` -- Private feed: \`https://github.com/:user.private.atom?token=:secret\` (Note: You can ONLY obtain this url via an [API](https://docs.github.com/en/rest/activity/feeds?apiVersion=2022-11-28) call with a [Personal Access Token](https://github.com/settings/tokens/new) with **ENOUGH** scopes now.) -- Wiki history: \`https://github.com/:owner/:repo/wiki.atom\` +- Repo releases: \`https://github.com/:owner/:repo/releases.atom\` +- Repo commits: \`https://github.com/:owner/:repo/commits.atom\` +- User activities: \`https://github.com/:user.atom\` +- Private feed: \`https://github.com/:user.private.atom?token=:secret\` (Note: You can ONLY obtain this url via an [API](https://docs.github.com/en/rest/activity/feeds?apiVersion=2022-11-28) call with a [Personal Access Token](https://github.com/settings/tokens/new) with **ENOUGH** scopes now.) +- Wiki history: \`https://github.com/:owner/:repo/wiki.atom\` + :::`, lang: 'en', }; diff --git a/lib/routes/github/pulls.ts b/lib/routes/github/pulls.ts index 1d0c87698873..7dcf7550fb81 100644 --- a/lib/routes/github/pulls.ts +++ b/lib/routes/github/pulls.ts @@ -14,7 +14,29 @@ export const route: Route = { path: '/pull/:user/:repo/:state?/:labels?', categories: ['programming'], example: '/github/pull/DIYgod/RSSHub', - parameters: { user: 'User name', repo: 'Repo name', state: 'the state of pull requests. Can be either `open`, `closed`, or `all`. Default: `open`.', labels: 'a list of comma separated label names' }, + parameters: { + user: 'GitHub username', + repo: 'GitHub repo name', + state: { + description: 'the state of pull requests.', + default: 'open', + options: [ + { + label: 'Open', + value: 'open', + }, + { + label: 'Closed', + value: 'closed', + }, + { + label: 'All', + value: 'all', + }, + ], + }, + labels: 'a list of comma separated label names', + }, features: { requireConfig: false, requirePuppeteer: false, @@ -61,7 +83,7 @@ async function handler(ctx) { return { allowEmpty: true, - title: `${user}/${repo} Pull requests`, + title: `${user}/${repo} ${state.replace(/^\S/, (s) => s.toUpperCase())} Pull Requests${labels ? ' - ' + labels : ''}`, link: host, item: data.map((item) => ({ title: item.title, diff --git a/lib/routes/github/star.ts b/lib/routes/github/star.ts index dd873c9594b1..f369b8f8d01e 100644 --- a/lib/routes/github/star.ts +++ b/lib/routes/github/star.ts @@ -45,7 +45,7 @@ async function handler(ctx) { Authorization: `bearer ${config.github.access_token}`, }, json: { - query: ` + query: /* GraphQL */ ` { repository(owner: "${user}", name: "${repo}") { stargazers(last: 10) { diff --git a/lib/routes/github/topic.ts b/lib/routes/github/topic.ts index 3be582428c1f..d14a3d9ebe05 100644 --- a/lib/routes/github/topic.ts +++ b/lib/routes/github/topic.ts @@ -32,7 +32,7 @@ export const route: Route = { | \`o\` | Sorting Order | \`asc\`, \`desc\` | | \`s\` | Sorting Criteria | \`stars\`, \`forks\`, \`updated\` | - For instance, the \`/github/topics/framework/l=php&o=desc&s=stars\` route will generate the RSS feed corresponding to this [page](https://github.com/topics/framework?l=php&o=desc&s=stars).`, +For instance, the \`/github/topics/framework/l=php&o=desc&s=stars\` route will generate the RSS feed corresponding to this [page](https://github.com/topics/framework?l=php\\&o=desc\\&s=stars).`, }; async function handler(ctx) { diff --git a/lib/routes/github/trending.tsx b/lib/routes/github/trending.tsx index a14ba30c996b..c3d4ec092c32 100644 --- a/lib/routes/github/trending.tsx +++ b/lib/routes/github/trending.tsx @@ -98,7 +98,7 @@ async function handler(ctx) { Authorization: `bearer ${config.github.access_token}`, }, json: { - query: ` + query: /* GraphQL */ ` query { ${trendingRepos .map( diff --git a/lib/routes/gitstar-ranking/index.tsx b/lib/routes/gitstar-ranking/index.tsx index 99a4ce7dade1..79a66a3c7241 100644 --- a/lib/routes/gitstar-ranking/index.tsx +++ b/lib/routes/gitstar-ranking/index.tsx @@ -152,8 +152,7 @@ To subscribe to [Repositories](https://gitstar-ranking.com/repositories), where | ---------------------------------------------------------- | ----------------------------------------------------------------- | | [Users](https://gitstar-ranking.com/users) | [users](https://rsshub.app/gitstar-ranking/users) | | [Organizations](https://gitstar-ranking.com/organizations) | [organizations](https://rsshub.app/gitstar-ranking/organizations) | -| [Repositories](https://gitstar-ranking.com/repositories) | [repositories](https://rsshub.app/gitstar-ranking/repositories) | -`, +| [Repositories](https://gitstar-ranking.com/repositories) | [repositories](https://rsshub.app/gitstar-ranking/repositories) |`, categories: ['programming'], features: { requireConfig: false, diff --git a/lib/routes/go/jihs/idwr.ts b/lib/routes/go/jihs/idwr.ts index b4ee2d730a34..4b1930739e52 100644 --- a/lib/routes/go/jihs/idwr.ts +++ b/lib/routes/go/jihs/idwr.ts @@ -131,7 +131,6 @@ 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)。 -::: -`, +:::`, }, }; diff --git a/lib/routes/go/mhlw/pdf.ts b/lib/routes/go/mhlw/pdf.ts index 267916b7ae4d..ef999fa702cf 100644 --- a/lib/routes/go/mhlw/pdf.ts +++ b/lib/routes/go/mhlw/pdf.ts @@ -58,11 +58,10 @@ export const route: Route = { example: '/go/mhlw/pdf/stf/seisakunitsuite/bunya/houkokusuunosuii', parameters: { category: 'Category, `stf/seisakunitsuite/bunya/houkokusuunosuii` as 新型コロナウイルス感染症の定点当たり報告数の推移 by default' }, description: `::: tip - Subscribing to this route will give you access to all PDF files on this page. +Subscribing to this route will give you access to all PDF files on this page. - If you subscribe to [新型コロナウイルス感染症の定点当たり報告数の推移](https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/houkokusuunosuii.html),where the URL is \`https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/houkokusuunosuii.html\`, extract the part \`https://www.mhlw.go.jp/\` to the end, which is \`.html\`, and use it as the parameter to fill in. Therefore, the route will be [\`/go/mhlw/stf/seisakunitsuite/bunya/houkokusuunosuii\`](https://rsshub.app/go/mhlw/stf/seisakunitsuite/bunya/houkokusuunosuii). -::: - `, +If you subscribe to [新型コロナウイルス感染症の定点当たり報告数の推移](https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/houkokusuunosuii.html),where the URL is \`https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/houkokusuunosuii.html\`, extract the part \`https://www.mhlw.go.jp/\` to the end, which is \`.html\`, and use it as the parameter to fill in. Therefore, the route will be [\`/go/mhlw/stf/seisakunitsuite/bunya/houkokusuunosuii\`](https://rsshub.app/go/mhlw/stf/seisakunitsuite/bunya/houkokusuunosuii). +:::`, categories: ['government'], features: { diff --git a/lib/routes/gocn/jobs.ts b/lib/routes/gocn/jobs.ts index 46a4cc556d6f..1e2bb99f00a0 100644 --- a/lib/routes/gocn/jobs.ts +++ b/lib/routes/gocn/jobs.ts @@ -46,9 +46,9 @@ async function handler() { })); return { - title: `GoCN社区-招聘`, + title: 'GoCN社区-招聘', link: base_url, - description: `获取GoCN站点招聘`, + description: '获取GoCN站点招聘', item: items, }; } diff --git a/lib/routes/gocn/news.ts b/lib/routes/gocn/news.ts index c84538bd38b3..48cc00fe43da 100644 --- a/lib/routes/gocn/news.ts +++ b/lib/routes/gocn/news.ts @@ -33,9 +33,9 @@ async function handler() { })); return { - title: `GoCN社区-最新动态`, + title: 'GoCN社区-最新动态', link: base_url, - description: `获取GoCN站点最新动态`, + description: '获取GoCN站点最新动态', item: items, }; } diff --git a/lib/routes/gocn/topics.ts b/lib/routes/gocn/topics.ts index 5226d170475b..2cc931f9ae74 100644 --- a/lib/routes/gocn/topics.ts +++ b/lib/routes/gocn/topics.ts @@ -49,9 +49,9 @@ async function handler() { })); return { - title: `GoCN社区-每日新闻`, + title: 'GoCN社区-每日新闻', link: base_url, - description: `获取GoCN站点每日新闻`, + description: '获取GoCN站点每日新闻', item: items, }; } diff --git a/lib/routes/google/citations.ts b/lib/routes/google/citations.ts index f70bbe226b81..494c1cd2ee6d 100644 --- a/lib/routes/google/citations.ts +++ b/lib/routes/google/citations.ts @@ -21,12 +21,12 @@ export const route: Route = { handler, description: `The parameter id in the route is the id in the URL of the user's Google Scholar reference page, for example \`https://scholar.google.com/citations?user=mlmE4JMAAAAJ\` to \`mlmE4JMAAAAJ\`. - Query parameters are also supported here, for example \`https://scholar.google.com/citations?user=mlmE4JMAAAAJ&sortby=pubdate\` to \`mlmE4JMAAAAJ&sortby=pubdate\`. Please make sure that the user id (\`mlmE4JMAAAAJ\` in this case) should be the first parameter in the query string.`, +Query parameters are also supported here, for example \`https://scholar.google.com/citations?user=mlmE4JMAAAAJ&sortby=pubdate\` to \`mlmE4JMAAAAJ&sortby=pubdate\`. Please make sure that the user id (\`mlmE4JMAAAAJ\` in this case) should be the first parameter in the query string.`, }; async function handler(ctx) { const id = ctx.req.param('id'); - const BASE_URL = `https://scholar.google.com`; + const BASE_URL = 'https://scholar.google.com'; const url = `https://scholar.google.com/citations?user=${id}`; const response = await got({ diff --git a/lib/routes/google/fonts.tsx b/lib/routes/google/fonts.tsx index d546321e49e2..0686a038eb39 100644 --- a/lib/routes/google/fonts.tsx +++ b/lib/routes/google/fonts.tsx @@ -40,7 +40,7 @@ export const route: Route = { | date | trending | popularity | alpha | style | ::: warning - This route requires API key, therefore it's only available when self-hosting, refer to the [Deploy Guide](https://docs.rsshub.app/deploy/config#route-specific-configurations) for route-specific configurations. +This route requires API key, therefore it's only available when self-hosting, refer to the [Deploy Guide](https://docs.rsshub.app/deploy/config#route-specific-configurations) for route-specific configurations. :::`, }; diff --git a/lib/routes/google/jules.ts b/lib/routes/google/jules.ts index 835135b52e44..f29605683843 100644 --- a/lib/routes/google/jules.ts +++ b/lib/routes/google/jules.ts @@ -31,12 +31,12 @@ async function handler() { .toArray() .map((el) => { const article = $(el); - const h2 = article.find('h2').first(); + const h2 = article.find('h2'); const id = h2.attr('id') || article.attr('id'); const title = h2.text().trim(); const anchor = id ? `${url}#${id}` : url; - // Date appears as a bold text node after h2 (no
    '; } quote += ''; } @@ -316,11 +318,11 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => { description += ``; } if (authorNameBold) { - description += ``; + description += ''; } description += originalItem.user?.name; if (authorNameBold) { - description += ``; + description += ''; } if (readable) { description += ''; @@ -334,11 +336,11 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => { description += ``; } if (authorNameBold) { - description += ``; + description += ''; } description += item.user?.name; if (authorNameBold) { - description += ``; + description += ''; } if (readable) { description += ''; @@ -358,16 +360,16 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => { description += ``; } if (authorNameBold) { - description += ``; + description += ''; } description += item.user?.name; if (authorNameBold) { - description += ``; + description += ''; } if (readable) { - description += ``; + description += ''; } - description += `: `; + description += ': '; } if (item.in_reply_to_screen_name) { description += showEmojiForRetweetAndReply ? '↩️ ' : showSymbolForRetweetAndReply ? 'Re ' : ''; @@ -384,7 +386,7 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => { if (showTimestampInDescription) { if (readable) { - description += `
    `; + description += '
    '; } description += `${parseDate(item.created_at)}`; } @@ -416,14 +418,15 @@ const ProcessFeed = (ctx, { data = [] }, params = {}) => { }, ], }) || - (item.is_quote_status && { - links: [ - { - url: `https://x.com/${item.quoted_status?.user?.screen_name}/status/${item.quoted_status?.id_str || item.quoted_status?.conversation_id_str}`, - type: 'quote', - }, - ], - }) || + (item.is_quote_status && + item.quoted_status?.user && { + links: [ + { + url: `https://x.com/${item.quoted_status?.user?.screen_name}/status/${item.quoted_status?.id_str || item.quoted_status?.conversation_id_str}`, + type: 'quote', + }, + ], + }) || (item.in_reply_to_screen_name && item.in_reply_to_status_id_str && { links: [ diff --git a/lib/routes/twreporter/newest.ts b/lib/routes/twreporter/newest.ts index eb65efd6e41a..2b362e10af1a 100644 --- a/lib/routes/twreporter/newest.ts +++ b/lib/routes/twreporter/newest.ts @@ -29,8 +29,8 @@ export const route: Route = { }; async function handler() { - const base = `https://www.twreporter.org`; - const url = `https://go-api.twreporter.org/v2/index_page`; + const base = 'https://www.twreporter.org'; + const url = 'https://go-api.twreporter.org/v2/index_page'; const res = await ofetch(url); const list = res.data.latest_section; const out = await Promise.all( @@ -45,7 +45,7 @@ async function handler() { ); return { - title: `報導者 | 最新`, + title: '報導者 | 最新', link: base, item: out, }; diff --git a/lib/routes/txks/news.ts b/lib/routes/txks/news.ts index dae83a9ccdcd..34f8654a523e 100644 --- a/lib/routes/txks/news.ts +++ b/lib/routes/txks/news.ts @@ -101,7 +101,7 @@ export const route: Route = { { title: '全国通信专业技术人员职业水平考试动态', source: ['www.txks.org.cn/index/work', 'www.txks.org.cn'], - target: `/news`, + target: '/news', }, ], example: '/txks/news', diff --git a/lib/routes/typora/changelog-dev.ts b/lib/routes/typora/changelog-dev.ts index 6859758d58ad..d93afcf226ff 100644 --- a/lib/routes/typora/changelog-dev.ts +++ b/lib/routes/typora/changelog-dev.ts @@ -50,7 +50,7 @@ async function handler() { }); return { - title: `Typora Changelog - Dev`, + title: 'Typora Changelog - Dev', link: currentUrl, description: 'Typora Changelog', item: items, diff --git a/lib/routes/typst/namespace.ts b/lib/routes/typst/namespace.ts index a24e78df1329..428a0b8f05eb 100644 --- a/lib/routes/typst/namespace.ts +++ b/lib/routes/typst/namespace.ts @@ -3,9 +3,7 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Typst', url: 'typst.com', - description: ` -Compose papers faster: Focus on your text and let Typst take care of layout and formatting. -`, + description: 'Compose papers faster: Focus on your text and let Typst take care of layout and formatting.', zh: { name: 'Typst', diff --git a/lib/routes/uber/blog.ts b/lib/routes/uber/blog.ts index aea54a2c92c6..a0e78dfc0737 100644 --- a/lib/routes/uber/blog.ts +++ b/lib/routes/uber/blog.ts @@ -31,10 +31,10 @@ export const route: Route = { maintainers: ['hulb'], handler, url: 'www.uber.com/en-HK/blog/engineering', - description: - "The English blog on any of Uber's regional sites (e.g., www.uber.com/en-JP/blog) is the same engineering blog provided by this route, so language selection is not supported. This route is not for the public news blog on specific regional sites (e.g., www.uber.com/ja-JP/blog).", + description: `The English blog on any of Uber's regional sites (e.g., [www.uber.com/en-JP/blog](http://www.uber.com/en-JP/blog)) is the same engineering blog provided by this route, so language selection is not supported. This route is not for the public news blog on specific regional sites (e.g., [www.uber.com/ja-JP/blog](http://www.uber.com/ja-JP/blog)).`, zh: { - description: 'uber的任何区域站点的英文blog(例如www.uber.com/en-JP/blog)都是相同的内容,正是本路由提供的engineering blog,因此本路由不提供语言选择;本路由不是uber在特定区域站点的公开新闻blog(例如www.uber.com/ja-JP/blog)', + description: + 'uber 的任何区域站点的英文 blog(例如 [www.uber.com/en-JP/blog](http://www.uber.com/en-JP/blog))都是相同的内容,正是本路由提供的 engineering blog,因此本路由不提供语言选择;本路由不是 uber 在特定区域站点的公开新闻 blog(例如 [www.uber.com/ja-JP/blog](http://www.uber.com/ja-JP/blog))', }, }; @@ -85,7 +85,7 @@ async function handler() { ); return { - title: `Uber Engineering Blog`, + title: 'Uber Engineering Blog', link: rootURL + '/blog/engineering', description: 'The technology behind Uber Engineering', item: result, diff --git a/lib/routes/uchicago/current.ts b/lib/routes/uchicago/current.ts index 5651e1984dd2..d0f0595f17e7 100644 --- a/lib/routes/uchicago/current.ts +++ b/lib/routes/uchicago/current.ts @@ -4,8 +4,8 @@ import type { Route } from '@/types'; import cache from '@/utils/cache'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; -import { getCookies, setCookies } from '@/utils/puppeteer-utils'; +import playwright from '@/utils/playwright'; +import { getCookies, setCookies } from '@/utils/playwright-utils'; export const route: Route = { path: '/journals/current/:journal', @@ -35,7 +35,7 @@ async function handler(ctx) { const baseUrl = 'https://www.journals.uchicago.edu'; const link = `${baseUrl}/toc/${journal}/current`; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uestc/auto.ts b/lib/routes/uestc/auto.ts index 5db79cbbbec0..2b861f3760f1 100644 --- a/lib/routes/uestc/auto.ts +++ b/lib/routes/uestc/auto.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseIndexUrl = 'https://www.auto.uestc.edu.cn/index/tzgg1.htm'; const host = 'https://www.auto.uestc.edu.cn/'; @@ -32,7 +32,7 @@ export const route: Route = { }; async function handler() { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uestc/bbs.ts b/lib/routes/uestc/bbs.ts index 4020e608db80..b32c48334822 100644 --- a/lib/routes/uestc/bbs.ts +++ b/lib/routes/uestc/bbs.ts @@ -35,13 +35,13 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, - description: ` -::: tip + description: `::: tip 仅支持自建,您需要设置以下配置才能正常使用: -- 河畔cookie: \`UESTC_BBS_COOKIE\` -- Header中的授权字段: \`UESTC_BBS_AUTH_KEY\` -::: -`, + +- 河畔 cookie: \`UESTC_BBS_COOKIE\` +- Header 中的授权字段: \`UESTC_BBS_AUTH_KEY\` + +:::`, radar: [ { source: ['bbs.uestc.edu.cn/*'], diff --git a/lib/routes/uestc/cqe.ts b/lib/routes/uestc/cqe.ts index 650fae4e545c..4ba17e54f545 100644 --- a/lib/routes/uestc/cqe.ts +++ b/lib/routes/uestc/cqe.ts @@ -3,7 +3,7 @@ import { load } from 'cheerio'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseUrl = 'https://cqe.uestc.edu.cn/'; @@ -52,7 +52,7 @@ async function handler(ctx) { throw new InvalidParameterError('type not supported'); } - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uestc/scse.ts b/lib/routes/uestc/scse.ts index 71689707d739..d50a94507b36 100644 --- a/lib/routes/uestc/scse.ts +++ b/lib/routes/uestc/scse.ts @@ -3,7 +3,7 @@ import dayjs from 'dayjs'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseIndexUrl = 'https://www.scse.uestc.edu.cn/index.htm'; const host = 'https://www.scse.uestc.edu.cn/'; @@ -46,7 +46,7 @@ export const route: Route = { }; async function handler() { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uestc/sice.ts b/lib/routes/uestc/sice.ts index 7a401f1357ba..664b0decf091 100644 --- a/lib/routes/uestc/sice.ts +++ b/lib/routes/uestc/sice.ts @@ -3,7 +3,7 @@ import dayjs from 'dayjs'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseIndexUrl = 'https://www.sice.uestc.edu.cn/index.htm'; const host = 'https://www.sice.uestc.edu.cn/'; @@ -33,7 +33,7 @@ export const route: Route = { }; async function handler() { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uestc/sise.ts b/lib/routes/uestc/sise.ts index eede6d8771e2..688db6c63f52 100644 --- a/lib/routes/uestc/sise.ts +++ b/lib/routes/uestc/sise.ts @@ -4,7 +4,7 @@ import dayjs from 'dayjs'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const baseUrl = 'https://sise.uestc.edu.cn/'; @@ -67,7 +67,7 @@ async function handler(ctx) { throw new InvalidParameterError('type not supported'); } - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uibe/hr.ts b/lib/routes/uibe/hr.ts index be06f587a8cc..8ab0c459e4bf 100644 --- a/lib/routes/uibe/hr.ts +++ b/lib/routes/uibe/hr.ts @@ -27,9 +27,9 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 如 [通知公告](http://hr.uibe.edu.cn/tzgg) 的 URL 为 \`http://hr.uibe.edu.cn/tzgg\`,其路由为 [\`/uibe/hr/tzgg\`](https://rsshub.app/uibe/hr/tzgg) +如 [通知公告](http://hr.uibe.edu.cn/tzgg) 的 URL 为 \`http://hr.uibe.edu.cn/tzgg\`,其路由为 [\`/uibe/hr/tzgg\`](https://rsshub.app/uibe/hr/tzgg) - 如 [教师招聘](http://hr.uibe.edu.cn/jszp) 中的 [招聘信息](http://hr.uibe.edu.cn/jszp/zpxx) 的 URL 为 \`http://hr.uibe.edu.cn/jszp/zpxx\`,其路由为 [\`/uibe/hr/jszp/zpxx\`](https://rsshub.app/uibe/jszp/zpxx) +如 [教师招聘](http://hr.uibe.edu.cn/jszp) 中的 [招聘信息](http://hr.uibe.edu.cn/jszp/zpxx) 的 URL 为 \`http://hr.uibe.edu.cn/jszp/zpxx\`,其路由为 [\`/uibe/hr/jszp/zpxx\`](https://rsshub.app/uibe/jszp/zpxx) :::`, }; diff --git a/lib/routes/ulapia/index.ts b/lib/routes/ulapia/index.ts index 3d472eaa6bae..96302d7b80e0 100644 --- a/lib/routes/ulapia/index.ts +++ b/lib/routes/ulapia/index.ts @@ -42,7 +42,7 @@ export const route: Route = { handler, description: `| 个股研报 | 行业研报 | 策略研报 | 宏观研报 | 新股研报 | 券商晨报(今日晨报) | | :-------------: | :----------------: | :----------------: | :-------------: | :-----------: | :------------------: | -| stock_research | industry_research | strategy_research | macro_research | ipo_research | brokerage_news |`, +| stock\\_research | industry\\_research | strategy\\_research | macro\\_research | ipo\\_research | brokerage\\_news |`, }; async function handler(ctx) { diff --git a/lib/routes/upc/jsj.ts b/lib/routes/upc/jsj.ts index 1dc8d9461b71..75319585802b 100644 --- a/lib/routes/upc/jsj.ts +++ b/lib/routes/upc/jsj.ts @@ -92,9 +92,9 @@ async function handler(ctx) { ); return { - title: HEAD[type] + `-计算机科学与技术学院`, + title: HEAD[type] + '-计算机科学与技术学院', link, - description: HEAD[type] + `-计算机科学与技术学院`, + description: HEAD[type] + '-计算机科学与技术学院', item: out, }; } diff --git a/lib/routes/upc/jwc.ts b/lib/routes/upc/jwc.ts index a2b21ccae42c..b0c67c28dfc6 100644 --- a/lib/routes/upc/jwc.ts +++ b/lib/routes/upc/jwc.ts @@ -100,9 +100,9 @@ export const route: Route = { ], name: '教务处', maintainers: ['sddzhyc'], - description: `| 所有通知 | 教学·运行 | 学业·学籍 | 教学·研究 | 课程·教材 | 实践·教学 | 创新·创业 | 语言·文字 | 继续·教育 | 本科·招生 | -| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -| tzgg | 18519 | 18520 | 18521 | 18522 | 18523 | 18524 | yywwz | jxwjy | bkwzs |`, + description: `| 所有通知 | 教学・运行 | 学业・学籍 | 教学・研究 | 课程・教材 | 实践・教学 | 创新・创业 | 语言・文字 | 继续・教育 | 本科・招生 | +| -------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | +| tzgg | 18519 | 18520 | 18521 | 18522 | 18523 | 18524 | yywwz | jxwjy | bkwzs |`, url: 'jwc.upc.edu.cn/tzgg/list.htm', handler, }; diff --git a/lib/routes/upc/main.ts b/lib/routes/upc/main.ts index ea41372a8013..e19ebeb77155 100644 --- a/lib/routes/upc/main.ts +++ b/lib/routes/upc/main.ts @@ -88,9 +88,9 @@ async function handler(ctx) { ); return { - title: HEAD[type] + `-中国石油大学(华东)`, + title: HEAD[type] + '-中国石油大学(华东)', link, - description: HEAD[type] + `-中国石油大学(华东)`, + description: HEAD[type] + '-中国石油大学(华东)', item: out, }; } diff --git a/lib/routes/ups/track.ts b/lib/routes/ups/track.ts index f1069ca75177..5421c6d30dde 100644 --- a/lib/routes/ups/track.ts +++ b/lib/routes/ups/track.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { DataItem, Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/track/:trackingNumber', @@ -26,7 +26,7 @@ async function handler(ctx) { const { trackingNumber } = ctx.req.param(); const url = `https://www.ups.com/track?loc=en_US&tracknum=${trackingNumber}`; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); diff --git a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts index aa7cad586a1c..799a3b8ad8c4 100644 --- a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts +++ b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts @@ -4,7 +4,7 @@ import { config } from '@/config'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/:id', @@ -38,7 +38,7 @@ async function handler(ctx) { const response = await cache.tryGet( link, async () => { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/uraaka-joshi/uraaka-joshi.ts b/lib/routes/uraaka-joshi/uraaka-joshi.ts index 1aef1cf8d76b..e2e2df4bd246 100644 --- a/lib/routes/uraaka-joshi/uraaka-joshi.ts +++ b/lib/routes/uraaka-joshi/uraaka-joshi.ts @@ -2,7 +2,7 @@ import { load } from 'cheerio'; import type { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; export const route: Route = { path: '/', @@ -22,10 +22,10 @@ export const route: Route = { }; async function handler() { - const link = `https://www.uraaka-joshi.com/`; - const title = `裏垢女子まとめ`; + const link = 'https://www.uraaka-joshi.com/'; + const title = '裏垢女子まとめ'; - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); diff --git a/lib/routes/usenix/usenix.ts b/lib/routes/usenix/usenix.ts index 8eff3387f781..e994b8a4b9d5 100644 --- a/lib/routes/usenix/usenix.ts +++ b/lib/routes/usenix/usenix.ts @@ -22,7 +22,7 @@ export const route: Route = { maintainers: ['ZeddYu'], handler, url: 'usenix.org/conferences/all', - description: `Return results from 2020`, + description: 'Return results from 2020', }; async function handler() { diff --git a/lib/routes/ustc/scms.ts b/lib/routes/ustc/scms.ts index da9bfef35cb4..6cbe52800a77 100644 --- a/lib/routes/ustc/scms.ts +++ b/lib/routes/ustc/scms.ts @@ -39,9 +39,9 @@ export const route: Route = { maintainers: ['boxie123'], handler, url: 'scms.ustc.edu.cn/', - description: `| 院内新闻 | 通知公告 | 科研动态 | 学术活动 | 其他 | -| -------- | -------- | -------- | -------- | -------- | -| ynxw | tzgg | kydt | xshd | 自定义id |`, + description: `| 院内新闻 | 通知公告 | 科研动态 | 学术活动 | 其他 | +| -------- | -------- | -------- | -------- | --------- | +| ynxw | tzgg | kydt | xshd | 自定义 id |`, }; async function handler(ctx) { diff --git a/lib/routes/utgd/topic.ts b/lib/routes/utgd/topic.ts index 50e43b34292c..998cea3ad1b8 100644 --- a/lib/routes/utgd/topic.ts +++ b/lib/routes/utgd/topic.ts @@ -30,7 +30,7 @@ export const route: Route = { description: `| 在线阅读专栏 | 卡片笔记专题 | | ------------ | ------------ | - 更多专栏请见 [专题广场](https://utgd.net/topic)`, +更多专栏请见 [专题广场](https://utgd.net/topic)`, }; async function handler(ctx) { diff --git a/lib/routes/v2ex/xna.ts b/lib/routes/v2ex/xna.ts index b37a94e17ddd..befd869aaa8b 100644 --- a/lib/routes/v2ex/xna.ts +++ b/lib/routes/v2ex/xna.ts @@ -52,9 +52,9 @@ async function handler(ctx) { }); return { - title: `V2EX-xna`, + title: 'V2EX-xna', link: pageUrl, - description: `V2EX-xna`, + description: 'V2EX-xna', item: items, }; } diff --git a/lib/routes/vimeo/category.ts b/lib/routes/vimeo/category.ts index f590fdab456e..7d19c8bdf157 100644 --- a/lib/routes/vimeo/category.ts +++ b/lib/routes/vimeo/category.ts @@ -79,7 +79,7 @@ async function handler(ctx) { item: vimeojs.map((item) => ({ title: item.name, description: renderDescription({ - videoUrl: item.uri.replace(`/videos`, ''), + videoUrl: item.uri.replace('/videos', ''), vdescription: item.description || '', }), pubDate: parseDate(item.created_time), diff --git a/lib/routes/vimeo/channel.ts b/lib/routes/vimeo/channel.ts index eb34fd65bbd1..8fd2baf825a1 100644 --- a/lib/routes/vimeo/channel.ts +++ b/lib/routes/vimeo/channel.ts @@ -42,7 +42,7 @@ async function handler(ctx) { }, }); const page2 = - channel === `bestoftheyear` + channel === 'bestoftheyear' ? await got({ method: 'get', url: `${url}/page:2/sort:date/format:detail`, diff --git a/lib/routes/vimeo/usr-videos.ts b/lib/routes/vimeo/usr-videos.ts index 00bb7db86906..f413d26ffc2a 100644 --- a/lib/routes/vimeo/usr-videos.ts +++ b/lib/routes/vimeo/usr-videos.ts @@ -26,7 +26,7 @@ export const route: Route = { maintainers: ['MisteryMonster'], handler, description: `::: tip Special category name attention - Some of the categories contain slash like \`3D/CG\` , must change the slash \`/\` to the vertical bar\`|\`. +Some of the categories contain slash like \`3D/CG\` , must change the slash \`/\` to the vertical bar\`|\`. :::`, }; diff --git a/lib/routes/visionias/daily-news-summary.ts b/lib/routes/visionias/daily-news-summary.ts index c4b1eed8afa6..6f749e25d833 100644 --- a/lib/routes/visionias/daily-news-summary.ts +++ b/lib/routes/visionias/daily-news-summary.ts @@ -40,15 +40,15 @@ async function handler(): Promise { language: 'en', item: items, image: `${baseUrl}/current-affairs/images/news-today-logo.svg`, - icon: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, - logo: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, + icon: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', + logo: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', allowEmpty: true, }; } function processNews(page) { const $ = load(page); - const items = $(`#quiz-start div[x-data="{ isExpanded: false }"]`) + const items = $('#quiz-start div[x-data="{ isExpanded: false }"]') .toArray() .map((item) => { const title = $(item).find('a>h5').text().trim(); diff --git a/lib/routes/visionias/monthly-magazine.ts b/lib/routes/visionias/monthly-magazine.ts index e8bbef7ae6a2..25b6e3d05d95 100644 --- a/lib/routes/visionias/monthly-magazine.ts +++ b/lib/routes/visionias/monthly-magazine.ts @@ -40,15 +40,15 @@ async function handler(): Promise { language: 'en', item: items, image: `${baseUrl}/current-affairs/images/news-today-logo.svg`, - icon: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, - logo: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, + icon: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', + logo: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', allowEmpty: true, }; } async function processNews(page) { const $ = load(page); - const divItems = $(`#monthly-table-of-content>div`).toArray(); + const divItems = $('#monthly-table-of-content>div').toArray(); const linkItems = divItems.flatMap((item) => { const maintitle = $(item).find('button>div:nth-child(2) div.text-left').text().trim(); return $(item) diff --git a/lib/routes/visionias/news-today.ts b/lib/routes/visionias/news-today.ts index 5c8d67bee89a..41a3f667cc9e 100644 --- a/lib/routes/visionias/news-today.ts +++ b/lib/routes/visionias/news-today.ts @@ -66,8 +66,8 @@ async function handler(ctx): Promise { language: 'en', item: items, image: `${baseUrl}/current-affairs/images/news-today-logo.svg`, - icon: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, - logo: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, + icon: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', + logo: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', allowEmpty: true, }; } @@ -75,7 +75,7 @@ async function handler(ctx): Promise { async function processCurrentNews(currentUrl) { const response = await ofetch(`${baseUrl}${currentUrl}`); const $ = load(response); - const items = $(`#table-of-content > ul > li > a`) + const items = $('#table-of-content > ul > li > a') .toArray() .map((item) => { const link = $(item).attr('href'); diff --git a/lib/routes/visionias/weekly-focus.ts b/lib/routes/visionias/weekly-focus.ts index bcf73b69f035..fe0ee3148171 100644 --- a/lib/routes/visionias/weekly-focus.ts +++ b/lib/routes/visionias/weekly-focus.ts @@ -51,8 +51,8 @@ async function handler(ctx): Promise { language: 'en', item: itemsPromise.map((item) => (item.status === 'fulfilled' ? item.value : { title: 'Error Parse News' })), image: `${baseUrl}/current-affairs/images/weekly-focus-logo.svg`, - icon: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, - logo: `https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png`, + icon: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', + logo: 'https://cdn.visionias.in/new-system-assets/images/home_page/home/vision-logo-footer.png', allowEmpty: true, }; } diff --git a/lib/routes/wabei/namespace.ts b/lib/routes/wabei/namespace.ts index 0c80de411b92..90836c14fe8b 100644 --- a/lib/routes/wabei/namespace.ts +++ b/lib/routes/wabei/namespace.ts @@ -3,6 +3,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '挖贝网', url: 'www.wabei.cn', - description: '挖贝网专注于新三板、A股和港股报道', + description: '挖贝网专注于新三板、A 股和港股报道', lang: 'zh-CN', }; diff --git a/lib/routes/wainao/topics.tsx b/lib/routes/wainao/topics.tsx index 1d935b9f3ad3..14f2260687ae 100644 --- a/lib/routes/wainao/topics.tsx +++ b/lib/routes/wainao/topics.tsx @@ -158,8 +158,7 @@ export const route: Route = { | [热点](https://www.wainao.me/topics/hotspot) | [人物](https://www.wainao.me/topics/people) | [身份](https://www.wainao.me/topics/identity) | [政治](https://www.wainao.me/topics/politics) | [社会](https://www.wainao.me/topics/society) | [文化](https://www.wainao.me/topics/culture) | [经济](https://www.wainao.me/topics/economics) | [环境](https://www.wainao.me/topics/environment) | [FUN](https://www.wainao.me/topics/fun) | | --------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------- | -| [hotspot](https://rsshub.app/wainao/topics/hotspot) | [people](https://rsshub.app/wainao/topics/people) | [identity](https://rsshub.app/wainao/topics/identity) | [politics](https://rsshub.app/wainao/topics/politics) | [society](https://rsshub.app/wainao/topics/society) | [culture](https://rsshub.app/wainao/topics/culture) | [economics](https://rsshub.app/wainao/topics/economics) | [environment](https://rsshub.app/wainao/topics/environment) | [fun](https://rsshub.app/wainao/topics/fun) | -`, +| [hotspot](https://rsshub.app/wainao/topics/hotspot) | [people](https://rsshub.app/wainao/topics/people) | [identity](https://rsshub.app/wainao/topics/identity) | [politics](https://rsshub.app/wainao/topics/politics) | [society](https://rsshub.app/wainao/topics/society) | [culture](https://rsshub.app/wainao/topics/culture) | [economics](https://rsshub.app/wainao/topics/economics) | [environment](https://rsshub.app/wainao/topics/environment) | [fun](https://rsshub.app/wainao/topics/fun) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/wallhaven/index.ts b/lib/routes/wallhaven/index.ts index d20d0e17040c..54d399d44280 100644 --- a/lib/routes/wallhaven/index.ts +++ b/lib/routes/wallhaven/index.ts @@ -30,9 +30,9 @@ export const route: Route = { handler, url: 'wallhaven.cc/', description: `::: tip - Subscribe pages starting with \`https://wallhaven.cc/search\`, fill the text after \`?\` as \`filter\` in the route. The following is an example: +Subscribe pages starting with \`https://wallhaven.cc/search\`, fill the text after \`?\` as \`filter\` in the route. The following is an example: - The text after \`?\` is \`q=id%3A711&sorting=random&ref=fp&seed=8g0dgd\` for [Wallpaper Search: #landscape - wallhaven.cc](https://wallhaven.cc/search?q=id%3A711&sorting=random&ref=fp&seed=8g0dgd), so the route is [/wallhaven/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd](https://rsshub.app/wallhaven/q=id%3A711&sorting=random&ref=fp&seed=8g0dgd) +The text after \`?\` is \`q=id%3A711&sorting=random&ref=fp&seed=8g0dgd\` for [Wallpaper Search: #landscape - wallhaven.cc](https://wallhaven.cc/search?q=id%3A711\\&sorting=random\\&ref=fp\\&seed=8g0dgd), so the route is [/wallhaven/q=id%3A711\\&sorting=random\\&ref=fp\\&seed=8g0dgd](https://rsshub.app/wallhaven/q=id%3A711\\&sorting=random\\&ref=fp\\&seed=8g0dgd) :::`, }; diff --git a/lib/routes/warthunder/news.tsx b/lib/routes/warthunder/news.tsx index 3a452a9c51d3..0fee47ed837b 100644 --- a/lib/routes/warthunder/news.tsx +++ b/lib/routes/warthunder/news.tsx @@ -38,8 +38,8 @@ export const route: Route = { maintainers: ['axojhf'], handler, url: 'warthunder.com/en/news', - description: `News data from [https://warthunder.com/en/news/](https://warthunder.com/en/news/) - The \`pubDate\` provided under UTC time zone, so please ignore the specific time!!!`, + description: `News data from +The \`pubDate\` provided under UTC time zone, so please ignore the specific time!!!`, }; async function handler() { diff --git a/lib/routes/washingtonpost/app.tsx b/lib/routes/washingtonpost/app.tsx index 8de1bc06e267..b4b49fced403 100644 --- a/lib/routes/washingtonpost/app.tsx +++ b/lib/routes/washingtonpost/app.tsx @@ -35,7 +35,7 @@ export const route: Route = { ], handler, description: `::: tip -For example, the category for https://www.washingtonpost.com/national/investigations would be /national/investigations. +For example, the category for would be /national/investigations. :::`, }; diff --git a/lib/routes/wbv-gpa/index.ts b/lib/routes/wbv-gpa/index.ts index d2164c0f21fd..997f592a0b57 100644 --- a/lib/routes/wbv-gpa/index.ts +++ b/lib/routes/wbv-gpa/index.ts @@ -14,10 +14,8 @@ export const route: Route = { path: '/:category?/:state?', maintainers: ['sk22'], categories: ['other'], - description: ` -Search housing by WBV-GPA, see "Angebote" menu item in https://www.wbv-gpa.at. -Filtering by state is done client-side. -`, + description: `Search housing by WBV-GPA, see "Angebote" menu item in . +Filtering by state is done client-side.`, parameters: { category: 'Anything behind `/angebote/` in the URL. Default: `wohnungen`', state: 'Optionally filter by Austrian state (`wien`, `steiermark`, ...)', diff --git a/lib/routes/wdfxw/bookfree.tsx b/lib/routes/wdfxw/bookfree.tsx index 417baad9108c..d13979759bfd 100644 --- a/lib/routes/wdfxw/bookfree.tsx +++ b/lib/routes/wdfxw/bookfree.tsx @@ -323,13 +323,12 @@ export const route: Route = { | [标准汇编](https://www.wdfxw.net/bookfree-00029.html) | [00029](https://rsshub.app/wdfxw/bookfree/00029) | | [其他](https://www.wdfxw.net/bookfree-00030.html) | [00030](https://rsshub.app/wdfxw/bookfree/00030) | | [职业资格考试](https://www.wdfxw.net/bookfree-00031.html) | [00031](https://rsshub.app/wdfxw/bookfree/00031) | -| [股票证券行业研究报告(研报)](https://www.wdfxw.net/bookfree-00032.html) | [00032](https://rsshub.app/wdfxw/bookfree/00032) | +| [股票证券行业研究报告 (研报)](https://www.wdfxw.net/bookfree-00032.html) | [00032](https://rsshub.app/wdfxw/bookfree/00032) | | [基金申请](https://www.wdfxw.net/bookfree-00033.html) | [00033](https://rsshub.app/wdfxw/bookfree/00033) | | [教师资格证考试资料](https://www.wdfxw.net/bookfree-00034.html) | [00034](https://rsshub.app/wdfxw/bookfree/00034) | | [专利说明书](https://www.wdfxw.net/bookfree-00035.html) | [00035](https://rsshub.app/wdfxw/bookfree/00035) | - -`, +`, categories: ['reading'], features: { requireConfig: false, diff --git a/lib/routes/web/series.ts b/lib/routes/web/series.ts index f6fa1f3efaea..e8d9e11ee31b 100644 --- a/lib/routes/web/series.ts +++ b/lib/routes/web/series.ts @@ -17,7 +17,7 @@ export const route: Route = { maintainers: ['KarasuShin'], handler, description: `::: tip - The \`seriesName\` can be extracted from the Series page URL: \`https://web.dev/series/:seriesName\` +The \`seriesName\` can be extracted from the Series page URL: \`https://web.dev/series/:seriesName\` :::`, }; diff --git a/lib/routes/wechat/mp.ts b/lib/routes/wechat/mp.ts index d080dc708873..7071c10a3fec 100755 --- a/lib/routes/wechat/mp.ts +++ b/lib/routes/wechat/mp.ts @@ -23,7 +23,7 @@ export const route: Route = { handler, description: `只适用拥有首页模板 (分享链接带有 homepage) 的公众号。例如从公众号分享出来的链接为 \`https://mp.weixin.qq.com/mp/homepage?__biz=MzA3MDM3NjE5NQ==&hid=4\`,\`biz\` 为 \`MzA3MDM3NjE5NQ==\`,\`hid\` 为 \`4\`。 - 有些页面里会有分栏, \`cid\` 可以通过元素选择器选中栏目查看\`data-index\`。如[链接](https://mp.weixin.qq.com/mp/homepage?__biz=MzA3MDM3NjE5NQ==&hid=4)里的 \`京都职人\` 栏目的 \`cid\` 为 \`0\`,\`文艺时光\` 栏目的 \`cid\` 为 \`2\`。如果不清楚的话最左边的栏目为\`0\`,其右方栏目依次递增 \`1\`。`, +有些页面里会有分栏, \`cid\` 可以通过元素选择器选中栏目查看\`data-index\`。如[链接](https://mp.weixin.qq.com/mp/homepage?__biz=MzA3MDM3NjE5NQ==\\&hid=4)里的 \`京都职人\` 栏目的 \`cid\` 为 \`0\`,\`文艺时光\` 栏目的 \`cid\` 为 \`2\`。如果不清楚的话最左边的栏目为\`0\`,其右方栏目依次递增 \`1\`。`, }; async function handler(ctx) { @@ -48,7 +48,7 @@ async function handler(ctx) { const list = JSONresponse.data.appmsg_list; const $ = load(HTMLresponse.data); // 标题,另外差一个菜单标题!求助 - const mptitle = $('div.articles_header').find('a').text() + `|` + $('div.articles_header > h2.rich_media_title').text(); + const mptitle = $('div.articles_header').find('a').text() + '|' + $('div.articles_header > h2.rich_media_title').text(); const articledata = await Promise.all( list.map((item) => { const single = { diff --git a/lib/routes/wechat/msgalbum.ts b/lib/routes/wechat/msgalbum.ts index 327057356930..3f6d8d7f659d 100644 --- a/lib/routes/wechat/msgalbum.ts +++ b/lib/routes/wechat/msgalbum.ts @@ -21,7 +21,8 @@ export const route: Route = { name: '公众号文章话题 Tag', maintainers: ['MisteryMonster'], handler, - description: `一些公众号(如看理想)会在微信文章里添加 Tag ,点入 Tag 的链接如 \`https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzA3MDM3NjE5NQ==&action=getalbum&album_id=1375870284640911361\`,其中\`biz\` 为 \`MzA3MDM3NjE5NQ==\`,\`aid\` 为 \`1375870284640911361\`。`, + description: + '一些公众号(如看理想)会在微信文章里添加 Tag ,点入 Tag 的链接如 `https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzA3MDM3NjE5NQ==&action=getalbum&album_id=1375870284640911361`,其中`biz` 为 `MzA3MDM3NjE5NQ==`,`aid` 为 `1375870284640911361`。', }; async function handler(ctx) { @@ -34,7 +35,7 @@ async function handler(ctx) { }); const $ = load(HTMLresponse.data); const list = $('li').toArray(); - const mptitle = $('.album__author-name').text() + `|` + $('.album__label-title').text(); + const mptitle = $('.album__author-name').text() + '|' + $('.album__label-title').text(); const articledata = await Promise.all( list.map((item) => { const link = $(item).attr('data-link').replace('http://', 'https://'); diff --git a/lib/routes/wechat/tgchannel.ts b/lib/routes/wechat/tgchannel.ts index b7aa7399d5b7..4b2274cb2d21 100644 --- a/lib/routes/wechat/tgchannel.ts +++ b/lib/routes/wechat/tgchannel.ts @@ -27,11 +27,11 @@ export const route: Route = { | \`2\` | #公众号全名 | 已启用 efb-patch-middleware | ::: tip - 启用搜索有助于在订阅了过多公众号的频道里有效筛选,不易因为大量公众号同时推送导致一些公众号消息被遗漏,但必须正确选择搜索查询类型,否则会搜索失败。 +启用搜索有助于在订阅了过多公众号的频道里有效筛选,不易因为大量公众号同时推送导致一些公众号消息被遗漏,但必须正确选择搜索查询类型,否则会搜索失败。 ::: ::: warning - 该方法需要通过 efb 进行频道绑定,具体操作见 [https://github.com/DIYgod/RSSHub/issues/2172](https://github.com/DIYgod/RSSHub/issues/2172) +该方法需要通过 efb 进行频道绑定,具体操作见 :::`, }; diff --git a/lib/routes/weibo/friends.ts b/lib/routes/weibo/friends.ts index dbc04bf3780d..517bb510e5c8 100644 --- a/lib/routes/weibo/friends.ts +++ b/lib/routes/weibo/friends.ts @@ -39,11 +39,11 @@ export const route: Route = { handler, url: 'weibo.com/', description: `::: warning - 此方案必须使用用户\`Cookie\`进行抓取 +此方案必须使用用户\`Cookie\`进行抓取 - 因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 +因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 - 微博用户 Cookie 的配置可参照部署文档 +微博用户 Cookie 的配置可参照部署文档 :::`, }; @@ -67,13 +67,13 @@ async function handler(ctx) { } const uid = await cache.tryGet( - `weibo:friends:login-user`, + 'weibo:friends:login-user', async () => { const _r = await got({ method: 'get', url: 'https://m.weibo.cn/api/config', headers: { - Referer: `https://m.weibo.cn/`, + Referer: 'https://m.weibo.cn/', Cookie: config.weibo.cookies, ...weiboUtils.apiHeaders, }, @@ -112,7 +112,7 @@ async function handler(ctx) { method: 'get', url: 'https://m.weibo.cn/feed/friends', headers: { - Referer: `https://m.weibo.cn/`, + Referer: 'https://m.weibo.cn/', Cookie: config.weibo.cookies, ...weiboUtils.apiHeaders, }, @@ -156,7 +156,7 @@ async function handler(ctx) { return weiboUtils.sinaimgTvax({ title, - link: `https://weibo.com`, + link: 'https://weibo.com', item: resultItems, }); } diff --git a/lib/routes/weibo/group.ts b/lib/routes/weibo/group.ts index 80f6573735a8..86f06863d7b2 100644 --- a/lib/routes/weibo/group.ts +++ b/lib/routes/weibo/group.ts @@ -32,11 +32,11 @@ export const route: Route = { maintainers: ['monologconnor', 'Rongronggg9'], handler, description: `::: warning - 由于微博官方未提供自定义分组相关 api, 此方案必须使用用户\`Cookie\`进行抓取 +由于微博官方未提供自定义分组相关 api, 此方案必须使用用户\`Cookie\`进行抓取 - 因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 +因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 - 微博用户 Cookie 的配置可参照部署文档 +微博用户 Cookie 的配置可参照部署文档 :::`, }; @@ -68,7 +68,7 @@ async function handler(ctx) { method: 'get', url: `https://m.weibo.cn/feed/group?gid=${gid}`, headers: { - Referer: `https://m.weibo.cn/`, + Referer: 'https://m.weibo.cn/', Cookie: config.weibo.cookies, ...weiboUtils.apiHeaders, }, diff --git a/lib/routes/weibo/namespace.ts b/lib/routes/weibo/namespace.ts index c0557360392c..c74735f839d8 100644 --- a/lib/routes/weibo/namespace.ts +++ b/lib/routes/weibo/namespace.ts @@ -4,8 +4,7 @@ export const namespace: Namespace = { name: '微博', url: 'weibo.com', description: `::: warning -微博会针对请求的来源地区返回不同的结果。\ -一个已知的例子为:部分视频因未知原因仅限中国大陆境内访问 (CDN 域名为 \`locallimit.us.sinaimg.cn\` 而非 \`f.video.weibocdn.com\`)。若一条微博含有这种视频且 RSSHub 实例部署在境外,抓取到的微博可能不含视频。将 RSSHub 部署在境内有助于抓取这种视频,但阅读器也必须处于境内网络环境以加载视频。 +微博会针对请求的来源地区返回不同的结果。一个已知的例子为:部分视频因未知原因仅限中国大陆境内访问 (CDN 域名为 \`locallimit.us.sinaimg.cn\` 而非 \`f.video.weibocdn.com\`)。若一条微博含有这种视频且 RSSHub 实例部署在境外,抓取到的微博可能不含视频。将 RSSHub 部署在境内有助于抓取这种视频,但阅读器也必须处于境内网络环境以加载视频。 ::: ::: warning @@ -34,12 +33,12 @@ export const namespace: Namespace = { | showEmojiInDescription | 是否展示正文和评论中的微博表情,关闭则替换为 \`[表情名]\` | 0/1/true/false | true | | showLinkIconInDescription | 是否展示正文和评论中的链接图标 | 0/1/true/false | true | | preferMobileLink | 是否使用移动版链接(默认使用 PC 版) | 0/1/true/false | false | -| showRetweeted | 是否显示转发的微博 | 0/1/true/false | true | -| showBloggerIcons | 是否显示评论中博主的标志,只在显示热门评论时有效 | 0/1/true/false | false | +| showRetweeted | 是否显示转发的微博 | 0/1/true/false | true | +| showBloggerIcons | 是否显示评论中博主的标志,只在显示热门评论时有效 | 0/1/true/false | false | 指定更多与默认值不同的参数选项可以改善 RSS 的可读性,如 -[https://rsshub.app/weibo/user/1642909335/readable=1&authorNameBold=1&showAuthorInTitle=1&showAuthorInDesc=1&showAuthorAvatarInDesc=1&showEmojiForRetweet=1&showRetweetTextInTitle=0&addLinkForPics=1&showTimestampInDescription=1&showTimestampInDescription=1&heightOfPics=150](https://rsshub.app/weibo/user/1642909335/readable=1&authorNameBold=1&showAuthorInTitle=1&showAuthorInDesc=1&showAuthorAvatarInDesc=1&showEmojiForRetweet=1&showRetweetTextInTitle=0&addLinkForPics=1&showTimestampInDescription=1&showTimestampInDescription=1&heightOfPics=150) + 的效果为 diff --git a/lib/routes/weibo/super-index.ts b/lib/routes/weibo/super-index.ts index a0f36e4e90a6..f0e906c9dced 100644 --- a/lib/routes/weibo/super-index.ts +++ b/lib/routes/weibo/super-index.ts @@ -40,8 +40,8 @@ export const route: Route = { | soul | 精华 | | video | 视频(暂不支持) | | album | 相册(暂不支持) | -| hot_sort | 热门 | -| sort_time | 最新帖子 | +| hot\\_sort | 热门 | +| sort\\_time | 最新帖子 | | feed | 最新评论 |`, }; diff --git a/lib/routes/weibo/timeline.ts b/lib/routes/weibo/timeline.ts index 6ab932141020..f8ece17c893e 100644 --- a/lib/routes/weibo/timeline.ts +++ b/lib/routes/weibo/timeline.ts @@ -35,9 +35,9 @@ export const route: Route = { maintainers: ['zytomorrow', 'DIYgod', 'Rongronggg9'], handler, description: `::: warning - 需要对应用户打开页面进行授权生成 token 才能生成内容 +需要对应用户打开页面进行授权生成 token 才能生成内容 - 自部署需要申请并配置微博 key,具体见部署文档 +自部署需要申请并配置微博 key,具体见部署文档 :::`, }; diff --git a/lib/routes/weibo/user-bookmarks.ts b/lib/routes/weibo/user-bookmarks.ts index 4369072805b1..9847983e963c 100644 --- a/lib/routes/weibo/user-bookmarks.ts +++ b/lib/routes/weibo/user-bookmarks.ts @@ -41,11 +41,11 @@ export const route: Route = { handler, url: 'weibo.com/', description: `::: warning - 此方案必须使用用户\`Cookie\`进行抓取,只可以获取本人的收藏动态 +此方案必须使用用户\`Cookie\`进行抓取,只可以获取本人的收藏动态 - 因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 +因微博 cookies 的过期与更新方案未经验证,部署一次 Cookie 的有效时长未知 - 微博用户 Cookie 的配置可参照部署文档 +微博用户 Cookie 的配置可参照部署文档 :::`, }; diff --git a/lib/routes/weibo/user.ts b/lib/routes/weibo/user.ts index 2c8b8c6db927..7241a03cb91a 100644 --- a/lib/routes/weibo/user.ts +++ b/lib/routes/weibo/user.ts @@ -49,7 +49,7 @@ export const route: Route = { maintainers: ['DIYgod', 'iplusx', 'Rongronggg9', 'Konano'], handler, description: `::: warning - 部分博主仅登录可见,未提供 Cookie 的情况下不支持订阅,可以通过打开 \`https://m.weibo.cn/u/:uid\` 验证 +部分博主仅登录可见,未提供 Cookie 的情况下不支持订阅,可以通过打开 \`https://m.weibo.cn/u/:uid\` 验证 :::`, }; diff --git a/lib/routes/weibo/utils.ts b/lib/routes/weibo/utils.ts index f33e283157bb..c4df2ea2fd19 100644 --- a/lib/routes/weibo/utils.ts +++ b/lib/routes/weibo/utils.ts @@ -6,8 +6,8 @@ import { config } from '@/config'; import cache from '@/utils/cache'; import got from '@/utils/got'; import logger from '@/utils/logger'; -import { getPuppeteerPage } from '@/utils/puppeteer'; -import { getCookies } from '@/utils/puppeteer-utils'; +import { getPlaywrightPage } from '@/utils/playwright'; +import { getCookies } from '@/utils/playwright-utils'; import { fallback, queryToBoolean, queryToInteger } from '@/utils/readable-social'; class RenewWeiboCookiesError extends Error { @@ -80,7 +80,7 @@ const weiboUtils = { logger.info(`Fetching visitor Cookies from ${url}`); } let times = 0; - const { page, destroy } = await getPuppeteerPage(url, { + const { page, destroy } = await getPlaywrightPage(url, { onBeforeLoad: async (page) => { const expectResourceTypes = new Set(['document', 'script', 'xhr', 'fetch']); await page.setUserAgent(weiboUtils.apiHeaders['User-Agent']); @@ -312,8 +312,8 @@ const weiboUtils = { // 处理转发的微博 if (status.retweeted_status) { html += readable - ? `
    ` - : `
    - 转发 `; + ? '
    ' + : '
    - 转发 '; if (!status.retweeted_status.user) { // 当转发的微博被删除时user为null status.retweeted_status.user = { @@ -335,10 +335,10 @@ const weiboUtils = { html += `
    原博:https://weibo.com/${status.retweeted_status.user.id}/${status.retweeted_status.bid}`; } if (showTimestampInDescription) { - html += `
    ` + new Date(status.retweeted_status.created_at).toLocaleString() + ``; + html += '
    ' + new Date(status.retweeted_status.created_at).toLocaleString() + ''; } if (readable) { - html += `
    `; + html += '
    '; } html += '
    '; @@ -548,7 +548,7 @@ const weiboUtils = { }); if (response.data && response.data.data) { const comments = response.data.data; - itemDesc += `
    `; + itemDesc += '
    '; itemDesc += '

    热门评论

    '; for (const comment of comments) { itemDesc += '

    '; diff --git a/lib/routes/wfdf/namespace.ts b/lib/routes/wfdf/namespace.ts index 9c320595c0f0..6192cd95da35 100644 --- a/lib/routes/wfdf/namespace.ts +++ b/lib/routes/wfdf/namespace.ts @@ -3,5 +3,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'WFDF', url: 'wfdf.sport', + categories: ['sport'], lang: 'en', }; diff --git a/lib/routes/wfdf/news.ts b/lib/routes/wfdf/news.ts index c9ea5616f0e7..eb0bbcb064bb 100644 --- a/lib/routes/wfdf/news.ts +++ b/lib/routes/wfdf/news.ts @@ -4,7 +4,7 @@ import { parseDate } from '@/utils/parse-date'; export const route: Route = { path: '/news', - categories: ['other'], + categories: ['sport'], example: '/wfdf/news', parameters: {}, features: { diff --git a/lib/routes/who/news-room.ts b/lib/routes/who/news-room.ts index 24ad2c45371e..f6b6f8212cf4 100644 --- a/lib/routes/who/news-room.ts +++ b/lib/routes/who/news-room.ts @@ -34,7 +34,7 @@ export const route: Route = { | --------------- | ------------ | | feature-stories | commentaries | - Language +Language | English | العربية | 中文 | Français | Русский | Español | Português | | ------- | ------- | ---- | -------- | ------- | ------- | --------- | diff --git a/lib/routes/whu/news.ts b/lib/routes/whu/news.ts index 66bd1c861717..ba044b810a50 100644 --- a/lib/routes/whu/news.ts +++ b/lib/routes/whu/news.ts @@ -16,15 +16,13 @@ export const route: Route = { name: '新闻网', maintainers: [], handler, - description: ` -category 参数可选,范围如下: + description: `category 参数可选,范围如下: -| 新闻栏目 | 武大资讯 | 学术动态 | 珞珈影像 | 武大视频 | -| -------- | -------- | -------- | -------- | -------- | -| 参数 | 0 或 \`wdzx/wdyw\` | 1 或 \`kydt\` | 2 或 \`stkj/ljyx\` | 3 或 \`stkj/wdsp\` | +| 新闻栏目 | 武大资讯 | 学术动态 | 珞珈影像 | 武大视频 | +| -------- | ---------------- | ----------- | ---------------- | ---------------- | +| 参数 | 0 或 \`wdzx/wdyw\` | 1 或 \`kydt\` | 2 或 \`stkj/ljyx\` | 3 或 \`stkj/wdsp\` | -此外 route 后可以加上 \`?limit=n\` 的查询参数,表示只获取前 n 条新闻;如果不指定默认为 10。 -`, +此外 route 后可以加上 \`?limit=n\` 的查询参数,表示只获取前 n 条新闻;如果不指定默认为 10。`, }; const parseCategory = (category: string | number) => { diff --git a/lib/routes/whu/rsgis.ts b/lib/routes/whu/rsgis.ts index c1cb00755d98..6716e0802525 100644 --- a/lib/routes/whu/rsgis.ts +++ b/lib/routes/whu/rsgis.ts @@ -232,9 +232,7 @@ export const route: Route = { ], name: '武汉大学遥感信息工程学院', maintainers: ['HPDell'], - description: ` - -| 分类名 | \`type\` 值 | 子类名 | \`sub\` 值 | + description: `| 分类名 | \`type\` 值 | 子类名 | \`sub\` 值 | | :------: | :-------- | :------: | :------- | | 首页 | \`index\` | | | | 学院新闻 | \`xyxw\` | 全部 | \`all\` | @@ -251,8 +249,7 @@ export const route: Route = { | | | 学院通知 | \`xytz\` | | | | 教学动态 | \`jxdt\` | | | | 学术动态 | \`xsdt\` | -| | | 人才引进 | \`rcyj\` | -`, +| | | 人才引进 | \`rcyj\` |`, handler: async (ctx: Context) => { const { type = 'index', sub = 'all' } = ctx.req.param(); let itemList: DataItem[]; diff --git a/lib/routes/wiensued/index.ts b/lib/routes/wiensued/index.ts index cd70d318bb40..c0e35b20af9f 100644 --- a/lib/routes/wiensued/index.ts +++ b/lib/routes/wiensued/index.ts @@ -14,10 +14,8 @@ export const route: Route = { path: '*', 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\`) -`, + 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'] diff --git a/lib/routes/wikinews/index.ts b/lib/routes/wikinews/index.ts index 1ccb16ab88dc..53b20f1e640c 100644 --- a/lib/routes/wikinews/index.ts +++ b/lib/routes/wikinews/index.ts @@ -28,7 +28,7 @@ export const route: Route = { name: '最新新闻', maintainers: ['KotoriK'], handler, - description: `根据维基新闻的[sitemap](https://zh.wikinews.org/wiki/Special:%E6%96%B0%E9%97%BB%E8%AE%A2%E9%98%85)获取新闻全文。目前仅支持中文维基新闻。`, + description: '根据维基新闻的[sitemap](https://zh.wikinews.org/wiki/Special:%E6%96%B0%E9%97%BB%E8%AE%A2%E9%98%85)获取新闻全文。目前仅支持中文维基新闻。', }; async function handler() { diff --git a/lib/routes/wise/pair.tsx b/lib/routes/wise/pair.tsx index 0338a02cc980..228c82db0ff3 100644 --- a/lib/routes/wise/pair.tsx +++ b/lib/routes/wise/pair.tsx @@ -53,7 +53,7 @@ export const route: Route = { name: 'FX Pair Yesterday', maintainers: ['HenryQW'], handler, - description: `Refer to [the list of supported currencies](https://wise.com/tools/exchange-rate-alerts/).`, + description: 'Refer to [the list of supported currencies](https://wise.com/tools/exchange-rate-alerts/).', }; async function handler(ctx) { @@ -108,7 +108,7 @@ async function handler(ctx) { return { title: `${source} to ${target} by Wise`, link, - description: `Exchange Rate from Wise`, + description: 'Exchange Rate from Wise', item: [single], }; } diff --git a/lib/routes/wizfile/index.ts b/lib/routes/wizfile/index.ts index 81e82eef44d3..8d9cd87a3c39 100644 --- a/lib/routes/wizfile/index.ts +++ b/lib/routes/wizfile/index.ts @@ -60,7 +60,7 @@ async function handler() { }); return { - title: `WziFile - 更新日志`, + title: 'WziFile - 更新日志', link: currentUrl, item: items, }; diff --git a/lib/routes/wmpvp/index.ts b/lib/routes/wmpvp/index.ts index ea8b3e959422..844ef7e11a34 100644 --- a/lib/routes/wmpvp/index.ts +++ b/lib/routes/wmpvp/index.ts @@ -63,7 +63,7 @@ async function handler(ctx) { return { title: `完美世界电竞 - ${TYPE_MAP[type]} 资讯`, - link: `https://news.wmpvp.com/`, + link: 'https://news.wmpvp.com/', item: items, }; } diff --git a/lib/routes/wogem/index.ts b/lib/routes/wogem/index.ts index edbebb315186..036b6429d36c 100644 --- a/lib/routes/wogem/index.ts +++ b/lib/routes/wogem/index.ts @@ -14,7 +14,7 @@ export const route: Route = { path: '/:page?', maintainers: ['sk22'], categories: ['other'], - description: `Pass in the name of the php file, e.g. \`angebote\` for \`/de/angebote.php\`\`.`, + description: 'Pass in the name of the php file, e.g. `angebote` for \\`/de/angebote.php\\`\\`.', parameters: { page: 'Page name, e.g. `angebote` for `angebote.php. Defaults to `angebote`', }, diff --git a/lib/routes/wordpress/index.ts b/lib/routes/wordpress/index.ts index 812ead699b5a..a0558f897558 100644 --- a/lib/routes/wordpress/index.ts +++ b/lib/routes/wordpress/index.ts @@ -143,13 +143,13 @@ export const route: Route = { description: `If you subscribe to [WordPress News](https://wordpress.org/news/),where the URL is \`https://wordpress.org/news/\`, Encode the URL using \`encodeURIComponent()\` and then use it as the parameter. Therefore, the route will be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews). ::: tip - If you wish to subscribe to specific categories or tags, you can fill in the "filter" parameter in the route. \`/category/Podcast\` to subscribe to the Podcast category. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast). +If you wish to subscribe to specific categories or tags, you can fill in the "filter" parameter in the route. \`/category/Podcast\` to subscribe to the Podcast category. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast). - You can also subscribe to multiple categories. \`/category/Podcast,Community\` to subscribe to both the Podcast and Community categories. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast,Community\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast,Community). +You can also subscribe to multiple categories. \`/category/Podcast,Community\` to subscribe to both the Podcast and Community categories. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast,Community\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Podcast,Community). - Categories and tags can be combined as well. \`/category/Releases/tag/tagging\` to subscribe to the Releases category and the tagging tag. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Releases/tag/tagging\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Releases/tag/tagging). - - You can also search for keywords. \`/search/Blog\` to search for the keyword "Blog". In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/search/Blog\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/search/Blog). +Categories and tags can be combined as well. \`/category/Releases/tag/tagging\` to subscribe to the Releases category and the tagging tag. In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Releases/tag/tagging\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/category/Releases/tag/tagging). + +You can also search for keywords. \`/search/Blog\` to search for the keyword "Blog". In this case, the route would be [\`/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/search/Blog\`](https://rsshub.app/wordpress/https%3A%2F%2Fwordpress.org%2Fnews/search/Blog). :::`, categories: ['blog'], diff --git a/lib/routes/wsj/news.ts b/lib/routes/wsj/news.ts index 146df4a89357..c9c575f0e119 100644 --- a/lib/routes/wsj/news.ts +++ b/lib/routes/wsj/news.ts @@ -27,19 +27,19 @@ export const route: Route = { name: 'News', maintainers: ['oppilate'], handler, - description: `en_us + description: `en\\_us | World | U.S. | Politics | Economy | Business | Tech | Markets | Opinion | Books & Arts | Real Estate | Life & Work | Sytle | Sports | | ----- | ---- | -------- | ------- | -------- | ---------- | ------- | ------- | ------------ | ----------- | ----------- | ------------------- | ------ | | world | us | politics | economy | business | technology | markets | opinion | books-arts | realestate | life-work | style-entertainment | sports | - zh-cn / zh-tw +zh-cn / zh-tw | 国际 | 中国 | 金融市场 | 经济 | 商业 | 科技 | 派 | 专栏与观点 | | ----- | ----- | -------- | ------- | -------- | ---------- | --------- | ---------- | | world | china | markets | economy | business | technology | life-arts | opinion | - Provide full article RSS for WSJ topics.`, +Provide full article RSS for WSJ topics.`, }; async function handler(ctx) { diff --git a/lib/routes/wyzxwk/article.ts b/lib/routes/wyzxwk/article.ts index c5193c956523..3ac2a23874ef 100644 --- a/lib/routes/wyzxwk/article.ts +++ b/lib/routes/wyzxwk/article.ts @@ -32,37 +32,37 @@ export const route: Route = { | -------- | -------- | | shidai | yulun | - 经济 +经济 | 经济视点 | 社会民生 | 三农关注 | 产业研究 | | -------- | -------- | -------- | -------- | | jingji | shehui | sannong | chanye | - 国际 +国际 | 国际纵横 | 国防外交 | | -------- | -------- | | guoji | guofang | - 思潮 +思潮 | 理想之旅 | 思潮碰撞 | 文艺新生 | 读书交流 | | -------- | -------- | -------- | -------- | | lixiang | sichao | wenyi | shushe | - 历史 +历史 | 历史视野 | 中华文化 | 中华医药 | 共产党人 | | -------- | -------- | -------- | -------- | | lishi | zhonghua | zhongyi | cpers | - 争鸣 +争鸣 | 风华正茂 | 工农之声 | 网友杂谈 | 网友时评 | | -------- | -------- | -------- | -------- | | qingnian | gongnong | zatan | shiping | - 活动 +活动 | 乌有公告 | 红色旅游 | 乌有讲堂 | 书画欣赏 | | -------- | -------- | --------- | -------- | diff --git a/lib/routes/xaut/index.ts b/lib/routes/xaut/index.ts index 395cd3be3279..c9dc5b92b93a 100644 --- a/lib/routes/xaut/index.ts +++ b/lib/routes/xaut/index.ts @@ -66,7 +66,7 @@ async function handler(ctx) { // 源链接 link: 'http://www.xaut.edu.cn', // 源说明 - description: `西安理工大学官网-` + dic_title[category], + description: '西安理工大学官网-' + dic_title[category], // 遍历此前获取的数据 item: await Promise.all( list.map((item) => diff --git a/lib/routes/xaut/jwc.ts b/lib/routes/xaut/jwc.ts index 0bfc7b8381a4..66b8dd471658 100644 --- a/lib/routes/xaut/jwc.ts +++ b/lib/routes/xaut/jwc.ts @@ -22,7 +22,7 @@ export const route: Route = { maintainers: ['mocusez'], handler, description: `::: warning - 有些内容需使用校园网或 VPN 访问知行网获取 +有些内容需使用校园网或 VPN 访问知行网获取 ::: | 通知公告 | 新闻动态 | 规章制度 | 竞赛结果公示 | 竞赛获奖通知 | 竞赛信息 | 公开公示 | @@ -70,7 +70,7 @@ async function handler(ctx) { // 源链接 link: rootUrl, // 源说明 - description: `西安理工大学教务处-` + dic_title[category], + description: '西安理工大学教务处-' + dic_title[category], // 遍历此前获取的数据 item: await Promise.all( list.map((item) => diff --git a/lib/routes/xaut/rsc.ts b/lib/routes/xaut/rsc.ts index 3c1af4ed9ad5..14b1dc54ddac 100644 --- a/lib/routes/xaut/rsc.ts +++ b/lib/routes/xaut/rsc.ts @@ -22,7 +22,7 @@ export const route: Route = { maintainers: ['mocusez', 'light0926'], handler, description: `::: warning - 有些内容指向外部链接,目前只提供这些链接,不提供具体内容,去除 jwc 和 index 的修改 +有些内容指向外部链接,目前只提供这些链接,不提供具体内容,去除 jwc 和 index 的修改 ::: | 通知公告 | 工作动态 | @@ -69,7 +69,7 @@ async function handler(ctx) { // 源链接 link: 'http://renshichu.xaut.edu.cn', // 源说明 - description: `西安理工大学人事处-` + dic_title[category], + description: '西安理工大学人事处-' + dic_title[category], // 遍历此前获取的数据 item: await Promise.all( list.map((item) => diff --git a/lib/routes/xhamster/index.ts b/lib/routes/xhamster/index.ts new file mode 100644 index 000000000000..c92562d5a00c --- /dev/null +++ b/lib/routes/xhamster/index.ts @@ -0,0 +1,147 @@ +import { load } from 'cheerio'; + +import type { Route } from '@/types'; +import got from '@/utils/got'; +import { parseDate } from '@/utils/parse-date'; + +export const route: Route = { + path: '/:creators', + categories: ['multimedia'], + example: '/xhamster/faustina-pierre', + parameters: { + creators: 'Creator slug from the URL (e.g. `faustina-pierre`)', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + nsfw: true, + }, + radar: [ + { + source: ['xhamster.com/creators/:creators', 'xhamster.com/creators/:creators/newest'], + target: '/:creators', + }, + ], + name: 'Newest Videos by Creator', + maintainers: ['eve2ptp'], + handler, + url: 'xhamster.com/faustina-pierre/newest', +}; + +interface VideoThumb { + id: number; + title: string; + pageURL: string; + thumbURL: string; + imageURL?: string; + trailerURL?: string; + trailerFallbackUrl?: string; + created?: number; + duration?: number; + views?: number; + isUHD?: boolean; +} + +interface Initials { + infoComponent?: { + pornstarTop?: { + name?: string; + }; + }; + trendingVideoSectionComponent?: { + videoListProps?: { + videoThumbProps?: VideoThumb[]; + }; + }; +} + +function extractInitials(scriptContent: string): Initials { + const match = scriptContent.match(/window\.initials\s*=\s*([\s\S]*?);?$/); + if (!match) { + throw new Error('initials not found'); + } + return JSON.parse(match[1]); +} + +function formatDuration(seconds: number): string { + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + const s = seconds % 60; + return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}` : `${m}:${String(s).padStart(2, '0')}`; +} + +function renderDescription(video: VideoThumb): string { + const thumb = video.imageURL ?? video.thumbURL; + const duration = video.duration ? formatDuration(video.duration) : ''; + const views = video.views === undefined ? '' : video.views.toLocaleString(); + const quality = video.isUHD ? '4K' : ''; + + let meta = ''; + + if (quality) { + meta += quality; + } + if (duration) { + meta += `${meta ? ' · ' : ''}Duration: ${duration}`; + } + if (views) { + meta += `${meta ? ' · ' : ''}Views: ${views}`; + } + + return ` + ${video.title} +

    ${meta}

    + `.trim(); +} + +async function handler(ctx) { + const { creators } = ctx.req.param(); + const pageUrl = `https://xhamster.com/creators/${encodeURIComponent(creators)}/newest`; + + const response = await got(pageUrl); + + const $ = load(response.data); + const initialsRaw = $('#initials-script').text(); + if (!initialsRaw) { + throw new Error('Could not locate initials script on page'); + } + + let initials: Initials; + try { + initials = extractInitials(initialsRaw); + } catch { + throw new Error('Failed to parse page data'); + } + + const creatorName = initials.infoComponent?.pornstarTop?.name ?? creators; + const videos = initials.trendingVideoSectionComponent?.videoListProps?.videoThumbProps ?? []; + + const items = videos.map((video) => ({ + title: `${video.title}${video.isUHD ? ' [4K]' : ''}`, + link: video.pageURL, + pubDate: video.created ? parseDate(video.created * 1000) : undefined, + author: creatorName, + description: renderDescription(video), + media: { + content: { + url: video.trailerURL ?? video.pageURL, + type: 'video/mp4', + ...(video.duration && { duration: video.duration }), + }, + thumbnail: { + url: video.imageURL ?? video.thumbURL, + }, + }, + })); + + return { + title: `${creatorName} - newest videos on xHamster`, + link: pageUrl, + description: `Latest videos from ${creatorName} on xHamster`, + item: items, + }; +} diff --git a/lib/routes/xhamster/namespace.ts b/lib/routes/xhamster/namespace.ts new file mode 100644 index 000000000000..48e5450af3b5 --- /dev/null +++ b/lib/routes/xhamster/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'xHamster', + url: 'xhamster.com', + lang: 'en', +}; diff --git a/lib/routes/xianbao/index.ts b/lib/routes/xianbao/index.ts index fc77de5efaaf..ab7f5414e5da 100644 --- a/lib/routes/xianbao/index.ts +++ b/lib/routes/xianbao/index.ts @@ -10,8 +10,7 @@ export const route: Route = { handler, example: '/xianbao', parameters: { category: '类别id,默认为:latest' }, - description: ` -| 分类 | id | + description: `| 分类 | id | | ------------ | -------------- | | 最新 | latest | | 赚客吧 | zuankeba | @@ -26,8 +25,8 @@ export const route: Route = { | 小嘀咕 | xiaodigu | | 葫芦侠 | huluxia | | 小刀娱乐网 | xiadao | -| 技术QQ网 | qqjishu | -| YYOK大全 | yyok | +| 技术 QQ 网 | qqjishu | +| YYOK 大全 | yyok | | 活动资讯网 | huodong | | 免费赚钱中心 | mianfei | | 一小时 | yixiaoshi | diff --git a/lib/routes/xiaoheihe/add2cart.ts b/lib/routes/xiaoheihe/add2cart.ts index d6c5b5c36b97..79a9d42e9d1e 100644 --- a/lib/routes/xiaoheihe/add2cart.ts +++ b/lib/routes/xiaoheihe/add2cart.ts @@ -57,7 +57,7 @@ async function handler(ctx) { return { title: `小黑盒 ${platform.toUpperCase()} 喜加一`, - link: `https://xiaoheihe.cn`, + link: 'https://xiaoheihe.cn', item: items, }; } diff --git a/lib/routes/xiaoheihe/discount.ts b/lib/routes/xiaoheihe/discount.ts index df1aa9ebf9ae..75243bc28d9e 100644 --- a/lib/routes/xiaoheihe/discount.ts +++ b/lib/routes/xiaoheihe/discount.ts @@ -19,9 +19,9 @@ export const route: Route = { name: '游戏折扣', maintainers: ['tssujt'], handler, - description: `| PC | Switch | PSN | Xbox | -| ----- | ------ | ----- | ----- | -| pc | switch | psn | xbox |`, + description: `| PC | Switch | PSN | Xbox | +| -- | ------ | --- | ---- | +| pc | switch | psn | xbox |`, }; const PLATFORM_MAP = { @@ -139,7 +139,7 @@ async function handler(ctx) { return { title: `小黑盒 ${platformInfo.desc} 游戏折扣`, - link: `https://xiaoheihe.cn`, + link: 'https://xiaoheihe.cn', item: items, }; } diff --git a/lib/routes/xiaoheihe/news.ts b/lib/routes/xiaoheihe/news.ts index cddb0f3bb6a3..5740b4c3c685 100644 --- a/lib/routes/xiaoheihe/news.ts +++ b/lib/routes/xiaoheihe/news.ts @@ -23,7 +23,7 @@ export const route: Route = { }; async function handler() { - const feedUrl = calculate(`https://api.xiaoheihe.cn/bbs/app/feeds/news?os_type=web&app=heybox&client_type=mobile&version=999.0.3&x_client_type=web&x_os_type=Mac&x_app=heybox&heybox_id=-1&appid=900018355&offset=0&limit=20`); + const feedUrl = calculate('https://api.xiaoheihe.cn/bbs/app/feeds/news?os_type=web&app=heybox&client_type=mobile&version=999.0.3&x_client_type=web&x_os_type=Mac&x_app=heybox&heybox_id=-1&appid=900018355&offset=0&limit=20'); const response = await got({ method: 'get', url: feedUrl, @@ -55,8 +55,8 @@ async function handler() { ); return { - title: `小黑盒游戏新闻`, - link: `https://xiaoheihe.cn`, + title: '小黑盒游戏新闻', + link: 'https://xiaoheihe.cn', item: items, }; } diff --git a/lib/routes/xiaoheihe/user.ts b/lib/routes/xiaoheihe/user.ts index d708a51bc39d..d19838935db1 100644 --- a/lib/routes/xiaoheihe/user.ts +++ b/lib/routes/xiaoheihe/user.ts @@ -64,7 +64,7 @@ async function handler(ctx) { return { title: `${username} 的动态`, - link: `https://xiaoheihe.cn`, + link: 'https://xiaoheihe.cn', item: items, }; } diff --git a/lib/routes/xiaohongshu/util.ts b/lib/routes/xiaohongshu/util.ts index dcd23dd56e40..29d2573e936c 100644 --- a/lib/routes/xiaohongshu/util.ts +++ b/lib/routes/xiaohongshu/util.ts @@ -6,7 +6,7 @@ import cache from '@/utils/cache'; import logger from '@/utils/logger'; import ofetch from '@/utils/ofetch'; import { parseDate } from '@/utils/parse-date'; -import puppeteer, { getPuppeteerPage } from '@/utils/puppeteer'; +import playwright, { getPlaywrightPage } from '@/utils/playwright'; // Common headers for requests const getHeaders = (cookie?: string) => ({ @@ -58,12 +58,12 @@ const getUser = (url, cache) => userPageData = userPageData._rawValue || userPageData; notes = notes._rawValue || notes; - // Cannot get collect data without puppeteer + // Cannot get collect data without Playwright return { userPageData, notes, collect: '' }; } - // Use puppeteer - const { page, destroy } = await getPuppeteerPage(url, { + // Use Playwright + const { page, destroy } = await getPlaywrightPage(url, { onBeforeLoad: async (page) => { await page.setRequestInterception(true); page.on('request', (request) => { @@ -127,8 +127,8 @@ const getBoard = (url, cache) => return state.Main; } - // Use puppeteer - const browser = await puppeteer(); + // Use Playwright + const browser = await playwright(); try { const page = await browser.newPage(); await page.setRequestInterception(true); @@ -151,9 +151,9 @@ const getBoard = (url, cache) => const formatText = (text) => text.replaceAll(/(\r\n|\r|\n)/g, '
    ').replaceAll('\t', ' '); // tag_list.id has nothing to do with its url -const formatTagList = (tagList) => tagList.reduce((acc, item) => acc + `#${item.name} `, ``); +const formatTagList = (tagList) => tagList.reduce((acc, item) => acc + `#${item.name} `, ''); -const formatImageList = (imageList) => imageList.reduce((acc, item) => acc + `
    `, ``); +const formatImageList = (imageList) => imageList.reduce((acc, item) => acc + `
    `, ''); const formatNote = (url, note) => ({ title: note.title, diff --git a/lib/routes/xiaomiyoupin/crowdfunding.ts b/lib/routes/xiaomiyoupin/crowdfunding.ts index ae5f4dbc4c72..b3e9d4034949 100644 --- a/lib/routes/xiaomiyoupin/crowdfunding.ts +++ b/lib/routes/xiaomiyoupin/crowdfunding.ts @@ -37,7 +37,11 @@ async function handler() { const sign = urlParams.get('sign'); // 1. fetchPageData - const pageData = await got(`${base_url}/mtop/navi/venue/page?page_id=${pageid}&pdl=jianyu&sign=${sign}`); + const pageData = await got(`${base_url}/mtop/navi/venue/page?page_id=${pageid}&pdl=jianyu&sign=${sign}`, { + headers: { + accept: '*/*', + }, + }); const crowd_funding_floor = pageData.data.data.floors.find((floor) => floor.module_key === 'crowding'); const query_list = crowd_funding_floor.query_list; diff --git a/lib/routes/xiaote/index.ts b/lib/routes/xiaote/index.ts index d961b8c18f7d..4a26ec6563ad 100644 --- a/lib/routes/xiaote/index.ts +++ b/lib/routes/xiaote/index.ts @@ -29,21 +29,23 @@ export const route: Route = { async function handler() { const { data } = await got.post('https://lcen.xiaote.net//api/graphql/', { json: { - query: `query($startCursor: Int) { - communities(startCursor: $startCursor) { - edges { - node { - objectId - content - createdAt - imageUrls - user{ - nickname + query: /* GraphQL */ ` + query ($startCursor: Int) { + communities(startCursor: $startCursor) { + edges { + node { + objectId + content + createdAt + imageUrls + user { + nickname + } } + } } } - } - }`, + `, }, }); diff --git a/lib/routes/xidian/cs.ts b/lib/routes/xidian/cs.ts index 443b6a2608a5..ddffd258e8e9 100644 --- a/lib/routes/xidian/cs.ts +++ b/lib/routes/xidian/cs.ts @@ -76,15 +76,15 @@ export const route: Route = { url: 'cs.xidian.edu.cn', maintainers: ['ZiHao256'], handler, - description: `| 文章来源 | 参数 | -| ---------------------- | ----------- | -| ✅主页-学院新闻 | xyxw | -| ✅主页-通知公告 | tzgg | -| ✅主页-交流合作 | jlhz1 | -| ✅主页-人事人才 | rsrc | -| ✅主页-本科生教育 / 本科教育-教学新闻 | bkjy_jxxw | -| ✅主页-研究生教育 / 研究生教育-研究生通知 | yjsjy_yjstz | -| ✅主页-就业招聘 | jyzhaop |`, + description: `| 文章来源 | 参数 | +| --------------------------------------------- | ------------ | +| ✅主页 - 学院新闻 | xyxw | +| ✅主页 - 通知公告 | tzgg | +| ✅主页 - 交流合作 | jlhz1 | +| ✅主页 - 人事人才 | rsrc | +| ✅主页 - 本科生教育 / 本科教育 - 教学新闻 | bkjy\\_jxxw | +| ✅主页 - 研究生教育 / 研究生教育 - 研究生通知 | yjsjy\\_yjstz | +| ✅主页 - 就业招聘 | jyzhaop |`, radar: [ { source: ['cs.xidian.edu.cn/'], diff --git a/lib/routes/xidian/gr.ts b/lib/routes/xidian/gr.ts index 614f92bab464..1f2ef643f2e9 100644 --- a/lib/routes/xidian/gr.ts +++ b/lib/routes/xidian/gr.ts @@ -190,33 +190,33 @@ export const route: Route = { url: 'gr.xidian.edu.cn', maintainers: ['ZiHao256'], handler, - description: `| 文章来源 | 参数 | -| ------------- | ------------ | -| ✅主页-最新动态 | home_zxdt | -| ✅主页-通知公告 | home_tzgg1 | -| ✅主页-讲座报告 | home_jzbg | -| ✅研院介绍-基本情况 | yyjs_jbqk | -| ✅研院介绍-机构设置 | yyjs_jbqk1 | -| ✅研院介绍-部门领导 | yyjs_jbqk2 | -| ✅研院介绍-服务指南 | yyjs_jbqk3 | -| ✅研院介绍-学院联系方式 | yyjs_jbqk4 | -| ✅招生信息 | yjsy | -| ✅招生信息-硕士研究生招生 | yjsy_yjszs | -| ✅招生信息-博士研究生招生 | yjsy_bsyjszs | -| ✅招生信息-其他类型招生 | yjsy_qtlxzs | -| ✅培养管理 | pygl | -| ✅培养管理-学术学位 | pygl_xsxw | -| ✅培养管理-专业学位 | pygl_zyxw | -| ✅培养管理-教学管理 | pygl_jxgl | -| ✅培养管理-课程建设 | pygl_jxgl1 | -| ✅培养管理-管理规定 | pygl_jxgl2 | -| ✅培养管理-国际交流 | pygl_jxgl3 | -| ✅培养管理-办事流程 | pygl_bslc | -| ✅学位授予 | xwsy | -| ✅学位授予-通知公告 | xwsy_tzgg | -| ✅学位授予-规章制度 | xwsy_gzzd | -| ✅学位授予-授位名单 | xwsy_swmd | -| ✅学位授予-资料下载 | xwsy_zlxz |`, + description: `| 文章来源 | 参数 | +| --------------------------- | ------------- | +| ✅主页 - 最新动态 | home\\_zxdt | +| ✅主页 - 通知公告 | home\\_tzgg1 | +| ✅主页 - 讲座报告 | home\\_jzbg | +| ✅研院介绍 - 基本情况 | yyjs\\_jbqk | +| ✅研院介绍 - 机构设置 | yyjs\\_jbqk1 | +| ✅研院介绍 - 部门领导 | yyjs\\_jbqk2 | +| ✅研院介绍 - 服务指南 | yyjs\\_jbqk3 | +| ✅研院介绍 - 学院联系方式 | yyjs\\_jbqk4 | +| ✅招生信息 | yjsy | +| ✅招生信息 - 硕士研究生招生 | yjsy\\_yjszs | +| ✅招生信息 - 博士研究生招生 | yjsy\\_bsyjszs | +| ✅招生信息 - 其他类型招生 | yjsy\\_qtlxzs | +| ✅培养管理 | pygl | +| ✅培养管理 - 学术学位 | pygl\\_xsxw | +| ✅培养管理 - 专业学位 | pygl\\_zyxw | +| ✅培养管理 - 教学管理 | pygl\\_jxgl | +| ✅培养管理 - 课程建设 | pygl\\_jxgl1 | +| ✅培养管理 - 管理规定 | pygl\\_jxgl2 | +| ✅培养管理 - 国际交流 | pygl\\_jxgl3 | +| ✅培养管理 - 办事流程 | pygl\\_bslc | +| ✅学位授予 | xwsy | +| ✅学位授予 - 通知公告 | xwsy\\_tzgg | +| ✅学位授予 - 规章制度 | xwsy\\_gzzd | +| ✅学位授予 - 授位名单 | xwsy\\_swmd | +| ✅学位授予 - 资料下载 | xwsy\\_zlxz |`, radar: [ { source: ['gr.xidian.edu.cn/'], diff --git a/lib/routes/ximalaya/album.ts b/lib/routes/ximalaya/album.ts index e6c8d37e571b..de8cd9a2dbdf 100644 --- a/lib/routes/ximalaya/album.ts +++ b/lib/routes/ximalaya/album.ts @@ -77,9 +77,9 @@ export const route: Route = { description: `目前喜马拉雅的 API 只能一集一集的获取各节目上的 ShowNote,会极大的占用系统资源,所以默认为不获取节目的 ShowNote。 ::: warning - 专辑类型即 url 中的分类拼音,使用通用分类 \`album\` 通常是可行的,专辑 id 是跟在**分类拼音**后的那个 id, 不要输成某集的 id 了 +专辑类型即 url 中的分类拼音,使用通用分类 \`album\` 通常是可行的,专辑 id 是跟在**分类拼音**后的那个 id, 不要输成某集的 id 了 - **付费内容需要配置好已购买账户的 token 才能收听,详情见部署页面的配置模块** +**付费内容需要配置好已购买账户的 token 才能收听,详情见部署页面的配置模块** :::`, }; diff --git a/lib/routes/xinpianchang/index.ts b/lib/routes/xinpianchang/index.ts index 60b7d355f7b5..55a662320595 100644 --- a/lib/routes/xinpianchang/index.ts +++ b/lib/routes/xinpianchang/index.ts @@ -20,9 +20,9 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 跳转到欲订阅的分类页,将 URL 的 \`/discover\` 到末尾的部分填入 \`params\` 参数。 +跳转到欲订阅的分类页,将 URL 的 \`/discover\` 到末尾的部分填入 \`params\` 参数。 - 如 [全部原创视频作品](https://www.xinpianchang.com/discover/article-0-0-all-all-0-0-score) 的 URL 为 \`https://www.xinpianchang.com/discover/article-0-0-all-all-0-0-score\`,其 \`/discover\` 到末尾的部分为 \`article-0-0-all-all-0-0-score\`,所以对应的路由为 [/xinpianchang/discover/article-0-0-all-all-0-0-score](https://rsshub.app/xinpianchang/discover/article-0-0-all-all-0-0-score)。 +如 [全部原创视频作品](https://www.xinpianchang.com/discover/article-0-0-all-all-0-0-score) 的 URL 为 \`https://www.xinpianchang.com/discover/article-0-0-all-all-0-0-score\`,其 \`/discover\` 到末尾的部分为 \`article-0-0-all-all-0-0-score\`,所以对应的路由为 [/xinpianchang/discover/article-0-0-all-all-0-0-score](https://rsshub.app/xinpianchang/discover/article-0-0-all-all-0-0-score)。 :::`, }; diff --git a/lib/routes/xjtlu/news.ts b/lib/routes/xjtlu/news.ts index 2152b710ba69..42de92b9d2ca 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').first().text().trim(); + const articleDate = $article('.edited-view .date, p.date').text().trim(); // Parse date based on language // English: "21 Jan 2026" with 'en' locale for month name recognition @@ -108,7 +108,7 @@ const handler = async (ctx) => { title: item.title, link: item.link!, category: item.category, - description: fullContent || undefined, + description: fullContent, pubDate, }; }) diff --git a/lib/routes/xjtu/ee-jzxx.ts b/lib/routes/xjtu/ee-jzxx.ts index d8c36523cd53..8d8086a3b434 100644 --- a/lib/routes/xjtu/ee-jzxx.ts +++ b/lib/routes/xjtu/ee-jzxx.ts @@ -31,9 +31,9 @@ export const route: Route = { handler, description: `栏目类型 -| 主页 | 本科生 | 研究生 | 科研学术 | 采购招标 | 招聘就业 | 行政办公 -| --- | ----- | ----- | ------ | ------- | ------ | ------ -| - | bks | yjs | kyxs | cgzb | zpjy | xzbg `, +| 主页 | 本科生 | 研究生 | 科研学术 | 采购招标 | 招聘就业 | 行政办公 | +| ---- | ------ | ------ | -------- | -------- | -------- | -------- | +| - | bks | yjs | kyxs | cgzb | zpjy | xzbg |`, }; async function handler(ctx) { diff --git a/lib/routes/xjtu/zs.ts b/lib/routes/xjtu/zs.ts index edf32ef009d6..9c7b3d682ed9 100644 --- a/lib/routes/xjtu/zs.ts +++ b/lib/routes/xjtu/zs.ts @@ -144,8 +144,7 @@ export const route: Route = { | [招生快讯](https://zs.xjtu.edu.cn/zsxx1/zskx.htm) | [招生政策](https://zs.xjtu.edu.cn/zsxx1/zszc.htm) | [招生计划](https://zs.xjtu.edu.cn/zsxx1/zsjh.htm) | [阳光公告](https://zs.xjtu.edu.cn/zsxx1/yggg.htm) | [历年录取](https://zs.xjtu.edu.cn/zsxx1/lnlq.htm) | | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | -| [zsxx1/zskx](https://rsshub.app/xjtu/zs/zsxx1/zskx) | [zsxx1/zszc](https://rsshub.app/xjtu/zs/zsxx1/zszc) | [zsxx1/zsjh](https://rsshub.app/xjtu/zs/zsxx1/zsjh) | [zsxx1/yggg](https://rsshub.app/xjtu/zs/zsxx1/yggg) | [zsxx1/lnlq](https://rsshub.app/xjtu/zs/zsxx1/lnlq) | -`, +| [zsxx1/zskx](https://rsshub.app/xjtu/zs/zsxx1/zskx) | [zsxx1/zszc](https://rsshub.app/xjtu/zs/zsxx1/zszc) | [zsxx1/zsjh](https://rsshub.app/xjtu/zs/zsxx1/zsjh) | [zsxx1/yggg](https://rsshub.app/xjtu/zs/zsxx1/yggg) | [zsxx1/lnlq](https://rsshub.app/xjtu/zs/zsxx1/lnlq) |`, categories: ['university'], features: { requireConfig: false, diff --git a/lib/routes/xsijishe/forum.ts b/lib/routes/xsijishe/forum.ts index afbb089840e0..d1c6d9a0c35a 100644 --- a/lib/routes/xsijishe/forum.ts +++ b/lib/routes/xsijishe/forum.ts @@ -35,7 +35,7 @@ export const route: Route = { maintainers: ['akynazh'], handler, description: `::: tip 关于子论坛 id 的获取方法 - \`/xsijishe/forum/51\` 对应于论坛 \`https://xsijishe.com/forum-51-1.html\`,这个论坛的 fid 为 51,也就是 \`forum-{fid}-1\` 中的 fid。 +\`/xsijishe/forum/51\` 对应于论坛 \`https://xsijishe.com/forum-51-1.html\`,这个论坛的 fid 为 51,也就是 \`forum-{fid}-1\` 中的 fid。 :::`, }; diff --git a/lib/routes/xsijishe/rank.ts b/lib/routes/xsijishe/rank.ts index f9ee988141ec..0248c9b6d2e9 100644 --- a/lib/routes/xsijishe/rank.ts +++ b/lib/routes/xsijishe/rank.ts @@ -3,11 +3,9 @@ import { load } from 'cheerio'; import { config } from '@/config'; import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; -import got from '@/utils/got'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; -import { puppeteerGet } from './utils'; +import { playwrightGet } from './utils'; const baseUrl = 'https://xsijishe.com'; @@ -50,113 +48,55 @@ export const route: Route = { async function handler(ctx) { const rankType = ctx.req.param('type'); let title; - let index; // 用于选择第几个 li + let index; if (rankType === 'weekly') { title = '司机社综合周排行榜'; - index = 0; // 第一个 li 是周榜 + index = 0; } else if (rankType === 'monthly') { title = '司机社综合月排行榜'; - index = 1; // 第二个 li 是月榜 + index = 1; } else { throw new InvalidParameterError('Invalid rank type'); } - const browser = await puppeteer(); - let usePuppeteer = false; - + const browser = await playwright(); const url = `${baseUrl}/portal.php`; - const headers = { - 'Accept-Encoding': 'gzip, deflate, br', - 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', - Cookie: config.xsijishe.cookie, - 'User-Agent': config.xsijishe.userAgent, - }; - - const resp = await got(url, { - headers, - }); - - const redirectMatch = resp.data.match(/window\.location\.href\s*=\s*"([^"]+)"/); - if (redirectMatch) { - const redirectUrl = `${baseUrl}${redirectMatch[1]}`; - // 使用提取到的地址重新请求 - const realResp = await got(redirectUrl, { - headers, - }); - resp.data = realResp.data; - } - - const $ = load(resp.data); - // 根据 index 选择对应的 li,然后获取其中的 dd 元素 - let items = $('.nex_recon_lists ul li') - .eq(index) - .find('.nex_recons_demens dl dd') - .toArray() - .map((item) => { - item = $(item); - const title = item.find('h5').text().trim(); - const link = item.find('a').attr('href'); - const description = item.find('img').prop('outerHTML'); - return { - title, - link: `${baseUrl}/${link}`, - description, - }; - }); - - if (items.length > 0) { - const firstItem = items[0]; - const resp = await got(firstItem.link, { - headers, + try { + const data = await playwrightGet(url, browser, '.nex_recon_lists', { + cookie: config.xsijishe.cookie, + userAgent: config.xsijishe.userAgent, }); - const $ = load(resp.data); - const firstViewBox = $('.t_f').first(); - if (firstViewBox.length === 0) { - usePuppeteer = true; - } - } - - items = await Promise.all( - items.map((item) => - cache.tryGet(item.link, async () => { - let data; - if (usePuppeteer) { - data = await puppeteerGet(item.link, browser); - } else { - const resp = await got(item.link, { - headers, - }); - data = resp.data; - } - const $ = load(data); - const firstViewBox = $('.t_f').first(); - - if (firstViewBox.length === 1) { - firstViewBox.find('img').each((_, img) => { - img = $(img); - if (img.attr('zoomfile')) { - img.attr('src', img.attr('zoomfile')); - img.removeAttr('zoomfile'); - img.removeAttr('file'); - } - img.removeAttr('onmouseover'); - }); - - item.description = firstViewBox.html(); + const $ = load(data); + const items = $('.nex_recon_lists ul li') + .eq(index) + .find('.nex_recons_demens dl dd') + .toArray() + .map((item) => { + item = $(item); + const title = item.find('h5').text().trim(); + const link = item.find('a').attr('href'); + const description = item.find('img').prop('outerHTML') ?? ''; + + if (!title || !link) { + return; } - return item; + return { + title, + link: new URL(link, `${baseUrl}/`).toString(), + description, + }; }) - ) - ); - - await browser.close(); - - return { - title, - link: url, - description: title, - item: items, - }; + .filter((item) => item !== undefined); + + return { + title, + link: url, + description: title, + item: items, + }; + } finally { + await browser.close(); + } } diff --git a/lib/routes/xsijishe/utils.ts b/lib/routes/xsijishe/utils.ts index 70cdb6782391..f74a47a772aa 100644 --- a/lib/routes/xsijishe/utils.ts +++ b/lib/routes/xsijishe/utils.ts @@ -1,25 +1,48 @@ -const puppeteerGet = async (url, browser) => { +import type { Browser } from '@/utils/playwright'; +import { setCookies } from '@/utils/playwright-utils'; + +interface PlaywrightGetOptions { + cookie?: string; + timeout?: number; + userAgent?: string; +} + +const playwrightGet = async (url: string, browser: Browser, waitForSelector = '.t_f', options: PlaywrightGetOptions = {}) => { const page = await browser.newPage(); const expectResourceTypes = new Set(['document', 'script']); - await page.setRequestInterception(true); - page.on('request', (request) => { - expectResourceTypes.has(request.resourceType()) ? request.continue() : request.abort(); - }); - await page.goto(url, { - waitUntil: 'domcontentloaded', - }); - - let html; - html = await page.evaluate(() => document.documentElement.innerHTML); - if (html.includes('抱歉,您尚未登录,没有权限访问该版块')) { - await page.close(); + try { + if (options.userAgent) { + await page.setUserAgent(options.userAgent); + } + + if (options.cookie) { + await setCookies(page, options.cookie, 'xsijishe.com'); + } + + await page.setRequestInterception(true); + page.on('request', (request) => { + expectResourceTypes.has(request.resourceType()) ? request.continue() : request.abort(); + }); + await page.goto(url, { + waitUntil: 'domcontentloaded', + }); + + let html = await page.evaluate(() => document.documentElement.innerHTML); + if (html.includes('抱歉,您尚未登录,没有权限访问该版块')) { + return html; + } + + try { + await page.waitForSelector(waitForSelector, { timeout: options.timeout ?? 10000 }); + } catch { + // Return the loaded HTML even when the expected selector is missing. + } + + html = await page.evaluate(() => document.documentElement.innerHTML); return html; + } finally { + await page.close(); } - - await page.waitForSelector('.t_f'); - html = await page.evaluate(() => document.documentElement.innerHTML); - await page.close(); - return html; }; -export { puppeteerGet }; +export { playwrightGet }; diff --git a/lib/routes/xueqiu/cookies.ts b/lib/routes/xueqiu/cookies.ts index 5111e63eb97c..076310122616 100644 --- a/lib/routes/xueqiu/cookies.ts +++ b/lib/routes/xueqiu/cookies.ts @@ -1,13 +1,13 @@ import { config } from '@/config'; import cache from '@/utils/cache'; -import puppeteer from '@/utils/puppeteer'; -import { getCookies } from '@/utils/puppeteer-utils'; +import playwright from '@/utils/playwright'; +import { getCookies } from '@/utils/playwright-utils'; export const parseToken = (link: string) => cache.tryGet( 'xueqiu:token', async () => { - const browser = await puppeteer(); + const browser = await playwright(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { diff --git a/lib/routes/xueqiu/timeline.ts b/lib/routes/xueqiu/timeline.ts index 9f3ecae05d6e..4638175914fd 100644 --- a/lib/routes/xueqiu/timeline.ts +++ b/lib/routes/xueqiu/timeline.ts @@ -28,7 +28,7 @@ export const route: Route = { maintainers: ['ErnestDong'], handler, description: `::: warning - 用户关注动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户关注动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 ::: | -1 | -2 | 1 | diff --git a/lib/routes/xueqiu/user-stock.ts b/lib/routes/xueqiu/user-stock.ts index 44959474ad0c..ac51a212bfea 100644 --- a/lib/routes/xueqiu/user-stock.ts +++ b/lib/routes/xueqiu/user-stock.ts @@ -31,7 +31,7 @@ export const route: Route = { maintainers: ['hillerliao'], handler, description: `::: warning - 用户自选动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户自选动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts index 89de8c06ab18..5b111f133908 100644 --- a/lib/routes/xueqiu/user.ts +++ b/lib/routes/xueqiu/user.ts @@ -4,7 +4,7 @@ import { parseToken } from '@/routes/xueqiu/cookies'; import type { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; -import puppeteer from '@/utils/puppeteer'; +import playwright from '@/utils/playwright'; const rootUrl = 'https://xueqiu.com'; @@ -50,7 +50,7 @@ async function handler(ctx) { const link = `${rootUrl}/u/${id}`; const token = await parseToken(link); - const browser = await puppeteer(); + const browser = await playwright(); try { const mainPage = await browser.newPage(); diff --git a/lib/routes/xunhupay/index.ts b/lib/routes/xunhupay/index.ts index 59b965b642ab..a9ad383df916 100644 --- a/lib/routes/xunhupay/index.ts +++ b/lib/routes/xunhupay/index.ts @@ -32,8 +32,8 @@ async function handler() { return await buildData({ link, url: link, - title: `%title%`, - description: `%description%`, + title: '%title%', + description: '%description%', params: { title: '博客', description: '虎皮椒-博客', diff --git a/lib/routes/xupt/jyc.ts b/lib/routes/xupt/jyc.ts index 5e1e640cc82f..d509cf1e7ca4 100644 --- a/lib/routes/xupt/jyc.ts +++ b/lib/routes/xupt/jyc.ts @@ -25,8 +25,8 @@ export const route: Route = { name: '教务处通知公告', maintainers: ['StudyingLover'], handler, - description: `| 分类 | 参数 | -| ---- | ---- | + description: `| 分类 | 参数 | +| -------- | ---- | | 通知公告 | tzgg |`, }; diff --git a/lib/routes/xwenming/index.ts b/lib/routes/xwenming/index.ts index f65d50b1b436..7fadde5fa72d 100644 --- a/lib/routes/xwenming/index.ts +++ b/lib/routes/xwenming/index.ts @@ -138,8 +138,7 @@ export const route: Route = { | [科技前沿](https://www.xwenming.com/index.php/category/news) | [news](https://rsshub.app/xwenming/category/news) | | [疑难杂症](https://www.xwenming.com/index.php/category/solve) | [solve](https://rsshub.app/xwenming/category/solve) | | [通知专栏](https://www.xwenming.com/index.php/category/notice) | [notice](https://rsshub.app/xwenming/category/notice) | -| [未分类](https://www.xwenming.com/index.php/category/uncategorized) | [uncategorized](https://rsshub.app/xwenming/category/uncategorized) | -`, +| [未分类](https://www.xwenming.com/index.php/category/uncategorized) | [uncategorized](https://rsshub.app/xwenming/category/uncategorized) |`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/yahoo/namespace.ts b/lib/routes/yahoo/namespace.ts index fc2da8e81d75..fa507dec371a 100644 --- a/lib/routes/yahoo/namespace.ts +++ b/lib/routes/yahoo/namespace.ts @@ -2,6 +2,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Yahoo', - url: 'hk.news.yahoo.com', - lang: 'zh-HK', + url: 'news.yahoo.com', + lang: 'en-us', }; diff --git a/lib/routes/yahoo/news/index.ts b/lib/routes/yahoo/news/index.ts index 58b6a44f6761..45fc7f265065 100644 --- a/lib/routes/yahoo/news/index.ts +++ b/lib/routes/yahoo/news/index.ts @@ -1,9 +1,8 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; import parser from '@/utils/rss-parser'; -import { getArchive, getCategories, parseItem, parseList } from './utils'; +import { getArchive, parseItem, parseList, regionConfig } from './utils'; export const route: Route = { path: '/news/:region/:category?', @@ -30,10 +29,9 @@ export const route: Route = { maintainers: ['KeiLongW', 'williamgateszhao'], handler, url: 'news.yahoo.com/', - description: ` -\`Region\` + description: `\`Region\` -Support all regions represented by the asterisk (*) in *.news.yahoo.com, such as hk/tw/au/ca/fr/malaysia/nz/sg/uk/en(us). For www.yahoo.com, use en or us. Sites with news domains other than *.news.yahoo.com, such as de.nachrichten.yahoo.com or news.yahoo.co.jp, are not supported. +Support all regions represented by the asterisk (\\*) in \\*.news.yahoo.com, such as hk/tw/au/ca/fr/malaysia/nz/sg/uk/en(us). For [www.yahoo.com](http://www.yahoo.com), use en or us. Sites with news domains other than \\*.news.yahoo.com, such as de.nachrichten.yahoo.com or news.yahoo.co.jp, are not supported. \`Category\` @@ -41,17 +39,17 @@ The parsing method for Yahoo Hong Kong and Taiwan is quite unique. All supported Category for hk.news.yahoo.com (hongkong) -| 全部 | 港聞 | 兩岸國際 | 財經 | 娛樂 | 體育 | 健康 | 親子 | 副刊 | +| 全部 | 港聞 | 兩岸國際 | 財經 | 娛樂 | 體育 | 健康 | 親子 | 副刊 | | ------- | --------- | -------- | -------- | ------------- | ------ | ------ | --------- | ---------- | | (empty) | hong-kong | world | business | entertainment | sports | health | parenting | supplement | -Category for tw.news.yahoo.com (taiwan) +Category for tw\\.news.yahoo.com (taiwan) -| 全部 | 政治 | 財經 | 娛樂 | 運動 | 社會地方 | 國際 | 生活 | 健康 | 科技 | 品味 | -| ------- | -------- | ------- | ------------- | ------ | -------- | ----- | --------- | ------ | ---------- | ----- | -| (empty) | politics | finance | entertainment | sports | society | world | lifestyle | health | technology | style | +| 全部 | 政治 | 財經 | 娛樂 | 運動 | 社會地方 | 國際 | 生活 | 健康 | 科技 | +| ------- | -------- | ------- | ------------- | ------ | -------- | ----- | --------- | ------ | ---------- | +| (empty) | politics | finance | entertainment | sports | society | world | lifestyle | health | technology | -Other Yahoo news is fetched from the RSS provided by Yahoo. Please refer to the categories displayed on the pages of *.news.yahoo.com (for example, "world"), and try to access *.news.yahoo.com/rss/world to see if it is accessible and contains recent news (some categories exist but are not updated). If it is accessible and has recent news, then that category can be used on the corresponding site. For example, the available categories for news.yahoo.com are as follows +Other Yahoo news is fetched from the RSS provided by Yahoo. Please refer to the categories displayed on the pages of \\*.news.yahoo.com (for example, "world"), and try to access \\*.news.yahoo.com/rss/world to see if it is accessible and contains recent news (some categories exist but are not updated). If it is accessible and has recent news, then that category can be used on the corresponding site. For example, the available categories for news.yahoo.com are as follows Category for news.yahoo.com (US) @@ -65,48 +63,45 @@ To give another example, since uk.news.yahoo.com/rss/ukoriginal is accessible an For Yahoo Hong Kong and Yahoo Taiwan, please use another "news source" route. -For other Yahoo News, this route's RSS provides the author field. You can use RSSHub's built-in "content filtering" feature. For example, /yahoo-wg/news/tw/technology?filter_author=Yahoo%20Tech|Engadget can filter out news with authors containing Yahoo Tech or Engadget from Yahoo Taiwan's technology news, which is the Chinese version of Engadget. -`, +For other Yahoo News, this route's RSS provides the author field. You can use RSSHub's built-in "content filtering" feature. For example, /yahoo-wg/news/tw/technology?filter\\_author=Yahoo%20Tech|Engadget can filter out news with authors containing Yahoo Tech or Engadget from Yahoo Taiwan's technology news, which is the Chinese version of Engadget.`, zh: { name: '新闻', - description: ` -\`区域 Region\` + description: `\`区域 Region\` -支持所有 *.news.yahoo.com 中*号所代表的区域, 例如\`hk/tw/au/ca/fr/malaysia/nz/sg/uk/en(us)\`, 其中 www.yahoo.com 用 en 或 us 来表示。不支持新闻域名不为 *.news.yahoo.com 的站点如 de.nachrichten.yahoo.com 或 news.yahoo.co.jp。 +支持所有 *.news.yahoo.com 中*号所代表的区域,例如\`hk/tw/au/ca/fr/malaysia/nz/sg/uk/en(us)\`, 其中 [www.yahoo.com](http://www.yahoo.com) 用 en 或 us 来表示。不支持新闻域名不为 \\*.news.yahoo.com 的站点如 de.nachrichten.yahoo.com 或 news.yahoo.co.jp。 \`分类 Category\` -香港和台湾雅虎的读取方式比较特别, 所有支持的 category 如下 +香港和台湾雅虎的读取方式比较特别,所有支持的 category 如下 hk.news.yahoo.com (香港) 所支持的分类 -| 全部 | 港聞 | 兩岸國際 | 財經 | 娛樂 | 體育 | 健康 | 親子 | 副刊 | -| ------- | --------- | -------- | -------- | ------------- | ------ | ------ | --------- | ---------- | +| 全部 | 港聞 | 兩岸國際 | 財經 | 娛樂 | 體育 | 健康 | 親子 | 副刊 | +| -------- | --------- | -------- | -------- | ------------- | ------ | ------ | --------- | ---------- | | (留空) | hong-kong | world | business | entertainment | sports | health | parenting | supplement | -tw.news.yahoo.com (台湾) 所支持的分类 +tw\\.news.yahoo.com (台湾) 所支持的分类 -| 全部 | 政治 | 財經 | 娛樂 | 運動 | 社會地方 | 國際 | 生活 | 健康 | 科技 | 品味 | -| ------- | -------- | ------- | ------------- | ------ | -------- | ----- | --------- | ------ | ---------- | ----- | -| (留空) | politics | finance | entertainment | sports | society | world | lifestyle | health | technology | style | +| 全部 | 政治 | 財經 | 娛樂 | 運動 | 社會地方 | 國際 | 生活 | 健康 | 科技 | +| -------- | -------- | ------- | ------------- | ------ | -------- | ----- | --------- | ------ | ---------- | +| (留空) | politics | finance | entertainment | sports | society | world | lifestyle | health | technology | -其他雅虎新闻读取自 yahoo 提供的 RSS, 请根据 *.news.yahoo.com 的页面上展示的分类(例如 world ), 尝试 *.news.yahoo.com/rss/world 能否访问并且有近期的新闻(有些分类存在但未更新), 如果可以的话则该分类可以用在相应站点, 例如 news.yahoo.com 可用的分类如下 +其他雅虎新闻读取自 yahoo 提供的 RSS, 请根据 \\*.news.yahoo.com 的页面上展示的分类 (例如 world), 尝试 \\*.news.yahoo.com/rss/world 能否访问并且有近期的新闻 (有些分类存在但未更新), 如果可以的话则该分类可以用在相应站点,例如 news.yahoo.com 可用的分类如下 news.yahoo.com (美国) 所支持的分类 -| All | US | Politics | World | Science | Tech | -| ------- | -- | -------- | ----- | ------- | ---- | -| (留空) | us | politics | world | science | tech | +| All | US | Politics | World | Science | Tech | +| ------ | -- | -------- | ----- | ------- | ---- | +| (留空) | us | politics | world | science | tech | -再举例, 由于 uk.news.yahoo.com/rss/ukoriginal 可以访问并且有较新的新闻, 所以 /yahoo/news/uk/ukoriginal 是一个有效的RSSHub路由。 +再举例,由于 uk.news.yahoo.com/rss/ukoriginal 可以访问并且有较新的新闻,所以 /yahoo/news/uk/ukoriginal 是一个有效的 RSSHub 路由。 \`作者 author\` -对于香港和台湾雅虎, 请使用另一个"新聞來源"路由。 +对于香港和台湾雅虎,请使用另一个 "新聞來源" 路由。 -对于其他雅虎新闻, 本路由的 RSS 中提供了 author 字段, 可使用 RSSHub 的内置"内容过滤"功能, 例如 /yahoo-wg/news/tw/technology?filter_author=Yahoo%20Tech|Engadget 可从台湾雅虎的科技新闻中过滤出作者名称中包含 Yahoo Tech 或者 Engadget 的新闻, 即瘾科技中文版。 -`, +对于其他雅虎新闻,本路由的 RSS 中提供了 author 字段,可使用 RSSHub 的内置 "内容过滤" 功能,例如 /yahoo-wg/news/tw/technology?filter\\_author=Yahoo%20Tech|Engadget 可从台湾雅虎的科技新闻中过滤出作者名称中包含 Yahoo Tech 或者 Engadget 的新闻,即瘾科技中文版。`, }, }; @@ -120,13 +115,16 @@ async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; if (['hk', 'tw'].includes(region)) { - const categoryMap = await getCategories(region, cache.tryGet); - const tag = category ? categoryMap[category].yctMap : null; + const { categoryMap } = regionConfig[region]; + if (category && !categoryMap[category]) { + throw new InvalidParameterError(`Unknown category for ${region}: ${category}`); + } + const tags = category ? categoryMap[category].tags : undefined; - const response = await getArchive(region, limit, tag); + const response = await getArchive(region, limit, tags); const list = parseList(region, response); - const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); + const items = await Promise.all(list.map((item) => parseItem(item))); return { title: `Yahoo 新聞 ${region.toUpperCase()} - ${category ? categoryMap[category].name : '所有類別'}`, @@ -138,7 +136,7 @@ async function handler(ctx) { const rssUrl = `https://${region ? `${region}.` : ''}news.yahoo.com/rss/${category ? `${category}/` : ''}`; const feed = await parser.parseURL(rssUrl); const filteredItems = feed.items.filter((item) => item?.link && !item.link.includes('promotions') && new URL(item.link).hostname.match(/.*\.yahoo\.com$/)); - const items = await Promise.all(filteredItems.map((item) => parseItem(item, cache.tryGet))); + const items = await Promise.all(filteredItems.map((item) => parseItem(item))); return { title: `Yahoo News ${region.toUpperCase()} - ${category ? category.toUpperCase() : 'All'}`, diff --git a/lib/routes/yahoo/news/listid.ts b/lib/routes/yahoo/news/listid.ts index 074d68b4419c..567a4c8d041c 100644 --- a/lib/routes/yahoo/news/listid.ts +++ b/lib/routes/yahoo/news/listid.ts @@ -1,6 +1,5 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; import { getList, parseItem, parseList } from './utils'; @@ -28,18 +27,17 @@ export const route: Route = { name: '合作媒體', maintainers: ['TonyRL', 'williamgateszhao', 'tpnonthealps'], handler, - description: ` -| 合作媒體 (\`HK\`) | \`:listId\` | -| ----------------- | ---------------------------------------- | -| 東方日報 | \`33ddd580-0ab3-11e8-bfe1-4b555fb1e429\` | -| now.com | \`01b4d760-0ab4-11e8-af3a-54037d3dced3\` | -| am730 | \`c4842090-0ab2-11e8-af7f-041a72ce7398\` | -| BBC | \`4d3fc9a0-fac8-11e9-87f2-564ca250983e\` | -| 信報財經新聞 | \`5a8a0aa0-0ab3-11e8-b3dc-d990c79d6cb1\` | -| 香港電台 | \`b4bfc2d0-0ab3-11e8-bf9f-c888fc09923f\` | -| 法新社 | \`1cc44280-facb-11e9-ad7c-f3ba971275c8\` | -| Bloomberg | \`40023670-facc-11e9-9dde-9175ff306602\` | -| 香港動物報 | \`6058fa9c-d74d-487a-8b49-aa99a2a2978e\` |`, + description: `| 合作媒體 (\`HK\`) | \`:listId\` | +| --------------- | -------------------------------------- | +| 東方日報 | \`33ddd580-0ab3-11e8-bfe1-4b555fb1e429\` | +| now\\.com | \`01b4d760-0ab4-11e8-af3a-54037d3dced3\` | +| am730 | \`c4842090-0ab2-11e8-af7f-041a72ce7398\` | +| BBC | \`4d3fc9a0-fac8-11e9-87f2-564ca250983e\` | +| 信報財經新聞 | \`5a8a0aa0-0ab3-11e8-b3dc-d990c79d6cb1\` | +| 香港電台 | \`b4bfc2d0-0ab3-11e8-bf9f-c888fc09923f\` | +| 法新社 | \`1cc44280-facb-11e9-ad7c-f3ba971275c8\` | +| Bloomberg | \`40023670-facc-11e9-9dde-9175ff306602\` | +| 香港動物報 | \`6058fa9c-d74d-487a-8b49-aa99a2a2978e\` |`, }; async function handler(ctx) { @@ -55,11 +53,11 @@ async function handler(ctx) { // console.log('Is response an array?', Array.isArray(response.stream_items)); const list = parseList(region, response.stream_items); - const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); + const items = await Promise.all(list.map((item) => parseItem(item))); const author = items[0].author; - const atIndex = author.indexOf('@'); // fing '@' - const source = atIndex === -1 ? author : author.slice(atIndex + 1).trim(); + const atIndex = author?.indexOf('@'); // fing '@' + const source = atIndex === -1 ? author : author?.slice(atIndex + 1).trim(); // console.log(source); return { diff --git a/lib/routes/yahoo/news/provider-helper.ts b/lib/routes/yahoo/news/provider-helper.ts index 13d4fd3de885..d02bfc31945f 100644 --- a/lib/routes/yahoo/news/provider-helper.ts +++ b/lib/routes/yahoo/news/provider-helper.ts @@ -1,6 +1,5 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; import { getProviderList } from './utils'; @@ -36,7 +35,7 @@ async function handler(ctx) { throw new InvalidParameterError(`Unknown region: ${region}`); } - const providerList = await getProviderList(region, cache.tryGet); + const providerList = await getProviderList(region); const items = providerList.map((provider) => ({ ...provider, diff --git a/lib/routes/yahoo/news/provider.ts b/lib/routes/yahoo/news/provider.ts index 8e4010ddda73..64cc42563f1c 100644 --- a/lib/routes/yahoo/news/provider.ts +++ b/lib/routes/yahoo/news/provider.ts @@ -1,6 +1,5 @@ import InvalidParameterError from '@/errors/types/invalid-parameter'; import type { Route } from '@/types'; -import cache from '@/utils/cache'; import { getArchive, getProviderList, parseItem, parseList } from './utils'; @@ -28,8 +27,7 @@ export const route: Route = { name: '新聞來源', maintainers: ['TonyRL', 'williamgateszhao'], handler, - description: ` -\`Region\` + description: `\`Region\` | 香港 | 台灣 | | ---- | ---- | @@ -37,10 +35,9 @@ export const route: Route = { \`ProviderId\` -除了可以通过路由"新聞來源列表"获得外, 也可通过 hk.news.yahoo.com/archive 和 tw.news.yahoo.com/archive 选择"新闻来源"后通过页面 Url 来获得。 +除了可以通过路由 "新聞來源列表" 获得外,也可通过 hk.news.yahoo.com/archive 和 tw\\.news.yahoo.com/archive 选择 "新闻来源" 后通过页面 Url 来获得。 -例如 hk.news.yahoo.com/yahoo_movies_hk_660--所有分類/archive, \`yahoo_movies_hk_660\` 就是 ProviderId 。 -`, +例如 hk.news.yahoo.com/yahoo\\_movies\\_hk\\_660-- 所有分類 /archive, \`yahoo_movies_hk_660\` 就是 ProviderId 。`, }; async function handler(ctx) { @@ -50,13 +47,13 @@ async function handler(ctx) { } const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; - const providerList = await getProviderList(region, cache.tryGet); + const providerList = await getProviderList(region); const provider = providerList.find((p) => p.key === providerId); - const response = await getArchive(region, limit, null, providerId); + const response = await getArchive(region, limit, [], providerId); const list = parseList(region, response); - const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); + const items = await Promise.all(list.map((item) => parseItem(item))); return { title: `Yahoo 新聞 - ${provider?.title ?? ''}`, diff --git a/lib/routes/yahoo/news/utils.tsx b/lib/routes/yahoo/news/utils.tsx index d735c3a61a58..474c5cf9dcd9 100644 --- a/lib/routes/yahoo/news/utils.tsx +++ b/lib/routes/yahoo/news/utils.tsx @@ -2,24 +2,67 @@ import { load } from 'cheerio'; import { renderToString } from 'hono/jsx/dom/server'; import { config } from '@/config'; +import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -const getArchive = async (region, limit, tag, providerId?) => { - const { data: response } = await got( - `https://${region}.news.yahoo.com/_td-news/api/resource/NCPListService;api=archive;ncpParams=${encodeURIComponent( - JSON.stringify({ - query: { - count: limit, - // imageSizes: '220x128', - start: 0, - providerid: providerId, - tag, - }, - }) - )}` - ); - return response; +const regionConfig = { + hk: { + spaceId: '2143854493', + lang: 'zh-Hant-HK', + categoryMap: { + business: { name: '財經', tags: ['yct:001000298', 'yct:001000123', 'yct:001000721'] }, + entertainment: { name: '娛樂', tags: ['yct:001000031'] }, + health: { name: '健康', tags: ['yct:001000395'] }, + 'hong-kong': { name: '港聞', tags: ['yct:001000661'] }, + parenting: { name: '親子', tags: ['yct:001000267'] }, + sports: { name: '體育', tags: ['yct:001000001'] }, + supplement: { name: '副刊', tags: ['yct:001000560', 'yct:001000780', 'yct:001000931', 'yct:001001039', 'yct:001000374'] }, + world: { name: '兩岸國際', tags: ['yct:001000680'] }, + }, + }, + tw: { + spaceId: '2144446726', + lang: 'zh-Hant-TW', + categoryMap: { + entertainment: { name: '娛樂', tags: ['yct:001000031'] }, + finance: { name: '財經', tags: ['yct:001000298', 'yct:001000123'] }, + health: { name: '健康', tags: ['yct:001000395'] }, + lifestyle: { name: '生活', tags: ['ymedia:category=000000126', 'yct:001000560', 'yct:001000374', 'yct:001001117', 'yct:001000659', 'yct:001000616'] }, + politics: { name: '政治', tags: ['yct:001000661'] }, + society: { name: '社會地方', tags: ['ymedia:category=000000179', 'yct:001000798', 'yct:001000667'] }, + sports: { name: '運動', tags: ['yct:001000001'] }, + technology: { name: '科技', tags: ['yct:001000931', 'yct:001000742', 'ymedia:category=000000175'] }, + world: { name: '國際', tags: ['ymedia:category=000000030', 'ymedia:category=000000032'] }, + }, + }, +}; + +const getArchive = async (region, limit, tags?: string[], providerId?) => { + const { spaceId, lang } = regionConfig[region]; + const params = new URLSearchParams({ + count: limit, + device: 'desktop', + documentType: 'article,video', + id: 'search', + lang, + namespace: 'news', + region: region.toUpperCase(), + site: 'news', + start: '0', + version: 'v1', + imageSizes: '498x280,100x100', + providerid: providerId ?? '', + spaceId, + }); + if (tags) { + for (const tag of tags) { + params.append('tag', tag); + } + } + + const { data: response } = await got(`https://tw-gw-news.media.yahoo.com/api/v1/gql/saved_query?${params.toString()}`); + return response.data.stream.contents; }; const getList = async (region, listId) => { @@ -27,9 +70,9 @@ const getList = async (region, listId) => { return response; }; -const getCategories = (region, tryGet) => - tryGet(`yahoo:${region}:categoryMap`, async () => { - const { PageStore } = await getStores(region, tryGet); +const getCategories = (region) => + cache.tryGet(`yahoo:${region}:categoryMap`, async () => { + const { PageStore } = await getStores(region); const { Col1: col1 } = PageStore.pagesConfigRaw.base.section.regions; const { categoryMap } = col1.find((c) => c.name === 'ArchiveFilterBar').props; @@ -43,43 +86,59 @@ const getCategories = (region, tryGet) => return categoryMap; }); -const getProviderList = (region, tryGet) => - tryGet(`yahoo:${region}:providerList`, async () => { - const { ProviderListStore } = await getStores(region, tryGet); +const getProviderList = async (region) => { + const stores = await getStores(region); + return stores.providerList.map((provider) => ({ + title: provider.title, + key: provider.key, + link: new URL(`${provider.key}--所有類別/archive`, `https://${region}.news.yahoo.com`).href, + })); +}; - return ProviderListStore.providerList.flatMap((list) => - list.providers.map((provider) => ({ - title: `${list.title} - ${provider.title}`, - key: provider.key, - link: new URL(provider.url, `https://${region}.news.yahoo.com`).href, - })) - ); - }); +const findStoresObject = (node) => { + if (!node || typeof node !== 'object') { + return null; + } + if ('breakingNews' in node) { + return node; + } + for (const value of Object.values(node)) { + const found = findStoresObject(value); + if (found) { + return found; + } + } + return null; +}; -const getStores = (region, tryGet) => - tryGet(`yahoo:${region}:stores`, async () => { +const getStores = (region) => + cache.tryGet(`yahoo:${region}:stores`, async () => { const { data: response } = await got(`https://${region}.news.yahoo.com/archive`); const $ = load(response); - const appData = JSON.parse( - $('script:contains("root.App.main")') - .text() - .match(/root.App.main\s+=\s+({.+});/)?.[1] as string - ); + const script = $('script:contains("pageBenjiConfig")').text(); + const rscText = script.match(/self\.__next_f\.push\(\[1,"\d:(.*)"\]\)/)?.[1]; + const rscData = JSON.parse(JSON.parse(`"${rscText}"`)); + + const stores = findStoresObject(rscData); + if (!stores) { + throw new Error(`Unable to locate stores data for region ${region}`); + } - return appData.context.dispatcher.stores; + return stores; }); const parseList = (region, response) => response.map((item) => ({ title: item.title, - link: item.url.startsWith('http') ? item.url : new URL(item.url, `https://${region}.news.yahoo.com`).href, + link: item.canonicalUrl ? item.canonicalUrl.url : item.url.startsWith('http') ? item.url : new URL(item.url, `https://${region}.news.yahoo.com`).href, description: item.summary, - pubDate: parseDate(item.published_at, 'X'), + pubDate: item.published_at ? parseDate(item.published_at, 'X') : item.pubDate ? parseDate(item.pubDate) : undefined, + author: item.provider_name ?? item.provider?.displayName ?? item.publisher, })); -const parseItem = (item, tryGet) => - tryGet(item.link, async () => { +const parseItem = (item) => + cache.tryGet(item.link, async () => { const { data: response } = await got(item.link, { headers: { 'User-Agent': config.trueUA, @@ -90,10 +149,10 @@ const parseItem = (item, tryGet) => const ldJson = JSON.parse( $('script[type="application/ld+json"]') .toArray() - .find((ele) => $(ele).text().includes('"@type":"NewsArticle"'))?.children[0].data + .find((ele) => $(ele).text().includes('"@type":"NewsArticle"'))?.children[0].data || '{}' ); - const author = ldJson.author.name; - const body = $('.atoms'); + const author = ldJson.author?.name; + const body = $('.atoms').length ? $('.atoms') : $('.article-detail').length ? $('.article-detail') : $('.bodyItems-wrapper'); body.find('noscript, .recommendation-contents, .text-gandalf, [id^="sda-inbody-"]').remove(); // remove padding @@ -140,12 +199,12 @@ const parseItem = (item, tryGet) => .toArray() .map((ele) => $(ele).html()) .join(''); - item.author = author; + item.author = author ?? item.author; item.category = ldJson.keywords; - item.pubDate = parseDate(ldJson.datePublished); - item.updated = parseDate(ldJson.dateModified); + item.pubDate = ldJson.datePublished ? parseDate(ldJson.datePublished) : item.pubDate; + item.updated = ldJson.dateModified ? parseDate(ldJson.dateModified) : item.updated; return item; }); -export { getArchive, getCategories, getList, getProviderList, getStores, parseItem, parseList }; +export { getArchive, getCategories, getList, getProviderList, getStores, parseItem, parseList, regionConfig }; diff --git a/lib/routes/yamibo/bbs/forum.ts b/lib/routes/yamibo/bbs/forum.ts index d873a0cec3ef..c2755afec3c2 100644 --- a/lib/routes/yamibo/bbs/forum.ts +++ b/lib/routes/yamibo/bbs/forum.ts @@ -38,7 +38,7 @@ export const route: Route = { ], }, description: `::: warning -百合会BBS访问部分板块需要用户登录认证,请参考配置说明 +百合会 BBS 访问部分板块需要用户登录认证,请参考配置说明 :::`, }; diff --git a/lib/routes/yamibo/bbs/thread.ts b/lib/routes/yamibo/bbs/thread.ts index 8ec70b2ea6c2..042e740a59c5 100644 --- a/lib/routes/yamibo/bbs/thread.ts +++ b/lib/routes/yamibo/bbs/thread.ts @@ -33,7 +33,7 @@ export const route: Route = { ], }, description: `::: warning -百合会BBS访问部分讨论串需要用户登录认证,请参考配置说明 +百合会 BBS 访问部分讨论串需要用户登录认证,请参考配置说明 :::`, }; diff --git a/lib/routes/yande/namespace.ts b/lib/routes/yande/namespace.ts index 83371c10c93b..f6511722b0e5 100644 --- a/lib/routes/yande/namespace.ts +++ b/lib/routes/yande/namespace.ts @@ -3,6 +3,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'yande.re', url: 'yande.re', - description: `yande post`, + description: 'yande post', lang: 'en', }; diff --git a/lib/routes/yande/post.ts b/lib/routes/yande/post.ts index 85c6797b1adc..aba7807912ce 100644 --- a/lib/routes/yande/post.ts +++ b/lib/routes/yande/post.ts @@ -28,9 +28,9 @@ export const route: Route = { ], name: 'Popular Recent Posts', maintainers: ['magic-akari', 'SettingDust', 'fashioncj', 'NekoAria'], - description: `| 最近 24 小时 | 最近一周 | 最近一月 | 最近一年 | -| ------- | -------- | ------- | -------- | -| 1d | 1w | 1m | 1y |`, + description: `| 最近 24 小时 | 最近一周 | 最近一月 | 最近一年 | +| ------------ | -------- | -------- | -------- | +| 1d | 1w | 1m | 1y |`, handler, features: { nsfw: true, diff --git a/lib/routes/ycwb/index.tsx b/lib/routes/ycwb/index.tsx index ad3ae24e7dd3..283c17f13706 100644 --- a/lib/routes/ycwb/index.tsx +++ b/lib/routes/ycwb/index.tsx @@ -25,7 +25,7 @@ export const route: Route = { handler, description: `注:小部分栏目的 URL 会给出 nodeid。如未给出,可打开某条新闻链接后,查看网页源代码,搜索 nodeid 的值。 - 常用栏目节点: +常用栏目节点: | 首页 | 中国 | 国际 | 体育 | 要闻 | 珠江评论 | 民生观察 | 房产 | 金羊教育 | 金羊财富 | 金羊文化 | 金羊健康 | 金羊汽车 | | ---- | ---- | ---- | ---- | ---- | -------- | -------- | ---- | -------- | -------- | -------- | -------- | -------- | diff --git a/lib/routes/yicai/feed.ts b/lib/routes/yicai/feed.ts index c81f1c8c4c5f..fcbe10a65dc8 100644 --- a/lib/routes/yicai/feed.ts +++ b/lib/routes/yicai/feed.ts @@ -29,7 +29,7 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 全部主题词见 [此处](https://www.yicai.com/feed/alltheme) +全部主题词见 [此处](https://www.yicai.com/feed/alltheme) :::`, }; diff --git a/lib/routes/yicai/news.ts b/lib/routes/yicai/news.ts index 019e7edd6dab..a2a4a35b313c 100644 --- a/lib/routes/yicai/news.ts +++ b/lib/routes/yicai/news.ts @@ -42,7 +42,7 @@ export const route: Route = { | books | 阅读周刊 | | loushi | 地产 | | automobile | 汽车 | -| china_financial_herald | 对话陆家嘴 | +| china\\_financial\\_herald | 对话陆家嘴 | | fashion | 时尚 | | ad | 商业资讯 | | info | 资讯 | diff --git a/lib/routes/yicai/utils.ts b/lib/routes/yicai/utils.ts index 2623c800881a..ed148f1b8c5a 100644 --- a/lib/routes/yicai/utils.ts +++ b/lib/routes/yicai/utils.ts @@ -16,7 +16,7 @@ const ProcessItems = async (apiUrl, tryGet) => { const items = response.data.map((item) => ({ title: item.NewsTitle, - link: item.url.startsWith('http') ? item.url : `${rootUrl}${item.AppID === 0 ? `/vip` : ''}${item.url}`, + link: item.url.startsWith('http') ? item.url : `${rootUrl}${item.AppID === 0 ? '/vip' : ''}${item.url}`, author: item.NewsAuthor || item.NewsSource || item.CreaterName, pubDate: timezone(parseDate(item.CreateDate), +8), category: [item.ChannelName], diff --git a/lib/routes/yicai/video.ts b/lib/routes/yicai/video.ts index 5169a146ebab..c7c44770b732 100644 --- a/lib/routes/yicai/video.ts +++ b/lib/routes/yicai/video.ts @@ -58,7 +58,7 @@ export const route: Route = { | diyishengyin | 第一声音 | | sanliangboqianjin | 财智双全 | | weilaiyaoqinghan | 未来邀请函 | -| zjdy | 主角 ▪ 大医 | +| zjdy | 主角 ▪ 大医 | | leye | 乐业之城 | | sanrenxing | 价值三人行 | | yuandongli | 中国源动力 | diff --git a/lib/routes/ymgal/game.tsx b/lib/routes/ymgal/game.tsx index eb09deb7d09f..3efecc38d1bd 100644 --- a/lib/routes/ymgal/game.tsx +++ b/lib/routes/ymgal/game.tsx @@ -59,7 +59,7 @@ async function handler() { }); return { - title: `月幕 Galgame - 本月新作`, + title: '月幕 Galgame - 本月新作', link: `${host}/release-list/${year}/${month}`, description: '月幕 Galgame - 本月新作', item: items, diff --git a/lib/routes/yna/index.ts b/lib/routes/yna/index.ts index 48abb34732d8..609953f7f447 100644 --- a/lib/routes/yna/index.ts +++ b/lib/routes/yna/index.ts @@ -26,12 +26,12 @@ export const route: Route = { name: 'News', maintainers: ['quiniapiezoelectricity'], handler, - description: ` -| Language | 한국어 | English | 简体中文 | 日本語 | عربي | Español | Français | -| --------- | ------ | ------- | -------- | ------ | ------ | ------- | -------- | -| \`:lang\` | \`ko\` | \`en\` | \`cn\` | \`jp\` | \`ar\` | \`es\` | \`fr\` | + description: `| Language | 한국어 | English | 简体中文 | 日本語 | عربي | Español | Français | +| -------- | ------ | ------- | -------- | ------ | ---- | ------- | -------- | +| \`:lang\` | \`ko\` | \`en\` | \`cn\` | \`jp\` | \`ar\` | \`es\` | \`fr\` | For a full list of RSS Feed Channels, please refer to the RSS feed page of the corresponding language + | RSS Feed Page | | --------------------------------------------------------- | | [한국어](https://www.yna.co.kr/rss/index?site=footer_rss) | @@ -43,9 +43,8 @@ For a full list of RSS Feed Channels, please refer to the RSS feed page of the c | [Français](https://fr.yna.co.kr/channel/index) | ::: tip -For example, the path for the RSS feed url https://www.yna.co.kr/rss/economy.xml and https://cn.yna.co.kr/RSS/news.xml would be \`/ko/economy\` and \`/cn/news\` respectively. -::: -`, +For example, the path for the RSS feed url and would be \`/ko/economy\` and \`/cn/news\` respectively. +:::`, }; async function handler(ctx) { diff --git a/lib/routes/ynet/list.ts b/lib/routes/ynet/list.ts index fb92d0ed3577..2cbd8cf018cc 100644 --- a/lib/routes/ynet/list.ts +++ b/lib/routes/ynet/list.ts @@ -70,7 +70,7 @@ export const handler = async (ctx: Context): Promise => { const $aEl: Cheerio = $el.find('h2 a'); const title: string = $aEl.text(); - const pubDateStr: string | undefined = $el.find('em.fRight').text() || undefined; + const pubDateStr: string | undefined = $el.find('em.fRight').text(); const linkUrl: string | undefined = $aEl.attr('href'); const upDatedStr: string | undefined = pubDateStr; @@ -153,10 +153,9 @@ export const route: Route = { description: '列表 ID,可在对应列表页 URL 中找到', }, }, - description: `:::tip + description: `::: tip 订阅 [北青快讯](https://news.ynet.com/list/2121t76.html),其源网址为 \`https://news.ynet.com/list/2121t76.html\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/ynet/list/news/2121t76\`](https://rsshub.app/ynet/list/news/2121t76)。 -::: -`, +:::`, categories: ['new-media'], features: { requireConfig: false, diff --git a/lib/routes/youmemark/index.ts b/lib/routes/youmemark/index.ts index 1d272abbd004..84111ca4a2da 100644 --- a/lib/routes/youmemark/index.ts +++ b/lib/routes/youmemark/index.ts @@ -28,7 +28,7 @@ export const route: Route = { ], description: `Get user's public bookmarks from YouMeMark ::: tip - Add \`?limit=\` to the end of the route to limit the number of items. Default is 10. +Add \`?limit=\` to the end of the route to limit the number of items. Default is 10. :::`, }; diff --git a/lib/routes/youtube/channel.ts b/lib/routes/youtube/channel.ts index fa53b06c6d87..4d19db7c347b 100644 --- a/lib/routes/youtube/channel.ts +++ b/lib/routes/youtube/channel.ts @@ -23,14 +23,16 @@ export const route: Route = { maintainers: ['DIYgod', 'pseudoyu'], handler, description: `::: tip Parameter -| Name | Description | Default | -| ---------- | ----------------------------------------------------------------------------------- | ------- | -| embed | Whether to embed the video, fill in any value to disable embedding | embed | + +| Name | Description | Default | +| ------------ | ---------------------------------------------------------------------------------- | ------- | +| embed | Whether to embed the video, fill in any value to disable embedding | embed | | filterShorts | Whether to filter out shorts from the feed, fill in any falsy value to show shorts | true | + ::: ::: tip -YouTube provides official RSS feeds for channels, for instance [https://www.youtube.com/feeds/videos.xml?channel_id=UCDwDMPOZfxVV0x_dz0eQ8KQ](https://www.youtube.com/feeds/videos.xml?channel_id=UCDwDMPOZfxVV0x_dz0eQ8KQ). +YouTube provides official RSS feeds for channels, for instance . :::`, features: { requireConfig: [ @@ -63,7 +65,7 @@ async function handler(ctx) { const filterShorts = filterShortsStr === null || filterShortsStr === '' || filterShortsStr === 'true'; if (!utils.isYouTubeChannelId(id)) { - throw new InvalidParameterError(`Invalid YouTube channel ID. \nYou may want to use /youtube/user/:id instead.`); + throw new InvalidParameterError('Invalid YouTube channel ID. \nYou may want to use /youtube/user/:id instead.'); } const isJsonFeed = ctx.req.query('format') === 'json'; diff --git a/lib/routes/youtube/charts.ts b/lib/routes/youtube/charts.ts index 207041375123..0c81b6f87511 100644 --- a/lib/routes/youtube/charts.ts +++ b/lib/routes/youtube/charts.ts @@ -19,7 +19,7 @@ export const route: Route = { | ----------- | --------- | ---------------- | -------------- | | TopArtists | TopSongs | TopVideos | TrendingVideos | - Country Code +Country Code | Argentina | Australia | Austria | Belgium | Bolivia | Brazil | Canada | | --------- | --------- | ------- | ------- | ------- | ------ | ------ | diff --git a/lib/routes/youtube/user.ts b/lib/routes/youtube/user.ts index a98ca2f143f5..3787ef04a97e 100644 --- a/lib/routes/youtube/user.ts +++ b/lib/routes/youtube/user.ts @@ -15,10 +15,12 @@ export const route: Route = { routeParams: 'Extra parameters, see the table below', }, description: `::: tip Parameter -| Name | Description | Default | -| ---------- | ----------------------------------------------------------------------------------- | ------- | -| embed | Whether to embed the video, fill in any value to disable embedding | embed | + +| Name | Description | Default | +| ------------ | ---------------------------------------------------------------------------------- | ------- | +| embed | Whether to embed the video, fill in any value to disable embedding | embed | | filterShorts | Whether to filter out shorts from the feed, fill in any falsy value to show shorts | true | + :::`, features: { requireConfig: [ diff --git a/lib/routes/youtube/utils.tsx b/lib/routes/youtube/utils.tsx index 4cb4c3171cdc..563aa3dd99b6 100644 --- a/lib/routes/youtube/utils.tsx +++ b/lib/routes/youtube/utils.tsx @@ -118,17 +118,22 @@ export async function getSubscriptionsRecusive(part, nextPageToken?) { // taken from https://webapps.stackexchange.com/a/101153 export const isYouTubeChannelId = (id) => /^UC[\w-]{21}[AQgw]$/.test(id); export const getLive = (id, cache) => - cache.tryGet(`youtube:getLive:${id}`, async () => { - const res = await exec((youtube) => - youtube.search.list({ - part: 'snippet', - channelId: id, - eventType: 'live', - type: 'video', - }) - ); - return res; - }); + cache.tryGet( + `youtube:getLive:${id}`, + async () => { + const res = await exec((youtube) => + youtube.search.list({ + part: 'snippet', + channelId: id, + eventType: 'live', + type: 'video', + }) + ); + return res; + }, + config.cache.routeExpire, + false + ); export const getVideoUrl = (id: string) => `https://www.youtube-nocookie.com/embed/${id}?controls=1&autoplay=1&mute=0`; // Get the appropriate playlist ID with or without shorts diff --git a/lib/routes/yuanliao/index.ts b/lib/routes/yuanliao/index.ts index 3e869c9ef4ed..170e4bb94d23 100644 --- a/lib/routes/yuanliao/index.ts +++ b/lib/routes/yuanliao/index.ts @@ -153,8 +153,7 @@ export const route: Route = { | [意见建议](https://yuanliao.info/t/suggestions) | [suggestions](https://rsshub.app/yuanliao/suggestions) | | [插件发布](https://yuanliao.info/t/plugins) | [plugins](https://rsshub.app/yuanliao/plugins) | | [插件需求](https://yuanliao.info/t/plugin-needs) | [plugin-needs](https://rsshub.app/yuanliao/plugin-needs) | -| [开发者](https://yuanliao.info/t/developers) | [developers](https://rsshub.app/yuanliao/developers) | -`, +| [开发者](https://yuanliao.info/t/developers) | [developers](https://rsshub.app/yuanliao/developers) |`, categories: ['bbs'], features: { requireConfig: false, diff --git a/lib/routes/yxdzqb/index.tsx b/lib/routes/yxdzqb/index.tsx index 463a1c1be8a5..7d2a9bae9a20 100644 --- a/lib/routes/yxdzqb/index.tsx +++ b/lib/routes/yxdzqb/index.tsx @@ -39,7 +39,7 @@ export const route: Route = { url: 'yxdzqb.com/', description: `| Steam 最新折扣 | Steam 热门游戏折扣 | Steam 热门中文游戏折扣 | Steam 历史低价 | Steam 中文游戏历史低价 | | -------------- | ------------------ | ---------------------- | -------------- | ---------------------- | -| discount | popular | popular_cn | low | low_cn |`, +| discount | popular | popular\\_cn | low | low\\_cn |`, }; async function handler(ctx) { diff --git a/lib/routes/yyets/article.ts b/lib/routes/yyets/article.ts index f4de56910fe5..d919cab0bcc9 100644 --- a/lib/routes/yyets/article.ts +++ b/lib/routes/yyets/article.ts @@ -42,7 +42,7 @@ export const route: Route = { handler, description: `| 全部 | 影视资讯 | 收视快报 | 人人影评 | 人人剧评 | 新剧评测 | 片单推荐 | | ---- | -------- | -------- | --------- | --------- | ----------- | -------- | -| | news | report | m_review | t_review | new_review | recom |`, +| | news | report | m\\_review | t\\_review | new\\_review | recom |`, }; async function handler(ctx) { diff --git a/lib/routes/yystv/docs.ts b/lib/routes/yystv/docs.ts index 836dfe958a00..7912ff20badc 100644 --- a/lib/routes/yystv/docs.ts +++ b/lib/routes/yystv/docs.ts @@ -31,7 +31,7 @@ export const route: Route = { }; async function handler() { - const url = `https://www.yystv.cn/docs`; + const url = 'https://www.yystv.cn/docs'; const response = await ofetch(url); const $ = load(response); @@ -62,7 +62,7 @@ async function handler() { return { title: '游研社-' + $('title').text(), - link: `https://www.yystv.cn/docs`, + link: 'https://www.yystv.cn/docs', item: items, }; } diff --git a/lib/routes/zagg/new-arrivals.tsx b/lib/routes/zagg/new-arrivals.tsx index b3fe344d74c2..94659cacaa24 100644 --- a/lib/routes/zagg/new-arrivals.tsx +++ b/lib/routes/zagg/new-arrivals.tsx @@ -21,7 +21,7 @@ export const route: Route = { name: 'New Arrivals', maintainers: ['EthanWng97'], 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\``, + 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`', }; async function handler(ctx) { diff --git a/lib/routes/zaimanhua/comic.ts b/lib/routes/zaimanhua/comic.ts index 227f1e400f11..752525fab48e 100644 --- a/lib/routes/zaimanhua/comic.ts +++ b/lib/routes/zaimanhua/comic.ts @@ -12,9 +12,15 @@ export const route: Route = { path: '/comic/:id', categories: ['anime'], parameters: { id: '漫画ID' }, - example: '/zaimanhua/comic/14488', + example: '/zaimanhua/comic/57069', features: { - requireConfig: false, + requireConfig: [ + { + name: 'ZAIMANHUA_TOKEN', + optional: true, + description: '用户登录后,可以从浏览器开发者工具 Network 面板中的请求信息中获取 token,使用请求中的 `Authorization` 的值,完整设置为 `Bearer `,或直接设置 token 并由路由自动补齐 `Bearer ` 前缀。', + }, + ], requirePuppeteer: false, antiCrawler: false, supportBT: false, @@ -30,6 +36,10 @@ export const route: Route = { ], name: '漫画更新', maintainers: ['kjasn'], + description: `::: Warning +未登录用户无法获取到所有漫画,需要设置\`ZAIMANHUA_TOKEN\`环境变量以使用 API 授权访问。 +且由于源网站本身的限制,建议尽量在部署于中国大陆网络内的 RSSHub 节点中使用本路由。若在海外网络环境中使用,即使设置了\`ZAIMANHUA_TOKEN\`环境变量,也可能无法获取全部漫画。 +:::`, handler, }; @@ -38,12 +48,17 @@ async function handler(ctx) { const id = ctx.req.param('id'); const currentComicUrl = `${baseUrl}/api/v1/comic2/comic/detail?id=${id}`; - const response = await ofetch(currentComicUrl, { - headers: { - 'user-agent': config.trueUA, - referer: baseUrl, - }, - }); + const headers: Record = { + 'user-agent': config.trueUA, + referer: baseUrl, + }; + + const token = config.zaimanhua.token; + if (token) { + headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`; + } + + const response = await ofetch(currentComicUrl, { headers }); const comicInfo = response.data.comicInfo; const status = comicInfo.chapterList[0].title; // 更新状态 @@ -58,12 +73,7 @@ async function handler(ctx) { return await cache.tryGet(chapterUrl, async () => { // 获取章节内容 - const chapterResponse = await ofetch(chapterUrl, { - headers: { - 'user-agent': config.trueUA, - referer: baseUrl, - }, - }); + const chapterResponse = await ofetch(chapterUrl, { headers }); const chapterData = chapterResponse.data; const description = renderComic(chapterData.chapterInfo.page_url || []); diff --git a/lib/routes/zaimanhua/update.ts b/lib/routes/zaimanhua/update.ts index 32267ebb4649..fd166a1f1108 100644 --- a/lib/routes/zaimanhua/update.ts +++ b/lib/routes/zaimanhua/update.ts @@ -13,7 +13,13 @@ export const route: Route = { categories: ['anime'], example: '/zaimanhua/update', features: { - requireConfig: false, + requireConfig: [ + { + name: 'ZAIMANHUA_TOKEN', + optional: true, + description: '可从浏览器开发者工具中抓取站点请求头 `Authorization` 的 Bearer token,并配置为环境变量。可设置为完整值 `Bearer `,或仅设置 token 由路由自动补齐 `Bearer ` 前缀。', + }, + ], requirePuppeteer: false, antiCrawler: false, supportBT: false, @@ -29,15 +35,23 @@ export const route: Route = { ], name: '最近更新', maintainers: ['kjasn'], + description: `::: Warning +建议设置\`ZAIMANHUA_TOKEN\`环境变量以使用 API 授权访问。且由于源网站本身的限制,建议尽量在部署于中国大陆网络内的 RSSHub 节点中使用本路由。若在海外网络环境中使用,即使设置了\`ZAIMANHUA_TOKEN\`环境变量,也可能无法获取全部漫画。 +:::`, handler: async () => { const baseUrl = 'https://manhua.zaimanhua.com'; const currentUrl = `${baseUrl}/api/v1/comic2/update_list?status&theme&zone&cate&firstLetter&sortType&page=1&size=20`; + const headers: Record = { + 'user-agent': config.trueUA, + referer: baseUrl, + }; + const token = config.zaimanhua.token; + if (token) { + headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`; + } const response = await ofetch(currentUrl, { - headers: { - 'user-agent': config.trueUA, - referer: baseUrl, - }, + headers, }); // 近期更新漫画数据 @@ -53,12 +67,7 @@ export const route: Route = { return await cache.tryGet(chapterUrl, async () => { // 获取章节内容 - const chapterResponse = await ofetch(chapterUrl, { - headers: { - 'user-agent': config.trueUA, - referer: baseUrl, - }, - }); + const chapterResponse = await ofetch(chapterUrl, { headers }); const chapterData = chapterResponse.data; const description = renderComic(chapterData.chapterInfo.page_url || []); diff --git a/lib/routes/zaobao/other.ts b/lib/routes/zaobao/other.ts index 02b0aa05d8ed..941881164a2c 100644 --- a/lib/routes/zaobao/other.ts +++ b/lib/routes/zaobao/other.ts @@ -12,7 +12,7 @@ export const route: Route = { name: '其他栏目', maintainers: ['shunf4'], handler, - description: `除了上面两个兼容规则之外,联合早报网站里所有页面形如 [https://www.zaobao.com/lifestyle/health](https://www.zaobao.com/lifestyle/health) 这样的栏目都能被这个规则解析到,早报的大部分栏目都是这个样式的。你可以测试之后再订阅。`, + description: '除了上面两个兼容规则之外,联合早报网站里所有页面形如 这样的栏目都能被这个规则解析到,早报的大部分栏目都是这个样式的。你可以测试之后再订阅。', }; async function handler(ctx) { diff --git a/lib/routes/zaozao/article.ts b/lib/routes/zaozao/article.ts index 861b10a0ef8a..18887aa26598 100644 --- a/lib/routes/zaozao/article.ts +++ b/lib/routes/zaozao/article.ts @@ -35,7 +35,7 @@ async function handler(ctx) { method: 'put', url: `https://e.zaozao.run/article/page/${type}`, headers: { - Referer: `https://www.zaozao.run/`, + Referer: 'https://www.zaozao.run/', }, body: JSON.stringify({ pageNo: 1, @@ -51,9 +51,9 @@ async function handler(ctx) { const { data } = response.data; return { - title: `前端早早聊 - 文章`, + title: '前端早早聊 - 文章', link: `https://www.zaozao.run/article/${type}`, - description: `前端早早聊 - 文章`, + description: '前端早早聊 - 文章', item: data.map((item) => ({ title: item.title, link: item.url, diff --git a/lib/routes/zcool/discover.ts b/lib/routes/zcool/discover.ts index 28fad2bcda24..46b6b2e408d8 100644 --- a/lib/routes/zcool/discover.ts +++ b/lib/routes/zcool/discover.ts @@ -34,115 +34,115 @@ export const route: Route = { handler, description: `查看 **精选** 分类下的全部内容,其他参数选择默认,可直接使用路由 [\`/zcool/discover/0\`](https://rsshub.app/zcool/discover/0) - 查看 **精选** 分类下的 **运营设计** 子分类全部内容,其他参数选择默认,可直接使用路由 [\`/zcool/discover/0/617\`](https://rsshub.app/zcool/discover/0/617) +查看 **精选** 分类下的 **运营设计** 子分类全部内容,其他参数选择默认,可直接使用路由 [\`/zcool/discover/0/617\`](https://rsshub.app/zcool/discover/0/617) - 在 **精选** 分类下的 **运营设计** 子分类全部内容基础上,筛选出有 **视频**,可直接使用路由 [\`/zcool/discover/0/617/1\`](https://rsshub.app/zcool/discover/0/617/1) +在 **精选** 分类下的 **运营设计** 子分类全部内容基础上,筛选出有 **视频**,可直接使用路由 [\`/zcool/discover/0/617/1\`](https://rsshub.app/zcool/discover/0/617/1) - 在 **精选** 分类下的 **运营设计** 子分类全部内容基础上,筛选出有 **视频**,且城市选择 **北京**,可直接使用路由 [\`/zcool/discover/0/617/1/北京\`](https://rsshub.app/zcool/discover/0/617/1/北京) +在 **精选** 分类下的 **运营设计** 子分类全部内容基础上,筛选出有 **视频**,且城市选择 **北京**,可直接使用路由 [\`/zcool/discover/0/617/1/北京\`](https://rsshub.app/zcool/discover/0/617/1/北京) ::: tip - 下方仅提供 **分类及其子分类** 参数的代码。**学校** 参数的代码可以在 [站酷发现页](https://www.zcool.com.cn/discover) 中选择跳转后,从浏览器地址栏中找到。 +下方仅提供 **分类及其子分类** 参数的代码。**学校** 参数的代码可以在 [站酷发现页](https://www.zcool.com.cn/discover) 中选择跳转后,从浏览器地址栏中找到。 ::: - 分类 cate +分类 cate | 精选 | 平面 | 插画 | UI | 网页 | 摄影 | 三维 | 影视 | 空间 | 工业 / 产品 | 动漫 | 纯艺术 | 手工艺 | 服装 | 其他 | | ---- | ---- | ---- | -- | ---- | ---- | ---- | ---- | ---- | ----------- | ---- | ------ | ------ | ---- | ---- | | 0 | 8 | 1 | 17 | 607 | 33 | 24 | 610 | 609 | 499 | 608 | 612 | 611 | 613 | 44 | - 子分类 subCate +子分类 subCate - 精选 0 +精选 0 | 运营设计 | 包装 | 动画 / 影视 | 人像摄影 | 商业插画 | 电商 | APP 界面 | 艺术插画 | 家装设计 | 海报 | 文章 | | -------- | ---- | ----------- | -------- | -------- | ---- | -------- | -------- | -------- | ---- | ------ | | 617 | 9 | 30 | 34 | 2 | 616 | 757 | 292 | 637 | 10 | 809824 | - 平面 8 +平面 8 | 包装 | 海报 | 品牌 | IP 形象 | 字体 / 字形 | Logo | 书籍 / 画册 | 宣传物料 | 图案 | 信息图表 | PPT/Keynote | 其他平面 | 文章 | | ---- | ---- | ---- | ------- | ----------- | ---- | ----------- | -------- | ---- | -------- | ----------- | -------- | ---- | | 9 | 10 | 15 | 779 | 14 | 13 | 12 | 534 | 624 | 625 | 626 | 11 | 809 | - 插画 1 +插画 1 | 商业插画 | 概念设定 | 游戏原画 | 绘本 | 儿童插画 | 艺术插画 | 创作习作 | 新锐潮流插画 | 像素画 | 文章 | | -------- | -------- | -------- | ---- | -------- | -------- | -------- | ------------ | ------ | ---- | | 2 | 5 | 685 | 631 | 684 | 292 | 7 | 3 | 4 | 819 | - UI 17 +UI 17 | APP 界面 | 游戏 UI | 软件界面 | 图标 | 主题 / 皮肤 | 交互 / UE | 动效设计 | 闪屏 / 壁纸 | 其他 UI | 文章 | | -------- | ------- | -------- | ---- | ----------- | --------- | -------- | ----------- | ------- | ---- | | 757 | 692 | 621 | 20 | 19 | 623 | 797 | 21 | 23 | 822 | - 网页 607 +网页 607 | 电商 | 企业官网 | 游戏 / 娱乐 | 运营设计 | 移动端网页 | 门户网站 | 个人网站 | 其他网页 | 文章 | | ---- | -------- | ----------- | -------- | ---------- | -------- | -------- | -------- | ---- | | 616 | 614 | 693 | 617 | 777 | 615 | 618 | 620 | 823 | - 摄影 33 +摄影 33 | 人像摄影 | 风光摄影 | 人文 / 纪实摄影 | 美食摄影 | 产品摄影 | 环境 / 建筑摄影 | 时尚 / 艺术摄影 | 修图 / 后期 | 宠物摄影 | 婚礼摄影 | 其他摄影 | 文章 | | -------- | -------- | --------------- | -------- | -------- | --------------- | --------------- | ----------- | -------- | -------- | -------- | ---- | | 34 | 35 | 36 | 825 | 686 | 38 | 800 | 687 | 40 | 808 | 43 | 810 | - 三维 24 +三维 24 | 动画 / 影视 | 机械 / 交通 | 人物 / 生物 | 产品 | 场景 | 建筑 / 空间 | 其他三维 | 文章 | | ----------- | ----------- | ----------- | ---- | ---- | ----------- | -------- | ---- | | 30 | 25 | 27 | 807 | 26 | 29 | 32 | 818 | - 影视 610 +影视 610 | 短片 | Motion Graphic | 宣传片 | 影视后期 | 栏目片头 | MV | 设定 / 分镜 | 其他影视 | 文章 | | ---- | -------------- | ------ | -------- | -------- | --- | ----------- | -------- | ---- | | 645 | 649 | 804 | 646 | 647 | 644 | 650 | 651 | 817 | - 空间 609 +空间 609 | 家装设计 | 酒店餐饮设计 | 商业空间设计 | 建筑设计 | 舞台美术 | 展陈设计 | 景观设计 | 其他空间 | 文章 | | -------- | ------------ | ------------ | -------- | -------- | -------- | -------- | -------- | ---- | | 637 | 811 | 641 | 636 | 638 | 639 | 640 | 642 | 812 | - 工业 / 产品 499 +工业 / 产品 499 | 生活用品 | 电子产品 | 交通工具 | 工业用品 / 机械 | 人机交互 | 玩具 | 其他工业 / 产品 | 文章 | | -------- | -------- | -------- | --------------- | -------- | ---- | --------------- | ---- | | 508 | 506 | 509 | 511 | 510 | 689 | 514 | 813 | - 动漫 608 +动漫 608 | 短篇 / 格漫 | 中 / 长篇漫画 | 网络表情 | 单幅漫画 | 动画片 | 其他动漫 | 文章 | | ----------- | ------------- | -------- | -------- | ------ | -------- | ---- | | 628 | 629 | 632 | 627 | 633 | 635 | 820 | - 纯艺术 612 +纯艺术 612 | 绘画 | 雕塑 | 书法 | 实验艺术 | 文章 | | ---- | ---- | ---- | -------- | ---- | | 659 | 662 | 668 | 657 | 821 | - 手工艺 611 +手工艺 611 | 工艺品设计 | 手办 / 模玩 | 首饰设计 | 其他手工艺 | 文章 | | ---------- | ----------- | -------- | ---------- | ---- | | 654 | 656 | 756 | 658 | 816 | - 服装 613 +服装 613 | 休闲 / 流行服饰 | 正装 / 礼服 | 传统 / 民族服饰 | 配饰 | 鞋履设计 | 儿童服饰 | 其他服装 | 文章 | | --------------- | ----------- | --------------- | ---- | -------- | -------- | -------- | ---- | | 672 | 671 | 814 | 677 | 676 | 673 | 680 | 815 | - 其他 44 +其他 44 | 文案 / 策划 | VR 设计 | 独立游戏 | 其他 | 文章 | | ----------- | ------- | -------- | ---- | ---- | | 417 | 798 | 683 | 45 | 824 | - 推荐等级 recommendLevel +推荐等级 recommendLevel | 全部 | 编辑精选 | 首页推荐 | 全部推荐 | | ---- | -------- | -------- | -------- | diff --git a/lib/routes/zcool/user.ts b/lib/routes/zcool/user.ts index 5d578f27689d..e96e6f7d48a9 100644 --- a/lib/routes/zcool/user.ts +++ b/lib/routes/zcool/user.ts @@ -31,11 +31,11 @@ export const route: Route = { }, ], name: '用户作品', - description: ` 例如: + description: `例如: - 站酷的个人主页 \`https://baiyong.zcool.com.cn\` 对应 rss 路径 \`/zcool/user/baiyong\` +站酷的个人主页 \`https://baiyong.zcool.com.cn\` 对应 rss 路径 \`/zcool/user/baiyong\` - 站酷的个人主页 \`https://www.zcool.com.cn/u/568339\` 对应 rss 路径 \`/zcool/user/568339\``, +站酷的个人主页 \`https://www.zcool.com.cn/u/568339\` 对应 rss 路径 \`/zcool/user/568339\``, maintainers: ['junbaor'], handler, }; diff --git a/lib/routes/zhibo8/more.ts b/lib/routes/zhibo8/more.ts index 9d57a993c86d..1065b6cb0256 100644 --- a/lib/routes/zhibo8/more.ts +++ b/lib/routes/zhibo8/more.ts @@ -33,8 +33,7 @@ export const route: Route = { }, ], name: '滚动新闻', - description: ` -| NBA | 足球 | 电竞 | 综合 | + description: `| NBA | 足球 | 电竞 | 综合 | | --- | ----- | -------- | ------ | | nba | zuqiu | dianjing | zonghe |`, maintainers: ['nczitzk'], diff --git a/lib/routes/zhihu/check-cookie.ts b/lib/routes/zhihu/check-cookie.ts index 6d0938d92ede..f68c255135fe 100644 --- a/lib/routes/zhihu/check-cookie.ts +++ b/lib/routes/zhihu/check-cookie.ts @@ -18,9 +18,9 @@ async function handler() { }; } - const response = await ofetch(`https://www.zhihu.com/api/v4/me?include=is_realname`, { + const response = await ofetch('https://www.zhihu.com/api/v4/me?include=is_realname', { headers: { - Referer: `https://www.zhihu.com/`, + Referer: 'https://www.zhihu.com/', Cookie: cookie as string, }, }); diff --git a/lib/routes/zhihu/hot.ts b/lib/routes/zhihu/hot.ts index d76011596295..87eac28d0ef2 100644 --- a/lib/routes/zhihu/hot.ts +++ b/lib/routes/zhihu/hot.ts @@ -31,7 +31,7 @@ export const route: Route = { async function handler(ctx) { const category = ctx.req.param('category'); if (category) { - ctx.set('redirect', `/zhihu/hot`); + ctx.set('redirect', '/zhihu/hot'); return null; } @@ -39,7 +39,7 @@ async function handler(ctx) { const response = await got({ method: 'get', - url: `https://api.zhihu.com/topstory/hot-lists/total?limit=10&reverse_order=0`, + url: 'https://api.zhihu.com/topstory/hot-lists/total?limit=10&reverse_order=0', headers: { Cookie: cookie, }, @@ -56,8 +56,8 @@ async function handler(ctx) { }); return { - title: `知乎热榜`, - link: `https://www.zhihu.com/hot`, + title: '知乎热榜', + link: 'https://www.zhihu.com/hot', item: items, }; } diff --git a/lib/routes/zhihu/namespace.ts b/lib/routes/zhihu/namespace.ts index f04ce1a32ed4..db4a60819b99 100644 --- a/lib/routes/zhihu/namespace.ts +++ b/lib/routes/zhihu/namespace.ts @@ -4,7 +4,7 @@ export const namespace: Namespace = { name: '知乎', url: 'www.zhihu.com', description: `::: tip -自2024年7月,未登录状态下大部分路由[无法获取全文](https://github.com/DIYgod/RSSHub/issues/16260)。若有需要请在登陆知乎后寻找并添加包含\`z_c0\`的Cookies至环境变量\`ZHIHU_COOKIES\`。 +自 2024 年 7 月,未登录状态下大部分路由[无法获取全文](https://github.com/DIYgod/RSSHub/issues/16260)。若有需要请在登陆知乎后寻找并添加包含\`z_c0\`的 Cookies 至环境变量\`ZHIHU_COOKIES\`。 :::`, lang: 'zh-CN', }; diff --git a/lib/routes/zhihu/timeline.ts b/lib/routes/zhihu/timeline.ts index 0dd5454411d4..ebf480723b3a 100644 --- a/lib/routes/zhihu/timeline.ts +++ b/lib/routes/zhihu/timeline.ts @@ -28,7 +28,7 @@ export const route: Route = { maintainers: ['SeanChao'], handler, description: `::: warning - 用户关注动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 +用户关注动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; @@ -39,7 +39,7 @@ async function handler(ctx) { } const response = await got({ method: 'get', - url: `https://www.zhihu.com/api/v3/moments`, + url: 'https://www.zhihu.com/api/v3/moments', headers: { Cookie: cookie, }, @@ -145,8 +145,8 @@ async function handler(ctx) { }); return { - title: `知乎关注动态`, - link: `https://www.zhihu.com/follow`, + title: '知乎关注动态', + link: 'https://www.zhihu.com/follow', item: out, }; } diff --git a/lib/routes/zhihu/xhu/activities.ts b/lib/routes/zhihu/xhu/activities.ts index f50d03a60676..e649a1bc5ec6 100644 --- a/lib/routes/zhihu/xhu/activities.ts +++ b/lib/routes/zhihu/xhu/activities.ts @@ -30,10 +30,11 @@ export const route: Route = { description: `[xhu](https://github.com/REToys/xhu) ::: tip - 用户的 16 进制 id 获取方式: +用户的 16 进制 id 获取方式: + +1. 可以通过 RSSHub Radar 扩展获取; +2. 或者在用户主页打开 F12 控制台,执行以下代码:\`console.log(/"id":"([0-9a-f]*?)","urlToken"/.exec(document.getElementById('js-initialData').innerHTML)[1]);\` 即可获取用户的 16 进制 id。 - 1. 可以通过 RSSHub Radar 扩展获取; - 2. 或者在用户主页打开 F12 控制台,执行以下代码:\`console.log(/"id":"([0-9a-f]*?)","urlToken"/.exec(document.getElementById('js-initialData').innerHTML)[1]);\` 即可获取用户的 16 进制 id。 :::`, }; diff --git a/lib/routes/zhonglun/index.ts b/lib/routes/zhonglun/index.ts index 9cc81f66d812..35343c120a0f 100644 --- a/lib/routes/zhonglun/index.ts +++ b/lib/routes/zhonglun/index.ts @@ -91,11 +91,9 @@ export const route: Route = { handler, example: '/zhonglun/research/article/zh', parameters: { category: '语言,默认为 zh,即简体中文,可在对应分类页 URL 中找到' }, - description: ` -| ENG | 简体中文 | 日本語 | 한국어 | + description: `| ENG | 简体中文 | 日本語 | 한국어 | | --- | -------- | ------ | ------ | -| en | zh | ja | kr | - `, +| en | zh | ja | kr |`, categories: ['new-media'], features: { diff --git a/lib/routes/zhubai/index.ts b/lib/routes/zhubai/index.ts index 8ca6ad918873..d05ae7d8730b 100644 --- a/lib/routes/zhubai/index.ts +++ b/lib/routes/zhubai/index.ts @@ -21,7 +21,7 @@ export const route: Route = { maintainers: ['naixy28'], handler, description: `::: tip - 在路由末尾处加上 \`?limit=限制获取数目\` 来限制获取条目数量,默认值为\`20\` +在路由末尾处加上 \`?limit=限制获取数目\` 来限制获取条目数量,默认值为\`20\` :::`, }; diff --git a/lib/routes/zhuwang/index.ts b/lib/routes/zhuwang/index.ts index f530a238e931..80500f5bd396 100644 --- a/lib/routes/zhuwang/index.ts +++ b/lib/routes/zhuwang/index.ts @@ -63,7 +63,7 @@ async function handler() { }); return { - title: `全国今日生猪价格`, + title: '全国今日生猪价格', desription: '中国养猪网猪价频道是中国猪价权威平台,提供每日猪评,猪价和行情分析,并且预测猪价和分析每天的猪价排行。', link: baseUrl, item: priceItems, diff --git a/lib/routes/zju/cse/index.ts b/lib/routes/zju/cse/index.ts index 4473eb2eca0f..bb71c2100306 100644 --- a/lib/routes/zju/cse/index.ts +++ b/lib/routes/zju/cse/index.ts @@ -27,8 +27,8 @@ export const route: Route = { description: `栏目类型 | 简讯专栏 | 本科生教育 | 研究生教育 | 科研学术 | 人事工作 | 学生思政 | 对外交流 | 就业指导 | -| ------ | ------- | ------- | ------ | ------ | ------ | ------ | ------ | -| - | bksjy | yjsjy | kyxs | rsgz | xssz | dwjl | jyzd |`, +| -------- | ---------- | ---------- | -------- | -------- | -------- | -------- | -------- | +| - | bksjy | yjsjy | kyxs | rsgz | xssz | dwjl | jyzd |`, }; async function handler(ctx) { diff --git a/lib/routes/zju/list.ts b/lib/routes/zju/list.ts index 4e86f28a0d37..32a498370e61 100644 --- a/lib/routes/zju/list.ts +++ b/lib/routes/zju/list.ts @@ -26,7 +26,7 @@ export const route: Route = { async function handler(ctx) { const type = ctx.req.param('type') ?? 'xs'; - const link = host + type + `/list.htm`; + const link = host + type + '/list.htm'; const response = await got({ method: 'get', url: link, @@ -77,7 +77,7 @@ async function handler(ctx) { }) ); return { - title: `浙江大学` + $('ul.submenu .selected').text(), + title: '浙江大学' + $('ul.submenu .selected').text(), link, item: out, }; diff --git a/lib/routes/zju/sis/index.ts b/lib/routes/zju/sis/index.ts index c68955fa2e2d..56ac4f111a35 100644 --- a/lib/routes/zju/sis/index.ts +++ b/lib/routes/zju/sis/index.ts @@ -123,9 +123,8 @@ export const route: Route = { }, name: '外国语学院', description: `| 重要公告 | 最新通知 | 教育教学 | 科学研究 | 新闻动态 | 联系我们 | 党政管理 | 组织人事 | 科学研究 | 本科教育 | 研究生教育 | 学生思政 | 校友联络 | 对外交流 | -| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -`, +| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | ---------- | -------- | -------- | -------- | +| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |`, maintainers: ['Alex222222222222'], handler: handleSisRequest, url: 'www.sis.zju.edu.cn', diff --git a/lib/routes/zjut/cs/index.ts b/lib/routes/zjut/cs/index.ts index 06c4ec751d8e..165f1f5e518c 100644 --- a/lib/routes/zjut/cs/index.ts +++ b/lib/routes/zjut/cs/index.ts @@ -33,8 +33,8 @@ export const route: Route = { }, ], description: `| 新闻资讯 | 学术动态 | 通知公告 | -| ------- | ------- | ------- | -| 54 | 55 | 53 |`, +| -------- | -------- | -------- | +| 54 | 55 | 53 |`, }; async function handler(ctx) { diff --git a/lib/routes/zjut/da/index.ts b/lib/routes/zjut/da/index.ts index 77e230503b52..42f5cccaba2c 100644 --- a/lib/routes/zjut/da/index.ts +++ b/lib/routes/zjut/da/index.ts @@ -36,8 +36,8 @@ export const route: Route = { url: 'www.design.zjut.edu.cn', handler, description: `| 学院新闻 | 公告通知 | 科研申报 | 科研成果 | 文件与资源 | 学术交流 | -| -------- | -------- | -------- | -------- | -------- | -------- | -| 1 | 2 | 3 | 4 | 5 | 6 |`, +| -------- | -------- | -------- | -------- | ---------- | -------- | +| 1 | 2 | 3 | 4 | 5 | 6 |`, }; async function handler(ctx) { diff --git a/lib/routes/zjut/jwc/index.ts b/lib/routes/zjut/jwc/index.ts index e214d0f2b43d..04e202dcb614 100644 --- a/lib/routes/zjut/jwc/index.ts +++ b/lib/routes/zjut/jwc/index.ts @@ -32,21 +32,21 @@ export const route: Route = { target: '/jwc/:type', }, ], - description: `| 板块 | 参数 | -| ------- | ------- | -| 新闻动态 | 1838 | -| 课程思政 | 1842 | -| 校内动态 | 2613 | -| 学习思考 | 2614 | -| 成果展示 | 2615 | -| 媒体聚焦 | 2616 | -| 制度文件 | 2617 | -| 教学运行 | 1849 | -| 实践竞赛 | 1850 | -| 留学生Notice | 1851 | -| 项目申报 | 1852 | -| 学籍管理 | 1853 | -| 办事指南 | 1839 |`, + description: `| 板块 | 参数 | +| ------------- | ---- | +| 新闻动态 | 1838 | +| 课程思政 | 1842 | +| 校内动态 | 2613 | +| 学习思考 | 2614 | +| 成果展示 | 2615 | +| 媒体聚焦 | 2616 | +| 制度文件 | 2617 | +| 教学运行 | 1849 | +| 实践竞赛 | 1850 | +| 留学生 Notice | 1851 | +| 项目申报 | 1852 | +| 学籍管理 | 1853 | +| 办事指南 | 1839 |`, }; async function handler(ctx) { diff --git a/lib/routes/zjut/www/index.ts b/lib/routes/zjut/www/index.ts index cd489f22ccde..6d2e0544bc10 100644 --- a/lib/routes/zjut/www/index.ts +++ b/lib/routes/zjut/www/index.ts @@ -32,15 +32,15 @@ export const route: Route = { target: '/www/:type', }, ], - description: `| 板块 | 参数 | -| ------- | ------- | -| 学术动态 | xsdt_4662 | -| 三创·人物 | 4527 | -| 通知公告 | 4528 | -| 美誉工大 | 5389 | -| 智库工大 | 5390 | -| 工大校历 | 4520 | -| 校区班车 | xqbc |`, + description: `| 板块 | 参数 | +| ---------- | ---------- | +| 学术动态 | xsdt\\_4662 | +| 三创・人物 | 4527 | +| 通知公告 | 4528 | +| 美誉工大 | 5389 | +| 智库工大 | 5390 | +| 工大校历 | 4520 | +| 校区班车 | xqbc |`, }; async function handler(ctx) { diff --git a/lib/routes/zsxq/group.ts b/lib/routes/zsxq/group.ts index 358ecedd92f1..eeaaa462cd9a 100644 --- a/lib/routes/zsxq/group.ts +++ b/lib/routes/zsxq/group.ts @@ -32,9 +32,9 @@ export const route: Route = { ], }, handler, - description: `| all | digests | by_owner | questions | tasks | -| ---- | ------ | --------- | -------- | ------ | -| 最新 | 精华 | 只看星主 | 问答 | 作业 |`, + description: `| all | digests | by\\_owner | questions | tasks | +| ---- | ------- | --------- | --------- | ----- | +| 最新 | 精华 | 只看星主 | 问答 | 作业 |`, }; async function handler(ctx: Context): Promise { diff --git a/lib/routes/zxcs/namespace.ts b/lib/routes/zxcs/namespace.ts index a604ffcac87d..dc85a38d0cbc 100644 --- a/lib/routes/zxcs/namespace.ts +++ b/lib/routes/zxcs/namespace.ts @@ -2,6 +2,6 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: '知轩藏书', - url: 'zxcs.info', + url: 'zxcs.click', lang: 'zh-CN', }; diff --git a/lib/routes/zxcs/novel.ts b/lib/routes/zxcs/novel.ts index 6c5d19c65d95..5541810820f1 100644 --- a/lib/routes/zxcs/novel.ts +++ b/lib/routes/zxcs/novel.ts @@ -24,7 +24,7 @@ const types = { export const route: Route = { path: '/novel/:type', name: '小说列表', - url: 'zxcs.info', + url: 'zxcs.click', maintainers: ['liaochuan'], example: '/zxcs/novel/jinqigengxin', parameters: { type: '小说类型, 可在对应类型页 URL 中找到' }, @@ -42,7 +42,7 @@ export const route: Route = { }, radar: [ { - source: ['zxcs.info/:type'], + source: ['zxcs.click/:type'], target: '/novel/:type', }, ], @@ -52,7 +52,7 @@ export const route: Route = { async function handler(ctx) { const { type } = ctx.req.param(); - const baseUrl = `https://www.zxcs.info`; + const baseUrl = 'https://www.zxcs.click'; const link = `${baseUrl}/${type}`; const response = await ofetch(link); const $ = load(response); diff --git a/lib/routes/zyw/hot.ts b/lib/routes/zyw/hot.ts index b0f78c551510..5af3a811d78d 100644 --- a/lib/routes/zyw/hot.ts +++ b/lib/routes/zyw/hot.ts @@ -18,7 +18,7 @@ export const route: Route = { maintainers: ['nczitzk'], handler, description: `::: tip - 全部站点请见 [此处](https://hot.zyw.asia/#/list) +全部站点请见 [此处](https://hot.zyw.asia/#/list) ::: | 哔哩哔哩 | 微博 | 知乎 | 36 氪 | 百度 | 少数派 | IT 之家 | 澎湃新闻 | 今日头条 | 百度贴吧 | 稀土掘金 | 腾讯新闻 | diff --git a/lib/routes/zzu/dwzzb.ts b/lib/routes/zzu/dwzzb.ts index 5670dd3ab4ea..6d626029d4e6 100644 --- a/lib/routes/zzu/dwzzb.ts +++ b/lib/routes/zzu/dwzzb.ts @@ -23,8 +23,8 @@ export const route: Route = { maintainers: ['amandus1990'], handler, description: `| 党建工作 | 干部工作 | 人才工作 | 乡村振兴工作 | -| -------- | -------- | -------- | -------- | -| djgz | gbgz | rcgz | fpgz |`, +| -------- | -------- | -------- | ------------ | +| djgz | gbgz | rcgz | fpgz |`, }; async function handler(ctx) { diff --git a/lib/setup.test.ts b/lib/setup.test.ts index c9834ab957ac..6779f93901e5 100644 --- a/lib/setup.test.ts +++ b/lib/setup.test.ts @@ -26,7 +26,7 @@ ${script} }; const server = setupServer( - http.post(`https://api.openai.mock/v1/chat/completions`, () => + http.post('https://api.openai.mock/v1/chat/completions', () => HttpResponse.json({ choices: [ { @@ -37,12 +37,12 @@ const server = setupServer( ], }) ), - http.get(`http://rsshub.test/config`, () => + http.get('http://rsshub.test/config', () => HttpResponse.json({ UA: 'test', }) ), - http.get(`http://rsshub.test/buildData`, () => + http.get('http://rsshub.test/buildData', () => HttpResponse.text(`
    • @@ -58,7 +58,7 @@ const server = setupServer(
    `) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/appMsg`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/appMsg', () => HttpResponse.text( genWeChatMpPage( ` @@ -83,7 +83,7 @@ window.ip_wording = { ) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/original_empty`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/original_empty', () => HttpResponse.text( ` @@ -102,10 +102,10 @@ var msg_source_url = "https://mp.weixin.qq.com/rsshub_test/fake"; ` ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/original_source`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/original_source', () => HttpResponse.text( genWeChatMpPage( - `original content`, + 'original content', ` var item_show_type = "0"; var real_item_show_type = "0"; @@ -115,7 +115,7 @@ var msg_source_url = "https://mp.weixin.qq.com/rsshub_test/fake";` ) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/original_long`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/original_long', () => HttpResponse.text( genWeChatMpPage( 'long-content-'.repeat(10), @@ -128,7 +128,7 @@ var msg_source_url = "https://mp.weixin.qq.com/rsshub_test/fake";` ) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/img`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/img', () => HttpResponse.text( genWeChatMpPage('fake_description', [ ` @@ -150,7 +150,7 @@ window.picture_page_info_list = [ ]) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/audio`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/audio', () => HttpResponse.text( genWeChatMpPage('fake_description', [ ` @@ -175,7 +175,7 @@ window.cgiData = { ]) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/video`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/video', () => HttpResponse.text( genWeChatMpPage( 'fake_description', @@ -188,7 +188,7 @@ var ct = "${1_636_626_300}"; ) ) ), - http.get(`https://mp.weixin.qq.com/rsshub_test/fallback`, () => + http.get('https://mp.weixin.qq.com/rsshub_test/fallback', () => HttpResponse.text( genWeChatMpPage( 'fake_description', @@ -201,14 +201,14 @@ var ct = "${1_636_626_300}"; ) ) ), - http.get(`https://mp.weixin.qq.com/s/rsshub_test`, () => HttpResponse.redirect(`https://mp.weixin.qq.com/rsshub_test/fallback`)), - http.get(`https://mp.weixin.qq.com/s`, ({ request }) => { + http.get('https://mp.weixin.qq.com/s/rsshub_test', () => HttpResponse.redirect('https://mp.weixin.qq.com/rsshub_test/fallback')), + http.get('https://mp.weixin.qq.com/s', ({ request }) => { const url = new URL(request.url); if (url.searchParams.get('__biz') === 'rsshub_test' && url.searchParams.get('mid') === '1' && url.searchParams.get('idx') === '1' && url.searchParams.get('sn') === '1') { - return HttpResponse.redirect(`https://mp.weixin.qq.com/rsshub_test/fallback`); + return HttpResponse.redirect('https://mp.weixin.qq.com/rsshub_test/fallback'); } }), - http.get(`https://mp.weixin.qq.com/mp/rsshub_test/waf`, () => + http.get('https://mp.weixin.qq.com/mp/rsshub_test/waf', () => HttpResponse.text( ` @@ -236,8 +236,8 @@ var ct = "${1_636_626_300}"; ` ) ), - http.get(`https://mp.weixin.qq.com/s/rsshub_test_hit_waf`, () => HttpResponse.redirect(`https://mp.weixin.qq.com/mp/rsshub_test/waf`)), - http.get(`https://mp.weixin.qq.com/s/unknown_page`, () => + http.get('https://mp.weixin.qq.com/s/rsshub_test_hit_waf', () => HttpResponse.redirect('https://mp.weixin.qq.com/mp/rsshub_test/waf')), + http.get('https://mp.weixin.qq.com/s/unknown_page', () => HttpResponse.text( ` @@ -253,7 +253,7 @@ Unknown paragraph ` ) ), - http.get(`https://mp.weixin.qq.com/s/deleted_page`, () => + http.get('https://mp.weixin.qq.com/s/deleted_page', () => HttpResponse.text( ` @@ -269,17 +269,17 @@ Unknown paragraph ` ) ), - http.get(`https://mp.weixin.qq.com/s/rsshub_test_redirect_no_location`, () => HttpResponse.text('', { status: 302 })), - http.get(`https://mp.weixin.qq.com/s/rsshub_test_recursive_redirect`, () => HttpResponse.redirect(`https://mp.weixin.qq.com/s/rsshub_test_recursive_redirect`)), - http.get(`http://rsshub.test/headers`, ({ request }) => HttpResponse.json(Object.fromEntries(request.headers.entries()))), - http.post(`http://rsshub.test/form-post`, async ({ request }) => { + http.get('https://mp.weixin.qq.com/s/rsshub_test_redirect_no_location', () => HttpResponse.text('', { status: 302 })), + http.get('https://mp.weixin.qq.com/s/rsshub_test_recursive_redirect', () => HttpResponse.redirect('https://mp.weixin.qq.com/s/rsshub_test_recursive_redirect')), + http.get('http://rsshub.test/headers', ({ request }) => HttpResponse.json(Object.fromEntries(request.headers.entries()))), + http.post('http://rsshub.test/form-post', async ({ request }) => { const formData = await request.formData(); return HttpResponse.json({ test: formData.get('test'), req: { headers: Object.fromEntries(request.headers.entries()) }, }); }), - http.post(`http://rsshub.test/json-post`, async ({ request }) => { + http.post('http://rsshub.test/json-post', async ({ request }) => { const jsonData = (await request.json()) as { test: string; }; @@ -287,7 +287,7 @@ Unknown paragraph test: jsonData?.test, }); }), - http.get(`http://rsshub.test/rss`, () => HttpResponse.text('')) + http.get('http://rsshub.test/rss', () => HttpResponse.text('')) ); server.listen({ onUnhandledRequest: 'bypass' }); diff --git a/lib/techflowpost.test.ts b/lib/techflowpost.test.ts new file mode 100644 index 000000000000..57edf708964d --- /dev/null +++ b/lib/techflowpost.test.ts @@ -0,0 +1,156 @@ +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/types.ts b/lib/types.ts index 4b344f85a9c2..54c128ae3958 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -26,6 +26,7 @@ export type Category = | 'study' | 'journal' | 'finance' + | 'sport' | 'other'; // rss @@ -330,7 +331,7 @@ interface RouteItem { }> | false; - /** set to `true` if the feed uses puppeteer */ + /** set to `true` if the feed uses browser automation */ requirePuppeteer?: boolean; /** set to `true` if the target website has an anti-crawler mechanism */ diff --git a/lib/utils/cache.test.ts b/lib/utils/cache.test.ts index b5d37eb89623..d703d6c6e66d 100644 --- a/lib/utils/cache.test.ts +++ b/lib/utils/cache.test.ts @@ -42,6 +42,24 @@ describe('cache', () => { expect(await memory.has('missing')).toBe(false); }); + it('tryGet preserves snowflake ID precision', async () => { + process.env.CACHE_TYPE = 'memory'; + const cache = (await import('@/utils/cache')).default; + if (!cache.clients.memoryCache || !cache.status.available) { + throw new Error('Memory cache client error'); + } + + const snowflakeId = '1234567890123456789'; + const fetcher = vi.fn(() => Promise.resolve(snowflakeId)); + + const fresh = await cache.tryGet('snowflake', fetcher); + expect(fresh).toBe(snowflakeId); + const cached = await cache.tryGet('snowflake', fetcher); + expect(typeof cached).toBe('string'); + expect(cached).toBe(snowflakeId); + expect(fetcher).toHaveBeenCalledTimes(1); + }); + it('redis', async () => { process.env.CACHE_TYPE = 'redis'; const cache = (await import('@/utils/cache')).default; diff --git a/lib/utils/cache/http.test.ts b/lib/utils/cache/http.test.ts new file mode 100644 index 000000000000..6e663dee4773 --- /dev/null +++ b/lib/utils/cache/http.test.ts @@ -0,0 +1,118 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +const errorSpy = vi.fn(); +const infoSpy = vi.fn(); + +vi.mock('@/utils/logger', () => ({ + default: { + error: errorSpy, + info: infoSpy, + }, +})); + +const setHttpCacheEnv = () => { + process.env.CACHE_HTTP_URL = 'https://cache.example.com/'; + process.env.CACHE_HTTP_TOKEN = 'token'; +}; + +const clearHttpCacheEnv = () => { + delete process.env.CACHE_TYPE; + delete process.env.CACHE_HTTP_URL; + delete process.env.CACHE_HTTP_TOKEN; +}; + +describe('http cache module', () => { + afterEach(() => { + clearHttpCacheEnv(); + vi.unstubAllGlobals(); + vi.resetModules(); + vi.clearAllMocks(); + }); + + it('requires endpoint and token', async () => { + const cache = (await import('@/utils/cache/http')).default; + + cache.init(); + + expect(cache.status.available).toBe(false); + expect(errorSpy).toHaveBeenCalledWith('HTTP cache requires CACHE_HTTP_URL and CACHE_HTTP_TOKEN.'); + }); + + it('sets, gets, refreshes, and checks hashed keys', async () => { + setHttpCacheEnv(); + const requests: Array<{ body?: string; init?: RequestInit; url: string }> = []; + const fetchMock = vi.fn((input: RequestInfo | URL, init?: RequestInit) => { + requests.push({ + body: init?.body?.toString(), + init, + url: input.toString(), + }); + + if (init?.method === 'PUT') { + return new Response(null, { status: 204 }); + } + if (init?.method === 'HEAD') { + return new Response(null, { status: 204 }); + } + + return Response.json({ hit: true, value: 'cached' }); + }); + vi.stubGlobal('fetch', fetchMock); + + const cache = (await import('@/utils/cache/http')).default; + cache.init(); + + await cache.set('mock/key', { ok: true }, 30); + await expect(cache.get('mock/key')).resolves.toBe('cached'); + await expect(cache.get('mock/key', false)).resolves.toBe('cached'); + await expect(cache.has('mock/key')).resolves.toBe(true); + + expect(cache.status.available).toBe(true); + expect(requests[0].url).toMatch(/^https:\/\/cache\.example\.com\/v1\/cache\/rsshub%3Ahttp-cache%3A[a-f0-9]{32}$/); + expect(requests[0].init?.headers).toMatchObject({ + authorization: 'Bearer token', + 'content-type': 'application/json', + }); + expect(JSON.parse(requests[0].body || '{}')).toEqual({ + ttl: 30, + value: '{"ok":true}', + }); + expect(requests[1].url).toMatch(/\?refresh=1$/); + expect(requests[2].url).not.toContain('?refresh=1'); + expect(requests[3].init?.method).toBe('HEAD'); + }); + + it('treats a 404 response as a miss', async () => { + setHttpCacheEnv(); + vi.stubGlobal( + 'fetch', + vi.fn(() => Response.json({ hit: false }, { status: 404 })) + ); + + const cache = (await import('@/utils/cache/http')).default; + cache.init(); + + await expect(cache.get('missing')).resolves.toBe(''); + await expect(cache.has('missing')).resolves.toBe(false); + }); + + it('uses non-refreshing reads for global cache', async () => { + process.env.CACHE_TYPE = 'http'; + setHttpCacheEnv(); + const urls: string[] = []; + vi.stubGlobal( + 'fetch', + vi.fn((input: RequestInfo | URL) => { + urls.push(input.toString()); + return Response.json({ hit: true, value: 'route-cache' }); + }) + ); + + const cache = (await import('@/utils/cache')).default; + + await expect(cache.globalCache.get('route/key')).resolves.toBe('route-cache'); + + expect(cache.status.available).toBe(true); + expect(urls[0]).not.toContain('?refresh=1'); + }); +}); diff --git a/lib/utils/cache/http.ts b/lib/utils/cache/http.ts new file mode 100644 index 000000000000..07e642c68ad4 --- /dev/null +++ b/lib/utils/cache/http.ts @@ -0,0 +1,173 @@ +import { config } from '@/config'; +import logger from '@/utils/logger'; +import md5 from '@/utils/md5'; + +import type CacheModule from './base'; + +type CacheHitResponse = { + hit: true; + value: string; +}; + +const status = { available: false }; + +let baseUrl: string | undefined; +let apiToken: string | undefined; + +const toRemoteKey = (key: string) => `rsshub:http-cache:${md5(key)}`; + +const cacheUrl = (key: string, refresh = false) => { + const url = `${baseUrl}/v1/cache/${encodeURIComponent(toRemoteKey(key))}`; + return refresh ? `${url}?refresh=1` : url; +}; + +const requestSignal = () => (typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(config.requestTimeout) : undefined); + +const request = async (url: string, init: Omit = {}, headers: Record = {}) => { + if (!status.available || !apiToken) { + return null; + } + + try { + return await fetch(url, { + ...init, + headers: { + authorization: `Bearer ${apiToken}`, + ...headers, + }, + signal: requestSignal(), + }); + } catch (error) { + logger.error('HTTP cache request failed:', error); + return null; + } +}; + +const readResponseText = async (response: Response) => { + try { + return await response.text(); + } catch { + return ''; + } +}; + +const readCacheHitResponse = async (response: Response) => { + try { + return (await response.json()) as CacheHitResponse; + } catch { + return null; + } +}; + +const logUnexpectedResponse = async (operation: string, response: Response) => { + const message = await readResponseText(response); + logger.error(`HTTP cache ${operation} failed with status ${response.status}${message ? `: ${message}` : ''}`); + + if (response.status === 401) { + status.available = false; + } +}; + +export default { + init: () => { + baseUrl = config.httpCache.url?.replace(/\/+$/, ''); + apiToken = config.httpCache.token; + + if (!baseUrl || !apiToken) { + status.available = false; + logger.error('HTTP cache requires CACHE_HTTP_URL and CACHE_HTTP_TOKEN.'); + return; + } + + try { + new URL(baseUrl); + } catch { + status.available = false; + logger.error('HTTP cache URL is invalid.'); + return; + } + + status.available = true; + logger.info('HTTP cache configured.'); + }, + get: async (key: string, refresh = true) => { + if (!key) { + return null; + } + + const response = await request(cacheUrl(key, refresh), { method: 'GET' }); + if (!response) { + return null; + } + + if (response.status === 404) { + return ''; + } + + if (!response.ok) { + await logUnexpectedResponse('get', response); + return null; + } + + const data = await readCacheHitResponse(response); + if (data?.hit === true && typeof data.value === 'string') { + return data.value; + } + + logger.error('HTTP cache get returned an invalid response.'); + return null; + }, + has: async (key: string) => { + if (!key) { + return false; + } + + const response = await request(cacheUrl(key), { method: 'HEAD' }); + if (!response) { + return false; + } + + if (response.status === 204) { + return true; + } + + if (response.status === 404) { + return false; + } + + await logUnexpectedResponse('has', response); + return false; + }, + set: async (key: string, value?: string | Record, maxAge = config.cache.contentExpire) => { + if (!key) { + return; + } + + if (!value || value === 'undefined') { + value = ''; + } + if (typeof value === 'object') { + value = JSON.stringify(value); + } + + const response = await request( + cacheUrl(key), + { + method: 'PUT', + body: JSON.stringify({ + ttl: maxAge, + value, + }), + }, + { + 'content-type': 'application/json', + } + ); + + if (response && response.status !== 204) { + await logUnexpectedResponse('set', response); + } + }, + clients: {}, + status, +} as CacheModule; diff --git a/lib/utils/cache/index.ts b/lib/utils/cache/index.ts index 17e329700548..722c4badf036 100644 --- a/lib/utils/cache/index.ts +++ b/lib/utils/cache/index.ts @@ -3,6 +3,7 @@ import { isWorker } from '@/utils/is-worker'; import logger from '@/utils/logger'; import type CacheModule from './base'; +import http from './http'; import memory from './memory'; import redis from './redis'; @@ -16,76 +17,96 @@ const globalCache: { set: () => null, }; -let cacheModule: CacheModule; +const noopCacheModule: CacheModule = { + init: () => null, + get: () => null, + has: () => false, + set: () => null, + status: { + available: false, + }, + clients: {}, +}; + +let cacheModule: CacheModule = noopCacheModule; if (isWorker) { // No-op cache for Cloudflare Workers - cacheModule = { - init: () => null, - get: () => null, - has: () => false, - set: () => null, - status: { - available: false, - }, - clients: {}, - }; -} else if (config.cache.type === 'redis') { - cacheModule = redis; - cacheModule.init(); - const { redisClient } = cacheModule.clients; - globalCache.get = async (key) => { - if (key && cacheModule.status.available && redisClient) { - const value = await redisClient.get(key); - return value; - } - }; - globalCache.has = async (key) => { - if (key && cacheModule.status.available && redisClient) { - const result = await redisClient.exists(key); - return result > 0; - } - return false; - }; - globalCache.set = cacheModule.set; -} else if (config.cache.type === 'memory') { - cacheModule = memory; - cacheModule.init(); - const { memoryCache } = cacheModule.clients; - globalCache.get = (key) => { - if (key && cacheModule.status.available && memoryCache) { - return memoryCache.get(key, { updateAgeOnGet: false }) as string | undefined; - } - }; - globalCache.has = (key) => { - if (key && cacheModule.status.available && memoryCache) { - return memoryCache.has(key); - } - return false; - }; - globalCache.set = (key, value, maxAge = config.cache.routeExpire) => { - if (!value || value === 'undefined') { - value = ''; - } - if (typeof value === 'object') { - value = JSON.stringify(value); + cacheModule = noopCacheModule; +} else { + switch (config.cache.type) { + case 'redis': { + cacheModule = redis; + cacheModule.init(); + const { redisClient } = cacheModule.clients; + globalCache.get = async (key) => { + if (key && cacheModule.status.available && redisClient) { + const value = await redisClient.get(key); + return value; + } + }; + globalCache.has = async (key) => { + if (key && cacheModule.status.available && redisClient) { + const result = await redisClient.exists(key); + return result > 0; + } + return false; + }; + globalCache.set = cacheModule.set; + break; } - if (key && memoryCache) { - return memoryCache.set(key, value, { ttl: maxAge * 1000 }); + case 'http': + cacheModule = http; + cacheModule.init(); + globalCache.get = (key) => { + if (key && cacheModule.status.available) { + return cacheModule.get(key, false); + } + }; + globalCache.has = (key) => { + if (key && cacheModule.status.available) { + return cacheModule.has(key); + } + return false; + }; + globalCache.set = (key, value, maxAge = config.cache.routeExpire) => { + if (key && cacheModule.status.available) { + return cacheModule.set(key, value, maxAge); + } + }; + break; + case 'memory': { + cacheModule = memory; + cacheModule.init(); + const { memoryCache } = cacheModule.clients; + globalCache.get = (key) => { + if (key && cacheModule.status.available && memoryCache) { + return memoryCache.get(key, { updateAgeOnGet: false }) as string | undefined; + } + }; + globalCache.has = (key) => { + if (key && cacheModule.status.available && memoryCache) { + return memoryCache.has(key); + } + return false; + }; + globalCache.set = (key, value, maxAge = config.cache.routeExpire) => { + if (!value || value === 'undefined') { + value = ''; + } + if (typeof value === 'object') { + value = JSON.stringify(value); + } + if (key && memoryCache) { + return memoryCache.set(key, value, { ttl: maxAge * 1000 }); + } + }; + break; } - }; -} else { - cacheModule = { - init: () => null, - get: () => null, - has: () => false, - set: () => null, - status: { - available: false, - }, - clients: {}, - }; - logger.error('Cache not available, concurrent requests are not limited. This could lead to bad behavior.'); + default: + cacheModule = noopCacheModule; + logger.error('Cache not available, concurrent requests are not limited. This could lead to bad behavior.'); + } } // only give cache string, as the `!` condition tricky @@ -120,7 +141,7 @@ export default { return v as T; } else { const value = await getValueFunc(); - cacheModule.set(key, value, maxAge); + cacheModule.set(key, JSON.stringify(value), maxAge); return value; } diff --git a/lib/utils/common-config.charset.test.ts b/lib/utils/common-config.charset.test.ts index 29b02d9b2fa7..008141d66cab 100644 --- a/lib/utils/common-config.charset.test.ts +++ b/lib/utils/common-config.charset.test.ts @@ -30,7 +30,7 @@ describe('common-config charset', () => { const data = await buildData({ link: 'http://rsshub.test/buildData', url: 'http://rsshub.test/buildData', - title: `%title%`, + title: '%title%', params: { title: 'buildData', }, diff --git a/lib/utils/common-config.test.ts b/lib/utils/common-config.test.ts index 7cac7f5a6793..8acb8ebd855a 100644 --- a/lib/utils/common-config.test.ts +++ b/lib/utils/common-config.test.ts @@ -43,7 +43,7 @@ describe('index', () => { const data = await configUtils({ link: 'http://rsshub.test/buildData', url: 'http://rsshub.test/buildData', - title: `%title%`, + title: '%title%', params: { title: 'buildData', }, diff --git a/lib/utils/common-utils.ts b/lib/utils/common-utils.ts index 5515a0db4305..1e176863002a 100644 --- a/lib/utils/common-utils.ts +++ b/lib/utils/common-utils.ts @@ -2,6 +2,7 @@ import os from 'node:os'; import title from 'title'; +import { config } from '@/config'; import { parseDate } from '@/utils/parse-date'; // convert a string into title case @@ -41,7 +42,9 @@ const getLocalhostAddress = () => { .filter((iface) => iface?.family === 'IPv4' && !iface.internal) .map((iface) => iface?.address) .filter(Boolean); - address.push('[::]'); + if (!config.disableIPv6) { + address.push('[::]'); + } return address; }; diff --git a/lib/utils/got.test.ts b/lib/utils/got.test.ts index 9f6a891b6f24..5b2195f7ca54 100644 --- a/lib/utils/got.test.ts +++ b/lib/utils/got.test.ts @@ -15,7 +15,7 @@ describe('got', () => { const requestRun = vi.fn(); const { default: server } = await import('@/setup.test'); server.use( - http.get(`http://rsshub.test/retry-test`, () => { + http.get('http://rsshub.test/retry-test', () => { requestRun(); return HttpResponse.error(); }) diff --git a/lib/utils/got.ts b/lib/utils/got.ts index deb9d7cbbf97..1c04dfb97072 100644 --- a/lib/utils/got.ts +++ b/lib/utils/got.ts @@ -5,7 +5,7 @@ import ofetch from '@/utils/ofetch'; import { getSearchParamsString } from './helpers'; const getFakeGot = (defaultOptions?: any) => { - const fakeGot = (request, options?: any) => { + const fakeGot = async (request, options?: any) => { if (!(typeof request === 'string' || request instanceof Request) && request.url) { options = { ...request, @@ -67,10 +67,11 @@ const getFakeGot = (defaultOptions?: any) => { const response = ofetch(request, options); if (options?.responseType === 'arrayBuffer') { - return response.then((responseData) => ({ + const responseData = await response; + return { data: Buffer.from(responseData), body: Buffer.from(responseData), - })); + }; } return response; }; diff --git a/lib/utils/puppeteer-utils.test.ts b/lib/utils/playwright-utils.test.ts similarity index 93% rename from lib/utils/puppeteer-utils.test.ts rename to lib/utils/playwright-utils.test.ts index f41f3905cd0f..4151605d5b8c 100644 --- a/lib/utils/puppeteer-utils.test.ts +++ b/lib/utils/playwright-utils.test.ts @@ -1,8 +1,8 @@ -import type { Browser } from 'rebrowser-puppeteer'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import puppeteer from '@/utils/puppeteer'; -import { constructCookieArray, getCookies, parseCookieArray, setCookies } from '@/utils/puppeteer-utils'; +import type { Browser } from '@/utils/playwright'; +import playwright from '@/utils/playwright'; +import { constructCookieArray, getCookies, parseCookieArray, setCookies } from '@/utils/playwright-utils'; let browser: Browser | null = null; @@ -15,7 +15,7 @@ afterEach(async () => { vi.resetModules(); }); -describe('puppeteer-utils', () => { +describe('browser cookie utils', () => { const cookieArrayExampleCom = [ { name: 'foobar', value: '', domain: 'example.com' }, { name: 'foo', value: 'bar', domain: 'example.com' }, @@ -69,7 +69,7 @@ describe('puppeteer-utils', () => { }); it('getCookies httpbingo', async () => { - browser = await puppeteer(); + browser = await playwright(); const page = await browser.newPage(); await page.goto('https://httpbingo.org/cookies/set?foo=bar&baz=qux', { waitUntil: 'domcontentloaded', @@ -78,7 +78,7 @@ describe('puppeteer-utils', () => { }, 45000); it('setCookies httpbingo', async () => { - browser = await puppeteer(); + browser = await playwright(); const page = await browser.newPage(); // httpbingo.org cannot recognize cookies with empty name properly, so we cannot use cookieStrAll here await setCookies(page, cookieStrExampleCom, 'httpbingo.org'); @@ -90,7 +90,7 @@ describe('puppeteer-utils', () => { }, 45000); it('setCookies & getCookies example.org', async () => { - browser = await puppeteer(); + browser = await playwright(); const page = await browser.newPage(); // we can use cookieStrAll here! await setCookies(page, cookieStrAll, 'example.org'); diff --git a/lib/utils/playwright-utils.ts b/lib/utils/playwright-utils.ts new file mode 100644 index 000000000000..433c4bd67d6d --- /dev/null +++ b/lib/utils/playwright-utils.ts @@ -0,0 +1,58 @@ +/** + * Get Cookie-header-style cookie string from a browser cookie array. + * + * @param cookies Browser cookie array + * @param {RegExp | string} domainFilter Filter cookies by domain or RegExp + * @return {string} Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") + */ +const parseCookieArray = (cookies, domainFilter?: string | RegExp) => { + if (typeof domainFilter === 'string') { + const dotDomain = '.' + domainFilter; + cookies = cookies.filter(({ domain }) => domain === domainFilter || domain.endsWith(dotDomain)); + } else if (domainFilter && domainFilter.test !== undefined) { + cookies = cookies.filter(({ domain }) => domainFilter.test(domain)); + } + // {name: '', value: 'foobar'} => 'foobar' // https://stackoverflow.com/questions/42531198/cookie-without-a-name + // {name: 'foo', value: 'bar'} => 'foo=bar' + return cookies.map(({ name, value }) => (name ? `${name}=${value}` : value)).join('; '); +}; + +/** + * Construct a browser cookie array from a Cookie-header-style cookie string. + * + * @param {string} cookieStr Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") + * @param {string} domain Domain to set for each cookie + * @return Browser cookie array + */ +const constructCookieArray = (cookieStr, domain) => + cookieStr.split('; ').map((item) => { + const [name, value] = item.split('='); + return value === undefined ? { name: '', value: name, domain } : { name, value, domain }; + }); + +/** + * Set cookies for a page + * + * @param page Browser Page object + * @param {string} cookieStr Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") + * @param {string} domain Domain to set for each cookie + * @return {Promise} + */ +const setCookies = async (page, cookieStr, domain) => { + const cookies = constructCookieArray(cookieStr, domain); + await page.setCookie(...cookies); +}; + +/** + * Get Cookie-header-style cookie string from a page + * + * @param page Browser Page object + * @param {RegExp | string} domainFilter Filter cookies by domain or RegExp + * @return {Promise} Cookie-header-style cookie string + */ +const getCookies = async (page, domainFilter?: string) => { + const cookies = await page.cookies(); + return parseCookieArray(cookies, domainFilter); +}; + +export { constructCookieArray, getCookies, parseCookieArray, setCookies }; diff --git a/lib/utils/playwright.mock.test.ts b/lib/utils/playwright.mock.test.ts new file mode 100644 index 000000000000..f09351631f99 --- /dev/null +++ b/lib/utils/playwright.mock.test.ts @@ -0,0 +1,268 @@ +import { describe, expect, it, vi } from 'vitest'; + +const connect = vi.fn(); +const connectOverCDP = vi.fn(); +const launch = vi.fn(); + +const routeContinue = vi.fn(); +const routeAbort = vi.fn(); +const route = { + request: vi.fn(), + continue: routeContinue, + abort: routeAbort, +}; + +const request = { + resourceType: vi.fn(), + url: vi.fn(), +}; + +let page: any; +let context: any; +let browser: any; + +const createBrowserMocks = () => { + page = { + context: vi.fn(), + goto: vi.fn(), + on: vi.fn(), + route: vi.fn(), + setExtraHTTPHeaders: vi.fn(), + unroute: vi.fn(), + }; + + context = { + addCookies: vi.fn(), + close: vi.fn(() => Promise.resolve()), + cookies: vi.fn(), + newPage: vi.fn(() => Promise.resolve(page)), + }; + + browser = { + close: vi.fn(), + newContext: vi.fn(() => Promise.resolve(context)), + }; +}; + +const proxyMock = { + proxyObj: { url_regex: '.*' }, + proxyUrlHandler: new URL('http://proxy.local'), + multiProxy: undefined as any, + getCurrentProxy: vi.fn(), + markProxyFailed: vi.fn(), + getDispatcherForProxy: vi.fn(), +}; + +vi.mock('playwright', () => ({ + chromium: { + connect, + connectOverCDP, + launch, + }, +})); + +vi.mock('@/utils/proxy', () => ({ + default: proxyMock, +})); + +vi.mock('@/utils/logger', () => ({ + default: { + warn: vi.fn(), + debug: vi.fn(), + }, +})); + +const loadPlaywright = async () => { + vi.resetModules(); + const mod = await import('@/utils/playwright'); + return mod.getPlaywrightPage; +}; + +const resetMocks = () => { + createBrowserMocks(); + connect.mockReset(); + connectOverCDP.mockReset(); + launch.mockReset(); + routeContinue.mockReset(); + routeAbort.mockReset(); + route.request.mockReset(); + request.resourceType.mockReset(); + request.url.mockReset(); + proxyMock.multiProxy = undefined; + proxyMock.getCurrentProxy.mockReset(); + proxyMock.markProxyFailed.mockReset(); + delete process.env.PLAYWRIGHT_WS_ENDPOINT; + delete process.env.PUPPETEER_WS_ENDPOINT; +}; + +createBrowserMocks(); + +describe('getPlaywrightPage (mocked)', () => { + it('connects via ws endpoint and runs onBeforeLoad', async () => { + resetMocks(); + connect.mockResolvedValue(browser); + connectOverCDP.mockResolvedValue(browser); + launch.mockResolvedValue(browser); + page.goto.mockResolvedValue(undefined); + browser.close.mockResolvedValue(undefined); + process.env.PLAYWRIGHT_WS_ENDPOINT = 'ws://localhost:3000/?token=abc'; + proxyMock.getCurrentProxy.mockReturnValue(null); + + const getPlaywrightPage = await loadPlaywright(); + const onBeforeLoad = vi.fn(); + const close = browser.close; + const result = await getPlaywrightPage('https://example.com', { + noGoto: true, + onBeforeLoad, + }); + + const endpoint = connect.mock.calls[0][0] as string; + expect(connectOverCDP).not.toHaveBeenCalled(); + expect(endpoint).toContain('launch-options='); + expect(endpoint).not.toContain('launch='); + const launchOptions = JSON.parse(new URL(endpoint).searchParams.get('launch-options') || '{}'); + expect(launchOptions.args).not.toContainEqual(expect.stringContaining('--user-agent=')); + expect(onBeforeLoad).toHaveBeenCalled(); + + await result.destroy(); + expect(close).toHaveBeenCalled(); + }); + + it('does not override the browser user agent', async () => { + resetMocks(); + launch.mockResolvedValue(browser); + page.goto.mockResolvedValue(undefined); + proxyMock.getCurrentProxy.mockReturnValue(null); + + const getPlaywrightPage = await loadPlaywright(); + await getPlaywrightPage('https://example.com'); + + expect(launch).toHaveBeenCalledWith( + expect.objectContaining({ + args: expect.not.arrayContaining([expect.stringContaining('--user-agent=')]), + }) + ); + expect(browser.newContext).toHaveBeenCalledWith( + expect.not.objectContaining({ + userAgent: expect.any(String), + }) + ); + }); + + it('supports extending the browser auto close timeout', async () => { + vi.useFakeTimers(); + try { + resetMocks(); + launch.mockResolvedValue(browser); + browser.close.mockResolvedValue(undefined); + page.goto.mockResolvedValue(undefined); + proxyMock.getCurrentProxy.mockReturnValue(null); + + const getPlaywrightPage = await loadPlaywright(); + const close = browser.close; + await getPlaywrightPage('https://example.com', { + closeTimeout: 90000, + noGoto: true, + }); + + await vi.advanceTimersByTimeAsync(89999); + expect(close).not.toHaveBeenCalled(); + + await vi.advanceTimersByTimeAsync(1); + expect(close).toHaveBeenCalled(); + } finally { + vi.useRealTimers(); + } + }); + + it('handles requestfinished events after the remote page closes', async () => { + resetMocks(); + launch.mockResolvedValue(browser); + page.goto.mockResolvedValue(undefined); + proxyMock.getCurrentProxy.mockReturnValue(null); + + const getPlaywrightPage = await loadPlaywright(); + const handler = vi.fn(); + const rawOn = page.on; + await getPlaywrightPage('https://example.com', { + onBeforeLoad: (page) => { + page.on('requestfinished', handler); + }, + }); + + const finishedHandler = rawOn.mock.calls.find(([event]) => event === 'requestfinished')?.[1]; + await finishedHandler?.({ + response: vi.fn().mockRejectedValue(new Error('closed')), + url: () => 'https://example.com/api', + }); + + expect(handler).toHaveBeenCalledTimes(1); + const finishedRequest = handler.mock.calls[0][0]; + expect(finishedRequest.response()).toBeNull(); + expect(finishedRequest.url()).toBe('https://example.com/api'); + }); + + it('marks proxy failed when navigation throws with multi-proxy', async () => { + resetMocks(); + launch.mockResolvedValue(browser); + page.goto.mockRejectedValueOnce(new Error('fail')); + + const currentProxy = { + uri: 'http://user:pass@proxy.local:8080', + urlHandler: new URL('http://user:pass@proxy.local:8080'), + }; + proxyMock.multiProxy = {}; + proxyMock.getCurrentProxy.mockReturnValue(currentProxy); + + const getPlaywrightPage = await loadPlaywright(); + await expect(getPlaywrightPage('https://example.com')).rejects.toThrow('fail'); + + expect(proxyMock.markProxyFailed).toHaveBeenCalledWith(currentProxy.uri); + }); + + it('maps legacy networkidle waits to playwright networkidle', async () => { + resetMocks(); + launch.mockResolvedValue(browser); + page.goto.mockResolvedValue(undefined); + proxyMock.getCurrentProxy.mockReturnValue(null); + + const getPlaywrightPage = await loadPlaywright(); + const goto = page.goto; + await getPlaywrightPage('https://example.com', { + gotoConfig: { + waitUntil: 'networkidle2', + }, + }); + + expect(goto).toHaveBeenCalledWith('https://example.com', { + waitUntil: 'networkidle', + }); + }); + + it('keeps legacy request interception helpers', async () => { + resetMocks(); + launch.mockResolvedValue(browser); + page.goto.mockResolvedValue(undefined); + proxyMock.getCurrentProxy.mockReturnValue(null); + request.resourceType.mockReturnValue('image'); + request.url.mockReturnValue('https://example.com/logo.png'); + routeAbort.mockReturnValueOnce(new Promise((resolve) => setTimeout(resolve, 0))); + route.request.mockReturnValue(request); + + const getPlaywrightPage = await loadPlaywright(); + await getPlaywrightPage('https://example.com', { + onBeforeLoad: async (page) => { + await page.setRequestInterception(true); + page.on('request', (request) => { + request.resourceType() === 'document' ? request.continue() : request.abort(); + }); + }, + }); + + const routeHandler = page.route.mock.calls[0][1]; + await routeHandler(route); + + expect(routeAbort).toHaveBeenCalled(); + expect(routeContinue).not.toHaveBeenCalled(); + }); +}); diff --git a/lib/utils/playwright.test.ts b/lib/utils/playwright.test.ts new file mode 100644 index 000000000000..e5ec76855698 --- /dev/null +++ b/lib/utils/playwright.test.ts @@ -0,0 +1,67 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import type { Browser } from '@/utils/playwright'; + +import wait from './wait'; + +let browser: Browser | null = null; + +afterEach(async () => { + if (browser) { + await browser.close(); + browser = null; + } + + delete process.env.PROXY_URI; + delete process.env.PROXY_PROTOCOL; + delete process.env.PROXY_HOST; + delete process.env.PROXY_PORT; + delete process.env.PROXY_AUTH; + delete process.env.PROXY_URL_REGEX; + + vi.resetModules(); +}); + +describe('playwright', () => { + it('playwright run', async () => { + const { default: playwright } = await import('./playwright'); + browser = await playwright(); + const startTime = Date.now(); + const page = await browser.newPage(); + await page.goto('https://www.google.com', { + waitUntil: 'domcontentloaded', + }); + + const html = await page.evaluate(() => document.body.innerHTML); + expect(html.length).toBeGreaterThan(0); + + expect(browser.isConnected()).toBe(true); + const sleepTime = 31 * 1000 - (Date.now() - startTime); + if (sleepTime > 0) { + await wait(sleepTime); + } + expect(browser.isConnected()).toBe(false); + browser = null; + }, 45000); +}); + +describe('getPlaywrightPage', () => { + it('playwright run', async () => { + const { getPlaywrightPage } = await import('./playwright'); + const playwright = await getPlaywrightPage('https://www.google.com'); + const page = playwright.page; + browser = playwright.browser; + const startTime = Date.now(); + + const html = await page.evaluate(() => document.body.innerHTML); + expect(html.length).toBeGreaterThan(0); + + expect(browser.isConnected()).toBe(true); + const sleepTime = 31 * 1000 - (Date.now() - startTime); + if (sleepTime > 0) { + await wait(sleepTime); + } + expect(browser.isConnected()).toBe(false); + browser = null; + }, 45000); +}); diff --git a/lib/utils/playwright.ts b/lib/utils/playwright.ts new file mode 100644 index 000000000000..25b4a65da984 --- /dev/null +++ b/lib/utils/playwright.ts @@ -0,0 +1,341 @@ +import type { Browser as PlaywrightBrowser, BrowserContext, BrowserContextOptions, LaunchOptions, Page as PlaywrightPage, Request as PlaywrightRequest, Response as PlaywrightResponse, Route } from 'playwright'; +import { chromium } from 'playwright'; + +import { config } from '@/config'; + +import logger from './logger'; +import proxy from './proxy'; + +type SetCookieParam = Parameters[0][number]; +type Cookie = Awaited>[number]; +type GotoOptions = Parameters[1] & { + waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'networkidle0' | 'networkidle2'; +}; + +type ProxyState = NonNullable>; + +type RouteRequest = { + abort: (errorCode?: string) => Promise; + continue: (options?: Parameters[0]) => Promise; + resourceType: () => ReturnType; + url: () => string; +}; + +type FinishedRequest = { + response: () => { + status: () => number; + } | null; + url: () => string; +}; + +type RequestHandler = (request: RouteRequest) => Promise | void; +type RequestFinishedHandler = (request: FinishedRequest) => Promise | void; +type HandledRouteRequest = RouteRequest & { handled: boolean }; + +export type Page = PlaywrightPage & { + authenticate: (credentials: { password?: string; username?: string }) => Promise; + cookies: (urls?: string | string[]) => Promise; + goto: (url: string, options?: GotoOptions) => ReturnType; + on: ((event: 'request', handler: RequestHandler) => Page) & ((event: 'requestfinished', handler: RequestFinishedHandler) => Page) & PlaywrightPage['on']; + setCookie: (...cookies: SetCookieParam[]) => Promise; + setRequestInterception: (enabled: boolean) => Promise; + setUserAgent: (userAgent: string) => Promise; +}; + +export type Browser = PlaywrightBrowser & { + cookies: (urls?: string | string[]) => Promise; + newPage: () => Promise; + setCookie: (...cookies: SetCookieParam[]) => Promise; + userAgent: () => string; +}; + +const normalizeWaitUntil = (waitUntil: GotoOptions['waitUntil']) => (waitUntil === 'networkidle0' || waitUntil === 'networkidle2' ? 'networkidle' : waitUntil); + +const normalizeGotoOptions = (options?: GotoOptions): Parameters[1] | undefined => + options + ? { + ...options, + waitUntil: normalizeWaitUntil(options.waitUntil), + } + : options; + +const withDefaultCookiePath = (cookie: SetCookieParam): SetCookieParam => ('domain' in cookie && !('path' in cookie) ? { ...cookie, path: '/' } : cookie); + +const proxyServerFromUrl = (proxyUrl: URL) => { + const protocol = proxyUrl.protocol.replace('socks5h:', 'socks5:').replace('socks4a:', 'socks4:'); + return `${protocol}//${proxyUrl.host}`; +}; + +const getProxyOptions = (currentProxy: ProxyState | null | undefined) => { + if (!currentProxy) { + return {}; + } + + const username = currentProxy.urlHandler?.username; + const password = currentProxy.urlHandler?.password; + if (username || password) { + if (currentProxy.urlHandler.protocol !== 'http:') { + logger.warn('SOCKS/HTTPS proxy with authentication is not supported by playwright, continue without proxy'); + return {}; + } + + return { + proxy: { + password: decodeURIComponent(password ?? ''), + server: proxyServerFromUrl(currentProxy.urlHandler), + username: decodeURIComponent(username ?? ''), + }, + } satisfies Pick; + } + + return { + proxy: { + server: currentProxy.uri.replace('socks5h://', 'socks5://').replace('socks4a://', 'socks4://').replace(/\/$/, ''), + }, + } satisfies Pick; +}; + +const getLaunchOptions = (currentProxy?: ProxyState | null): LaunchOptions => ({ + args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', '--window-position=0,0', '--ignore-certificate-errors', '--ignore-certificate-errors-spki-list'], + executablePath: config.chromiumExecutablePath || undefined, + headless: true, + ...getProxyOptions(currentProxy), +}); + +const getContextOptions = (): BrowserContextOptions => ({ + ignoreHTTPSErrors: true, +}); + +const createRouteRequest = (route: Route): HandledRouteRequest => { + const request = route.request(); + const routeRequest = { + abort: async (errorCode) => { + routeRequest.handled = true; + await route.abort(errorCode); + }, + continue: async (options) => { + routeRequest.handled = true; + await route.continue(options); + }, + handled: false, + resourceType: () => request.resourceType(), + url: () => request.url(), + }; + return routeRequest; +}; + +const runRequestHandlers = async (handlers: RequestHandler[], request: HandledRouteRequest, index = 0): Promise => { + if (request.handled || index >= handlers.length) { + return; + } + + await handlers[index](request); + await runRequestHandlers(handlers, request, index + 1); +}; + +const createFinishedRequest = (request: PlaywrightRequest, response: PlaywrightResponse | null): FinishedRequest => ({ + response: () => + response + ? { + status: () => response.status(), + } + : null, + url: () => request.url(), +}); + +const patchPage = (page: PlaywrightPage, context: BrowserContext): Page => { + const compatPage = page as Page; + const requestHandlers: RequestHandler[] = []; + const originalGoto = page.goto.bind(page); + const originalOn = page.on.bind(page); + const originalRoute = page.route.bind(page); + const originalUnroute = page.unroute.bind(page); + let routeHandler: ((route: Route) => Promise) | undefined; + let requestInterceptionEnabled = false; + + compatPage.goto = (url, options) => originalGoto(url, normalizeGotoOptions(options)); + compatPage.cookies = (urls) => context.cookies(urls); + compatPage.setCookie = async (...cookies) => { + await context.addCookies(cookies.map((cookie) => withDefaultCookiePath(cookie))); + }; + compatPage.authenticate = async () => {}; + compatPage.setUserAgent = async (userAgent) => { + const contextWithCDP = context as BrowserContext & { + newCDPSession?: (page: PlaywrightPage) => Promise<{ + detach: () => Promise; + send: (method: string, params?: Record) => Promise; + }>; + }; + if (contextWithCDP.newCDPSession) { + const session = await contextWithCDP.newCDPSession(page); + await session.send('Network.setUserAgentOverride', { userAgent }); + await session.detach(); + return; + } + await page.setExtraHTTPHeaders({ + 'User-Agent': userAgent, + }); + }; + compatPage.setRequestInterception = async (enabled) => { + requestInterceptionEnabled = enabled; + if (enabled && !routeHandler) { + routeHandler = async (route) => { + const request = createRouteRequest(route); + await runRequestHandlers(requestHandlers, request); + if (request.handled) { + return; + } + await route.continue(); + }; + await originalRoute('**/*', routeHandler); + } else if (!enabled && routeHandler) { + await originalUnroute('**/*', routeHandler); + routeHandler = undefined; + } + }; + compatPage.on = ((event: string, handler: (...args: any[]) => any) => { + if (event === 'request') { + requestHandlers.push(handler as RequestHandler); + if (!requestInterceptionEnabled) { + originalOn(event, handler); + } + return compatPage; + } + if (event === 'requestfinished') { + originalOn(event, async (request) => { + let response: PlaywrightResponse | null = null; + try { + response = await request.response(); + } catch { + // The remote browser may close before Playwright resolves the response. + } + await (handler as RequestFinishedHandler)(createFinishedRequest(request, response)); + }); + return compatPage; + } + originalOn(event, handler); + return compatPage; + }) as Page['on']; + + return compatPage; +}; + +const createCompatBrowser = async (browser: PlaywrightBrowser, contextOptions: BrowserContextOptions): Promise => { + const context = await browser.newContext(contextOptions); + const compatBrowser = browser as Browser; + const originalClose = browser.close.bind(browser); + + compatBrowser.newPage = async () => patchPage(await context.newPage(), context); + compatBrowser.setCookie = async (...cookies) => { + await context.addCookies(cookies.map((cookie) => withDefaultCookiePath(cookie))); + }; + compatBrowser.cookies = (urls) => context.cookies(urls); + compatBrowser.userAgent = () => config.ua; + compatBrowser.close = async (options) => { + try { + await context.close(); + } catch { + // Ignore already-closed contexts. + } + await originalClose(options); + }; + + return compatBrowser; +}; + +const launchBrowser = async (currentProxy?: ProxyState | null) => { + const launchOptions = getLaunchOptions(currentProxy); + const browser = config.playwrightWSEndpoint ? await chromium.connect(getEndpointWithLaunchOptions(config.playwrightWSEndpoint, launchOptions)) : await chromium.launch(launchOptions); + return createCompatBrowser(browser, getContextOptions()); +}; + +const getEndpointWithLaunchOptions = (endpoint: string, launchOptions: LaunchOptions) => { + const endpointURL = new URL(endpoint); + endpointURL.searchParams.set('launch-options', JSON.stringify(launchOptions)); + return endpointURL.toString(); +}; + +const scheduleClose = (browser: Browser, timeout = 30000) => { + setTimeout(() => { + void browser.close(); + }, timeout); +}; + +/** + * @returns Playwright browser + */ +const outPlaywright = async () => { + const currentProxy = proxy.getCurrentProxy(); + const browser = await launchBrowser(currentProxy && proxy.proxyObj.url_regex === '.*' ? currentProxy : null); + scheduleClose(browser); + return browser; +}; + +export default outPlaywright; + +// No-op in Node.js environment (used by Worker build via alias) +export const setBrowserBinding = (_binding: any) => {}; + +/** + * @returns Playwright page + */ +export const getPlaywrightPage = async ( + url: string, + instanceOptions: { + closeTimeout?: number; + gotoConfig?: GotoOptions; + noGoto?: boolean; + onBeforeLoad?: (page: Page, browser?: Browser) => Promise | void; + } = {} +) => { + let allowProxy = false; + const proxyRegex = new RegExp(proxy.proxyObj.url_regex); + let urlHandler: URL | undefined; + try { + urlHandler = new URL(url); + } catch { + // ignore invalid URLs such as about:blank + } + + if (proxyRegex.test(url) && url.startsWith('http') && !(urlHandler && urlHandler.host === proxy.proxyUrlHandler?.host)) { + allowProxy = true; + } + + const currentProxy = proxy.getCurrentProxy(); + const currentProxyState = currentProxy && allowProxy ? currentProxy : null; + const hasProxy = Boolean(getProxyOptions(currentProxyState).proxy); + const browser = await launchBrowser(currentProxyState); + scheduleClose(browser, instanceOptions.closeTimeout); + const page = await browser.newPage(); + + if (hasProxy && currentProxyState) { + logger.debug(`Proxying request in playwright via ${currentProxyState.uri}: ${url}`); + } + + if (instanceOptions.onBeforeLoad) { + await instanceOptions.onBeforeLoad(page, browser); + } + + 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; + } + throw error; + } + } + + return { + browser, + destroy: async () => { + await browser.close(); + }, + page, + }; +}; + +export const getPuppeteerPage = getPlaywrightPage; diff --git a/lib/utils/playwright.worker.ts b/lib/utils/playwright.worker.ts new file mode 100644 index 000000000000..bdf49429ba3f --- /dev/null +++ b/lib/utils/playwright.worker.ts @@ -0,0 +1,255 @@ +// Worker-compatible Playwright using Cloudflare Browser Run. +import type { Browser as PlaywrightBrowser, BrowserContext, Page as PlaywrightPage, Request as PlaywrightRequest, Response as PlaywrightResponse, Route } from '@cloudflare/playwright'; +import { launch } from '@cloudflare/playwright'; + +import { config } from '@/config'; + +import logger from './logger'; + +type SetCookieParam = Parameters[0][number]; +type Cookie = Awaited>[number]; +type GotoOptions = Parameters[1] & { + waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'networkidle0' | 'networkidle2'; +}; + +type RouteRequest = { + abort: (errorCode?: string) => Promise; + continue: (options?: Parameters[0]) => Promise; + resourceType: () => ReturnType; + url: () => string; +}; + +type FinishedRequest = { + response: () => { + status: () => number; + } | null; + url: () => string; +}; + +type RequestHandler = (request: RouteRequest) => Promise | void; +type RequestFinishedHandler = (request: FinishedRequest) => Promise | void; +type HandledRouteRequest = RouteRequest & { handled: boolean }; + +export type Page = PlaywrightPage & { + authenticate: (credentials: { password?: string; username?: string }) => Promise; + cookies: (urls?: string | string[]) => Promise; + goto: (url: string, options?: GotoOptions) => ReturnType; + on: ((event: 'request', handler: RequestHandler) => Page) & ((event: 'requestfinished', handler: RequestFinishedHandler) => Page) & PlaywrightPage['on']; + setCookie: (...cookies: SetCookieParam[]) => Promise; + setRequestInterception: (enabled: boolean) => Promise; + setUserAgent: (userAgent: string) => Promise; +}; + +export type Browser = PlaywrightBrowser & { + cookies: (urls?: string | string[]) => Promise; + newPage: () => Promise; + setCookie: (...cookies: SetCookieParam[]) => Promise; + userAgent: () => string; +}; + +let browserBinding: any = null; + +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.'); + } + return browserBinding; +}; + +const normalizeWaitUntil = (waitUntil: GotoOptions['waitUntil']) => (waitUntil === 'networkidle0' || waitUntil === 'networkidle2' ? 'networkidle' : waitUntil); + +const normalizeGotoOptions = (options?: GotoOptions): Parameters[1] | undefined => + options + ? { + ...options, + waitUntil: normalizeWaitUntil(options.waitUntil), + } + : options; + +const withDefaultCookiePath = (cookie: SetCookieParam): SetCookieParam => ('domain' in cookie && !('path' in cookie) ? { ...cookie, path: '/' } : cookie); + +const createRouteRequest = (route: Route): HandledRouteRequest => { + const request = route.request(); + const routeRequest = { + abort: async (errorCode) => { + routeRequest.handled = true; + await route.abort(errorCode); + }, + continue: async (options) => { + routeRequest.handled = true; + await route.continue(options); + }, + handled: false, + resourceType: () => request.resourceType(), + url: () => request.url(), + }; + return routeRequest; +}; + +const runRequestHandlers = async (handlers: RequestHandler[], request: HandledRouteRequest, index = 0): Promise => { + if (request.handled || index >= handlers.length) { + return; + } + + await handlers[index](request); + await runRequestHandlers(handlers, request, index + 1); +}; + +const createFinishedRequest = (request: PlaywrightRequest, response: PlaywrightResponse | null): FinishedRequest => ({ + response: () => + response + ? { + status: () => response.status(), + } + : null, + url: () => request.url(), +}); + +const patchPage = (page: PlaywrightPage, context: BrowserContext): Page => { + const compatPage = page as Page; + const requestHandlers: RequestHandler[] = []; + const originalGoto = page.goto.bind(page); + const originalOn = page.on.bind(page); + const originalRoute = page.route.bind(page); + const originalUnroute = page.unroute.bind(page); + let routeHandler: ((route: Route) => Promise) | undefined; + let requestInterceptionEnabled = false; + + compatPage.goto = (url, options) => originalGoto(url, normalizeGotoOptions(options)); + compatPage.cookies = (urls) => context.cookies(urls); + compatPage.setCookie = async (...cookies) => { + await context.addCookies(cookies.map((cookie) => withDefaultCookiePath(cookie))); + }; + compatPage.authenticate = async () => {}; + compatPage.setUserAgent = async (userAgent) => { + await page.setExtraHTTPHeaders({ + 'User-Agent': userAgent, + }); + }; + compatPage.setRequestInterception = async (enabled) => { + requestInterceptionEnabled = enabled; + if (enabled && !routeHandler) { + routeHandler = async (route) => { + const request = createRouteRequest(route); + await runRequestHandlers(requestHandlers, request); + if (request.handled) { + return; + } + await route.continue(); + }; + await originalRoute('**/*', routeHandler); + } else if (!enabled && routeHandler) { + await originalUnroute('**/*', routeHandler); + routeHandler = undefined; + } + }; + compatPage.on = ((event: string, handler: (...args: any[]) => any) => { + if (event === 'request') { + requestHandlers.push(handler as RequestHandler); + if (!requestInterceptionEnabled) { + originalOn(event, handler); + } + return compatPage; + } + if (event === 'requestfinished') { + originalOn(event, async (request) => { + const response = await request.response(); + await (handler as RequestFinishedHandler)(createFinishedRequest(request, response)); + }); + return compatPage; + } + originalOn(event, handler); + return compatPage; + }) as Page['on']; + + return compatPage; +}; + +const createCompatBrowser = async (browser: PlaywrightBrowser): Promise => { + const context = await browser.newContext({ + ignoreHTTPSErrors: true, + }); + const compatBrowser = browser as Browser; + const originalClose = browser.close.bind(browser); + + compatBrowser.newPage = async () => patchPage(await context.newPage(), context); + compatBrowser.setCookie = async (...cookies) => { + await context.addCookies(cookies.map((cookie) => withDefaultCookiePath(cookie))); + }; + compatBrowser.cookies = (urls) => context.cookies(urls); + compatBrowser.userAgent = () => config.ua; + compatBrowser.close = async (options) => { + try { + await context.close(); + } catch { + // Ignore already-closed contexts. + } + await originalClose(options); + }; + + return compatBrowser; +}; + +const launchBrowser = async () => createCompatBrowser(await launch(getBrowserBinding(), { keep_alive: 60000 })); + +const scheduleClose = (browser: Browser) => { + setTimeout(() => { + void browser.close(); + }, 30000); +}; + +/** + * @returns Playwright browser + */ +const outPlaywright = async () => { + const browser = await launchBrowser(); + scheduleClose(browser); + return browser; +}; + +export default outPlaywright; + +/** + * @returns Playwright page + */ +export const getPlaywrightPage = async ( + url: string, + instanceOptions: { + gotoConfig?: GotoOptions; + noGoto?: boolean; + onBeforeLoad?: (page: Page, browser?: Browser) => Promise | void; + } = {} +) => { + logger.debug(`Launching Cloudflare Browser for: ${url}`); + + const browser = await launchBrowser(); + scheduleClose(browser); + const page = await browser.newPage(); + + if (instanceOptions.onBeforeLoad) { + await instanceOptions.onBeforeLoad(page, browser); + } + + if (!instanceOptions.noGoto) { + try { + await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); + } catch (error) { + logger.error(`Playwright navigation failed: ${error}`); + throw error; + } + } + + return { + browser, + destroy: async () => { + await browser.close(); + }, + page, + }; +}; + +export const getPuppeteerPage = getPlaywrightPage; diff --git a/lib/utils/proxy/pac-proxy.ts b/lib/utils/proxy/pac-proxy.ts index 09c18a31bd1e..ec7daa36f3ee 100644 --- a/lib/utils/proxy/pac-proxy.ts +++ b/lib/utils/proxy/pac-proxy.ts @@ -49,7 +49,7 @@ const pacProxy = (pacUri: Config['pacUri'], pacScript: Config['pacScript'], prox logger.warn('PAC_URI contains username and/or password, ignoring PROXY_AUTH'); proxyObj.auth = undefined; } else if (['http:', 'https:'].includes(pacUrlHandler.protocol)) { - logger.info('PROXY_AUTH is set and will be used for requests from Node.js. However, requests from puppeteer will not use it'); + logger.info('PROXY_AUTH is set and will be used for requests from Node.js. However, requests from Playwright will not use it'); promptProxyUri = true; } else { logger.warn(`PROXY_AUTH is only supported by HTTP(S) proxies, but got ${pacUrlHandler.protocol}, ignoring`); diff --git a/lib/utils/proxy/unify-proxy.ts b/lib/utils/proxy/unify-proxy.ts index f4c17bbab702..bbdff834ef09 100644 --- a/lib/utils/proxy/unify-proxy.ts +++ b/lib/utils/proxy/unify-proxy.ts @@ -47,7 +47,7 @@ const unifyProxy = (proxyUri: Config['proxyUri'] | string, proxyObj: Config['pro if (Number.parseInt(proxyObj.port)) { proxyUrlHandler.port = proxyObj.port; } else { - logger.warn(`PROXY_PORT is not a number, ignoring`); + logger.warn('PROXY_PORT is not a number, ignoring'); } } else { logger.warn('PROXY_PORT is not set, leaving proxy agent to determine'); @@ -67,7 +67,7 @@ const unifyProxy = (proxyUri: Config['proxyUri'] | string, proxyObj: Config['pro logger.warn('PROXY_URI contains username and/or password, ignoring PROXY_AUTH'); proxyObj.auth = undefined; } else if (['http:', 'https:'].includes(proxyUrlHandler.protocol)) { - logger.info('PROXY_AUTH is set and will be used for requests from Node.js. However, requests from puppeteer will not use it'); + logger.info('PROXY_AUTH is set and will be used for requests from Node.js. However, requests from Playwright will not use it'); promptProxyUri = true; } else { logger.warn(`PROXY_AUTH is only supported by HTTP(S) proxies, but got ${proxyUrlHandler.protocol}, ignoring`); @@ -85,13 +85,13 @@ const unifyProxy = (proxyUri: Config['proxyUri'] | string, proxyObj: Config['pro const protocol = proxyUrlHandler.protocol.replace(':', ''); if (possibleProtocol.includes(protocol)) { if (protocol !== 'http' && (proxyUrlHandler.username || proxyUrlHandler.password)) { - logger.warn("PROXY_URI is an HTTPS/SOCKS proxy with authentication, which is not supported by puppeteer (ignore if you don't need it)"); + logger.warn("PROXY_URI is an HTTPS/SOCKS proxy with authentication, which is not supported by Playwright (ignore if you don't need it)"); logger.info('To get rid of this, consider using an HTTP proxy instead'); } proxyObj.protocol = protocol; proxyObj.host = proxyUrlHandler.hostname; proxyObj.port = proxyUrlHandler.port || undefined; - // trailing slash will cause puppeteer to throw net::ERR_NO_SUPPORTED_PROXIES, trim it + // Trailing slash can make Chromium reject the proxy URL, trim it. proxyUri = proxyUrlHandler.href.endsWith('/') ? proxyUrlHandler.href.slice(0, -1) : proxyUrlHandler.href; isProxyValid = true; } else { diff --git a/lib/utils/puppeteer-utils.ts b/lib/utils/puppeteer-utils.ts index b421a1405c76..244e17066547 100644 --- a/lib/utils/puppeteer-utils.ts +++ b/lib/utils/puppeteer-utils.ts @@ -1,58 +1 @@ -/** - * Get Cookie-header-style cookie string from a puppeteer-style cookie array - * - * @param {import('puppeteer').Protocol.Network.CookieParam[]} cookies Puppeteer-style cookie array - * @param {RegExp | string} domainFilter Filter cookies by domain or RegExp - * @return {string} Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") - */ -const parseCookieArray = (cookies, domainFilter?: string | RegExp) => { - if (typeof domainFilter === 'string') { - const dotDomain = '.' + domainFilter; - cookies = cookies.filter(({ domain }) => domain === domainFilter || domain.endsWith(dotDomain)); - } else if (domainFilter && domainFilter.test !== undefined) { - cookies = cookies.filter(({ domain }) => domainFilter.test(domain)); - } - // {name: '', value: 'foobar'} => 'foobar' // https://stackoverflow.com/questions/42531198/cookie-without-a-name - // {name: 'foo', value: 'bar'} => 'foo=bar' - return cookies.map(({ name, value }) => (name ? `${name}=${value}` : value)).join('; '); -}; - -/** - * Construct a puppeteer-style cookie array from a Cookie-header-style cookie string - * - * @param {string} cookieStr Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") - * @param {string} domain Domain to set for each cookie - * @return {import('puppeteer').Protocol.Network.CookieParam[]} Puppeteer-style cookie array - */ -const constructCookieArray = (cookieStr, domain) => - cookieStr.split('; ').map((item) => { - const [name, value] = item.split('='); - return value === undefined ? { name: '', value: name, domain } : { name, value, domain }; - }); - -/** - * Set cookies for a page - * - * @param {import('puppeteer').Page} page Puppeteer Page object - * @param {string} cookieStr Cookie-header-style cookie string (e.g. "foobar; foo=bar; baz=qux") - * @param {string} domain Domain to set for each cookie - * @return {Promise} - */ -const setCookies = async (page, cookieStr, domain) => { - const cookies = constructCookieArray(cookieStr, domain); - await page.setCookie(...cookies); -}; - -/** - * Get Cookie-header-style cookie string from a page - * - * @param {import('puppeteer').Page} page Puppeteer Page object - * @param {RegExp | string} domainFilter Filter cookies by domain or RegExp - * @return {Promise} Cookie-header-style cookie string - */ -const getCookies = async (page, domainFilter?: string) => { - const cookies = await page.cookies(); - return parseCookieArray(cookies, domainFilter); -}; - -export { constructCookieArray, getCookies, parseCookieArray, setCookies }; +export { constructCookieArray, getCookies, parseCookieArray, setCookies } from './playwright-utils'; diff --git a/lib/utils/puppeteer.mock.test.ts b/lib/utils/puppeteer.mock.test.ts deleted file mode 100644 index a4c8e46de892..000000000000 --- a/lib/utils/puppeteer.mock.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -const connect = vi.fn(); -const launch = vi.fn(); - -const page = { - goto: vi.fn(), - authenticate: vi.fn(), -}; - -const browser = { - newPage: vi.fn(() => Promise.resolve(page)), - close: vi.fn(), -}; - -const proxyMock = { - proxyObj: { url_regex: '.*' }, - proxyUrlHandler: new URL('http://proxy.local'), - multiProxy: undefined as any, - getCurrentProxy: vi.fn(), - markProxyFailed: vi.fn(), - getDispatcherForProxy: vi.fn(), -}; - -vi.mock('rebrowser-puppeteer', () => ({ - default: { - connect, - launch, - }, -})); - -vi.mock('@/utils/proxy', () => ({ - default: proxyMock, -})); - -vi.mock('@/utils/logger', () => ({ - default: { - warn: vi.fn(), - debug: vi.fn(), - }, -})); - -const loadPuppeteer = async () => { - vi.resetModules(); - const mod = await import('@/utils/puppeteer'); - return mod.getPuppeteerPage; -}; - -describe('getPuppeteerPage (mocked)', () => { - it('connects via ws endpoint and runs onBeforeLoad', async () => { - connect.mockResolvedValue(browser); - launch.mockResolvedValue(browser); - page.goto.mockResolvedValue(undefined); - page.authenticate.mockResolvedValue(undefined); - browser.close.mockResolvedValue(undefined); - - process.env.PUPPETEER_WS_ENDPOINT = 'ws://localhost:3000/?token=abc'; - proxyMock.getCurrentProxy.mockReturnValue(null); - - const getPuppeteerPage = await loadPuppeteer(); - const onBeforeLoad = vi.fn(); - const result = await getPuppeteerPage('https://example.com', { - noGoto: true, - onBeforeLoad, - }); - - const endpoint = connect.mock.calls[0][0].browserWSEndpoint as string; - expect(endpoint).toContain('launch='); - expect(endpoint).toContain('stealth=true'); - expect(onBeforeLoad).toHaveBeenCalled(); - - await result.destroy(); - expect(browser.close).toHaveBeenCalled(); - - delete process.env.PUPPETEER_WS_ENDPOINT; - }); - - it('marks proxy failed when navigation throws with multi-proxy', async () => { - connect.mockResolvedValue(browser); - launch.mockResolvedValue(browser); - page.goto.mockRejectedValueOnce(new Error('fail')); - page.authenticate.mockResolvedValue(undefined); - - const currentProxy = { - uri: 'http://user:pass@proxy.local:8080', - urlHandler: new URL('http://user:pass@proxy.local:8080'), - }; - proxyMock.multiProxy = {}; - proxyMock.getCurrentProxy.mockReturnValue(currentProxy); - - const getPuppeteerPage = await loadPuppeteer(); - await expect(getPuppeteerPage('https://example.com')).rejects.toThrow('fail'); - - expect(proxyMock.markProxyFailed).toHaveBeenCalledWith(currentProxy.uri); - }); - - it('rethrows navigation errors without multi-proxy', async () => { - connect.mockResolvedValue(browser); - launch.mockResolvedValue(browser); - page.goto.mockRejectedValueOnce(new Error('fail')); - - proxyMock.multiProxy = undefined; - proxyMock.getCurrentProxy.mockReturnValue(null); - - const getPuppeteerPage = await loadPuppeteer(); - await expect(getPuppeteerPage('https://example.com')).rejects.toThrow('fail'); - }); -}); diff --git a/lib/utils/puppeteer.test.ts b/lib/utils/puppeteer.test.ts deleted file mode 100644 index bb2bd2b3a752..000000000000 --- a/lib/utils/puppeteer.test.ts +++ /dev/null @@ -1,254 +0,0 @@ -import type { Browser } from 'rebrowser-puppeteer'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -import wait from './wait'; - -let browser: Browser | null = null; - -afterEach(async () => { - if (browser) { - // double insurance to close unclosed browser immediately after each test - // if a test closure fails before it can close the browser, the browser process will probably be unclosed, - // especially when the test unit is run through `npm run vitest puppeteer` - await browser.close(); - browser = null; - } - delete process.env.PROXY_URI; - delete process.env.PROXY_PROTOCOL; - delete process.env.PROXY_HOST; - delete process.env.PROXY_PORT; - delete process.env.PROXY_AUTH; - delete process.env.PROXY_URL_REGEX; - - vi.resetModules(); -}); - -describe('puppeteer', () => { - it('puppeteer run', async () => { - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - const startTime = Date.now(); - const page = await browser.newPage(); - await page.goto('https://www.google.com', { - waitUntil: 'domcontentloaded', - }); - - const html = await page.evaluate(() => document.body.innerHTML); - expect(html.length).toBeGreaterThan(0); - - expect(browser.process()?.exitCode).toBe(null); // browser is still running - const sleepTime = 31 * 1000 - (Date.now() - startTime); // prevent long loading time from failing the test - if (sleepTime > 0) { - await wait(sleepTime); - } - expect(browser.process()?.exitCode).toBe(0); // browser is closed - browser = null; - }, 45000); - - // if (!process.env.GITHUB_ACTIONS) { - it('puppeteer stealth test', async () => { - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - const page = await browser.newPage(); - await page.goto('https://bot.sannysoft.com', { waitUntil: 'networkidle0' }); - // page rendering is not instant, wait for expected elements to appear - const [webDriverTest, chromeTest] = await Promise.all(['webdriver', 'chrome'].map((t) => page.waitForSelector(`td#${t}-result.result.passed`).then((hd) => hd?.evaluate((e) => e.textContent)))); - // these are something we really care about - expect(webDriverTest).toBe('missing (passed)'); - expect(chromeTest).toBe('present (passed)'); - }, 45000); - // } - - it('puppeteer accept http proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'http://user:pass@rsshub.proxy:2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - // trailing slash will cause net::ERR_NO_SUPPORTED_PROXIES, prohibit it - expect(browser.process()?.spawnargs.some((arg) => /^--proxy-server=http:\/\/.*[^/]$/.test(arg))).toBe(true); - }); - - it('puppeteer reject https proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'https://user:pass@rsshub.proxy:2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => arg.includes('--proxy-server'))).toBe(false); - }); - - it('puppeteer reject socks proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'socks5://user:pass@rsshub.proxy:2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => arg.includes('--proxy-server'))).toBe(false); - }); - - it('puppeteer accept http proxy', async () => { - process.env.PROXY_PROTOCOL = 'http'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => /^--proxy-server=http:\/\/rsshub.proxy:2333$/.test(arg))).toBe(true); - }, 10000); - - it('puppeteer accept https proxy', async () => { - process.env.PROXY_PROTOCOL = 'https'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => /^--proxy-server=https:\/\/rsshub.proxy:2333$/.test(arg))).toBe(true); - }, 10000); - - it('puppeteer accept socks4a proxy', async () => { - process.env.PROXY_PROTOCOL = 'socks4a'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => /^--proxy-server=socks4:\/\/rsshub.proxy:2333$/.test(arg))).toBe(true); - }, 10000); - - it('puppeteer accept socks5h proxy', async () => { - process.env.PROXY_PROTOCOL = 'socks5h'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { default: puppeteer } = await import('./puppeteer'); - browser = await puppeteer(); - - expect(browser.process()?.spawnargs.some((arg) => /^--proxy-server=socks5:\/\/rsshub.proxy:2333$/.test(arg))).toBe(true); - }, 10000); -}); - -describe('getPuppeteerPage', () => { - it('puppeteer run', async () => { - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com'); - const page = pup.page; - browser = pup.browser; - const startTime = Date.now(); - - const html = await page.evaluate(() => document.body.innerHTML); - expect(html.length).toBeGreaterThan(0); - - expect(browser.process()?.exitCode).toBe(null); // browser is still running - const sleepTime = 31 * 1000 - (Date.now() - startTime); // prevent long loading time from failing the test - if (sleepTime > 0) { - await wait(sleepTime); - } - expect(browser.process()?.exitCode).toBe(0); // browser is closed - }, 45000); - - it('puppeteer accept http proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'http://user:pass@rsshub.proxy:2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com', { - noGoto: true, - }); - browser = pup.browser; - - // trailing slash will cause net::ERR_NO_SUPPORTED_PROXIES, prohibit it - expect(browser.process()?.spawnargs.includes('--proxy-server=http://rsshub.proxy:2333')).toBe(true); - }); - - it('puppeteer respect proxy regex', async () => { - process.env.PROXY_URI = 'http://user:pass@rsshub.proxy:2333'; - process.env.PROXY_URL_REGEX = 'not-exist'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com'); - browser = pup.browser; - - // trailing slash will cause net::ERR_NO_SUPPORTED_PROXIES, prohibit it - expect(browser.process()?.spawnargs.includes('--proxy-server=http://rsshub.proxy:2333')).toBe(false); - }); - - it('puppeteer reject https proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'https://user:pass@rsshub.proxy:2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com'); - browser = pup.browser; - - expect(browser.process()?.spawnargs.some((arg) => arg.includes('--proxy-server'))).toBe(false); - }); - - it('puppeteer reject socks proxy uri w/ auth', async () => { - process.env.PROXY_URI = 'socks5://user:pass@rsshub.proxy:2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com'); - browser = pup.browser; - - expect(browser.process()?.spawnargs.some((arg) => arg.includes('--proxy-server'))).toBe(false); - }); - - it('puppeteer accept http proxy', async () => { - process.env.PROXY_PROTOCOL = 'http'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com', { - noGoto: true, - }); - browser = pup.browser; - - expect(browser.process()?.spawnargs.includes('--proxy-server=http://rsshub.proxy:2333')).toBe(true); - }, 10000); - - it('puppeteer accept https proxy', async () => { - process.env.PROXY_PROTOCOL = 'https'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com', { - noGoto: true, - }); - browser = pup.browser; - - expect(browser.process()?.spawnargs.includes('--proxy-server=https://rsshub.proxy:2333')).toBe(true); - }, 10000); - - it('puppeteer accept socks4a proxy', async () => { - process.env.PROXY_PROTOCOL = 'socks4a'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com', { - noGoto: true, - }); - browser = pup.browser; - - expect(browser.process()?.spawnargs.includes('--proxy-server=socks4://rsshub.proxy:2333')).toBe(true); - }, 10000); - - it('puppeteer accept socks5h proxy', async () => { - process.env.PROXY_PROTOCOL = 'socks5h'; - process.env.PROXY_HOST = 'rsshub.proxy'; - process.env.PROXY_PORT = '2333'; - - const { getPuppeteerPage } = await import('./puppeteer'); - const pup = await getPuppeteerPage('https://www.google.com', { - noGoto: true, - }); - browser = pup.browser; - - expect(browser.process()?.spawnargs.includes('--proxy-server=socks5://rsshub.proxy:2333')).toBe(true); - }, 10000); -}); diff --git a/lib/utils/puppeteer.ts b/lib/utils/puppeteer.ts index 2888aaf65f81..117b4cd4de0f 100644 --- a/lib/utils/puppeteer.ts +++ b/lib/utils/puppeteer.ts @@ -1,195 +1,2 @@ -import { anonymizeProxy } from 'proxy-chain'; -import type { Browser, Page } from 'rebrowser-puppeteer'; -import puppeteer from 'rebrowser-puppeteer'; - -import { config } from '@/config'; - -import logger from './logger'; -import proxy from './proxy'; - -/** - * @deprecated use getPage instead - * @returns Puppeteer browser - */ -const outPuppeteer = async () => { - const options = { - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - '--disable-blink-features=AutomationControlled', - '--window-position=0,0', - '--ignore-certificate-errors', - '--ignore-certificate-errors-spki-list', - `--user-agent=${config.ua}`, - ], - headless: true, - ignoreHTTPSErrors: true, - }; - - const insidePuppeteer: typeof puppeteer = puppeteer; - - const currentProxy = proxy.getCurrentProxy(); - if (currentProxy && proxy.proxyObj.url_regex === '.*') { - if (currentProxy.urlHandler?.username || currentProxy.urlHandler?.password) { - // only proxies with authentication need to be anonymized - if (currentProxy.urlHandler.protocol === 'http:') { - options.args.push(`--proxy-server=${await anonymizeProxy(currentProxy.uri)}`); - } else { - logger.warn('SOCKS/HTTPS proxy with authentication is not supported by puppeteer, continue without proxy'); - } - } else { - // Chromium cannot recognize socks5h and socks4a, so we need to trim their postfixes - options.args.push(`--proxy-server=${currentProxy.uri.replace('socks5h://', 'socks5://').replace('socks4a://', 'socks4://')}`); - } - } - const browser = await (config.puppeteerWSEndpoint - ? insidePuppeteer.connect({ - browserWSEndpoint: config.puppeteerWSEndpoint, - }) - : insidePuppeteer.launch( - config.chromiumExecutablePath - ? { - executablePath: config.chromiumExecutablePath, - ...options, - } - : options - )); - setTimeout(async () => { - await browser.close(); - }, 30000); - - return browser; -}; - -export default outPuppeteer; - -// No-op in Node.js environment (used by Worker build via alias) - -export const setBrowserBinding = (_binding: any) => {}; - -/** - * @returns Puppeteer page - */ -export const getPuppeteerPage = async ( - url: string, - instanceOptions: { - onBeforeLoad?: (page: Page, browser?: Browser) => Promise | void; - gotoConfig?: { - waitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'; - }; - noGoto?: boolean; - } = {} -) => { - const options = { - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - '--disable-blink-features=AutomationControlled', - '--window-position=0,0', - '--ignore-certificate-errors', - '--ignore-certificate-errors-spki-list', - `--user-agent=${config.ua}`, - ], - headless: true, - ignoreHTTPSErrors: true, - }; - - const insidePuppeteer: typeof puppeteer = puppeteer; - - let allowProxy = false; - const proxyRegex = new RegExp(proxy.proxyObj.url_regex); - let urlHandler; - try { - urlHandler = new URL(url); - } catch { - // ignore - } - - if (proxyRegex.test(url) && url.startsWith('http') && !(urlHandler && urlHandler.host === proxy.proxyUrlHandler?.host)) { - allowProxy = true; - } - - let hasProxy = false; - let currentProxyState: any = null; - const currentProxy = proxy.getCurrentProxy(); - if (currentProxy && allowProxy) { - currentProxyState = currentProxy; - if (currentProxy.urlHandler?.username || currentProxy.urlHandler?.password) { - // only proxies with authentication need to be anonymized - if (currentProxy.urlHandler.protocol === 'http:') { - const urlObj = new URL(currentProxy.uri); - urlObj.username = ''; - urlObj.password = ''; - options.args.push(`--proxy-server=${urlObj.toString().replace(/\/$/, '')}`); - hasProxy = true; - } else { - logger.warn('SOCKS/HTTPS proxy with authentication is not supported by puppeteer, continue without proxy'); - } - } else { - // Chromium cannot recognize socks5h and socks4a, so we need to trim their postfixes - options.args.push(`--proxy-server=${currentProxy.uri.replace('socks5h://', 'socks5://').replace('socks4a://', 'socks4://')}`); - hasProxy = true; - } - } - let browser: Browser; - if (config.puppeteerWSEndpoint) { - const endpointURL = new URL(config.puppeteerWSEndpoint); - endpointURL.searchParams.set('launch', JSON.stringify(options)); - endpointURL.searchParams.set('stealth', 'true'); - const endpoint = endpointURL.toString(); - browser = await insidePuppeteer.connect({ - browserWSEndpoint: endpoint, - }); - } else { - browser = await insidePuppeteer.launch( - config.chromiumExecutablePath - ? { - executablePath: config.chromiumExecutablePath, - ...options, - } - : options - ); - } - - setTimeout(async () => { - await browser.close(); - }, 30000); - - const page = await browser.newPage(); - - if (hasProxy && currentProxyState) { - logger.debug(`Proxying request in puppeteer via ${currentProxyState.uri}: ${url}`); - } - - if (hasProxy && currentProxyState && (currentProxyState.urlHandler?.username || currentProxyState.urlHandler?.password)) { - await page.authenticate({ - username: currentProxyState.urlHandler?.username, - password: currentProxyState.urlHandler?.password, - }); - } - - if (instanceOptions.onBeforeLoad) { - await instanceOptions.onBeforeLoad(page, browser); - } - - if (!instanceOptions.noGoto) { - try { - await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); - } catch (error) { - if (hasProxy && currentProxyState && proxy.multiProxy) { - logger.warn(`Puppeteer navigation failed with proxy ${currentProxyState.uri}, marking as failed: ${error}`); - proxy.markProxyFailed(currentProxyState.uri); - throw error; - } - throw error; - } - } - - return { - page, - destroy: async () => { - await browser.close(); - }, - browser, - }; -}; +export type { Browser, Page } from './playwright'; +export { default, getPlaywrightPage, getPlaywrightPage as getPuppeteerPage, setBrowserBinding } from './playwright'; diff --git a/lib/utils/puppeteer.worker.ts b/lib/utils/puppeteer.worker.ts index 74f94425aad7..ca40b70ae6f1 100644 --- a/lib/utils/puppeteer.worker.ts +++ b/lib/utils/puppeteer.worker.ts @@ -1,99 +1,2 @@ -// Worker-compatible puppeteer using @cloudflare/puppeteer -// This module uses Cloudflare Browser Rendering API -import type { Browser, Page } from '@cloudflare/puppeteer'; -import puppeteer from '@cloudflare/puppeteer'; - -import { config } from '@/config'; - -import logger from './logger'; - -// Browser binding from wrangler.toml -// This will be set by the Worker runtime -let browserBinding: any = null; - -// Set the browser binding from the Worker environment -export const setBrowserBinding = (binding: any) => { - browserBinding = binding; -}; - -/** - * Get the browser binding from the execution context - * In Cloudflare Workers, bindings are passed via the env parameter in fetch handler - */ -const getBrowserBinding = () => { - if (!browserBinding) { - throw new Error('Browser Rendering API not available. ' + 'This route requires Cloudflare Browser Rendering which is only available in remote mode. ' + 'Use `wrangler dev --remote` or deploy to Cloudflare Workers.'); - } - return browserBinding; -}; - -/** - * @deprecated use getPuppeteerPage instead - * @returns Puppeteer browser - */ -const outPuppeteer = async () => { - const binding = getBrowserBinding(); - const browser = await puppeteer.launch(binding, { - keep_alive: 60000, // Keep browser alive for 1 minute - }); - - setTimeout(async () => { - await browser.close(); - }, 30000); - - return browser; -}; - -export default outPuppeteer; - -/** - * @returns Puppeteer page - */ -export const getPuppeteerPage = async ( - url: string, - instanceOptions: { - onBeforeLoad?: (page: Page, browser?: Browser) => Promise | void; - gotoConfig?: { - waitUntil?: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'; - }; - noGoto?: boolean; - } = {} -) => { - const binding = getBrowserBinding(); - - logger.debug(`Launching Cloudflare Browser for: ${url}`); - - const browser = await puppeteer.launch(binding, { - keep_alive: 60000, // Keep browser alive for 1 minute for session reuse - }); - - setTimeout(async () => { - await browser.close(); - }, 30000); - - const page = await browser.newPage(); - - // Set user agent - await page.setUserAgent(config.ua); - - if (instanceOptions.onBeforeLoad) { - await instanceOptions.onBeforeLoad(page, browser); - } - - if (!instanceOptions.noGoto) { - try { - await page.goto(url, instanceOptions.gotoConfig || { waitUntil: 'domcontentloaded' }); - } catch (error) { - logger.error(`Puppeteer navigation failed: ${error}`); - throw error; - } - } - - return { - page, - destroy: async () => { - await browser.close(); - }, - browser, - }; -}; +export type { Browser, Page } from './playwright.worker'; +export { default, getPlaywrightPage, getPlaywrightPage as getPuppeteerPage, setBrowserBinding } from './playwright.worker'; diff --git a/lib/utils/request-rewriter/get.ts b/lib/utils/request-rewriter/get.ts index 95b4bc8a2ec4..25cc7a117e31 100644 --- a/lib/utils/request-rewriter/get.ts +++ b/lib/utils/request-rewriter/get.ts @@ -80,7 +80,7 @@ const getWrappedGet: (origin: T) => T = (origin) => url.host !== proxy.proxyUrlHandler?.host && url.host !== 'localhost' && !url.host.startsWith('127.') && - !(config.puppeteerWSEndpoint?.includes(url.host) ?? false) + !(config.playwrightWSEndpoint?.includes(url.host) ?? false) ) { options.agent = proxy.agent; } diff --git a/lib/utils/rss-parser.test.ts b/lib/utils/rss-parser.test.ts index 1c5edad9893a..460ac13bc7ca 100644 --- a/lib/utils/rss-parser.test.ts +++ b/lib/utils/rss-parser.test.ts @@ -7,6 +7,8 @@ import parser from '@/utils/rss-parser'; const rssXml = 'TestItem'; +const toArrayBuffer = (buf: Buffer): ArrayBuffer => buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer; + describe('rss-parser', () => { it('rss', async () => { const result = await parser.parseURL('http://rsshub.test/rss'); @@ -18,7 +20,7 @@ describe('rss-parser', () => { const compressed = zlib.gzipSync(Buffer.from(rssXml)); server.use( http.get('http://rsshub.test/rss-gzip', () => - HttpResponse.arrayBuffer(compressed.buffer as ArrayBuffer, { + HttpResponse.arrayBuffer(toArrayBuffer(compressed), { headers: { 'content-type': 'application/xml', 'content-encoding': 'gzip', @@ -36,7 +38,7 @@ describe('rss-parser', () => { const compressed = zlib.deflateSync(Buffer.from(rssXml)); server.use( http.get('http://rsshub.test/rss-deflate', () => - HttpResponse.arrayBuffer(compressed.buffer as ArrayBuffer, { + HttpResponse.arrayBuffer(toArrayBuffer(compressed), { headers: { 'content-type': 'application/xml', 'content-encoding': 'deflate', @@ -54,7 +56,7 @@ describe('rss-parser', () => { const compressed = zlib.brotliCompressSync(Buffer.from(rssXml)); server.use( http.get('http://rsshub.test/rss-br', () => - HttpResponse.arrayBuffer(compressed.buffer as ArrayBuffer, { + HttpResponse.arrayBuffer(toArrayBuffer(compressed), { headers: { 'content-type': 'application/xml', 'content-encoding': 'br', @@ -73,7 +75,7 @@ describe('rss-parser', () => { const compressed = zlib.zstdCompressSync(Buffer.from(rssXml)); server.use( http.get('http://rsshub.test/rss-zstd', () => - HttpResponse.arrayBuffer(compressed.buffer as ArrayBuffer, { + HttpResponse.arrayBuffer(toArrayBuffer(compressed), { headers: { 'content-type': 'application/xml', 'content-encoding': 'zstd', diff --git a/package.json b/package.json index 43d327ce6c9e..fd23fc2dcd81 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,9 @@ "dev": "cross-env NODE_ENV=dev NODE_OPTIONS='--max-http-header-size=32768' tsx watch --inspect --clear-screen=false lib/index.ts", "dev:cache": "cross-env NODE_ENV=production NODE_OPTIONS='--max-http-header-size=32768' tsx watch --clear-screen=false lib/index.ts", "eslint": "eslint --cache . --concurrency auto", - "format": "oxlint --type-aware --fix \"**/*.{ts,tsx,js,yml}\" && oxfmt .", - "format:check": "oxlint --type-aware \"**/*.{ts,tsx,js,yml}\" && oxfmt . --check", + "format": "oxlint --type-aware --fix . && oxfmt .", + "format:check": "oxlint --type-aware . && oxfmt . --check", + "format:description": "tsx scripts/workflow/format-description.ts", "format:staged": "lint-staged", "lint": "oxlint --type-aware .", "prepare": "husky || true", @@ -59,51 +60,50 @@ "@bbob/html": "4.3.1", "@bbob/plugin-helper": "4.3.1", "@bbob/preset-html5": "4.3.1", - "@honeybadger-io/js": "6.12.3", - "@hono/node-server": "1.19.13", - "@hono/zod-openapi": "1.2.4", - "@jocmp/mercury-parser": "3.0.7", - "@notionhq/client": "5.17.0", + "@honeybadger-io/js": "6.14.0", + "@hono/node-server": "2.0.1", + "@hono/zod-openapi": "1.3.0", + "@jocmp/mercury-parser": "3.0.8", + "@notionhq/client": "5.20.0", "@opentelemetry/api": "1.9.1", - "@opentelemetry/exporter-prometheus": "0.214.0", - "@opentelemetry/exporter-trace-otlp-http": "0.214.0", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/sdk-metrics": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1", + "@opentelemetry/exporter-prometheus": "0.217.0", + "@opentelemetry/exporter-trace-otlp-http": "0.217.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1", "@opentelemetry/semantic-conventions": "1.40.0", "@rss3/sdk": "0.0.25", - "@scalar/hono-api-reference": "0.10.6", - "@sentry/node": "10.47.0", + "@scalar/hono-api-reference": "0.10.14", + "@sentry/node": "10.52.0", "aes-js": "3.1.2", "cheerio": "1.2.0", - "city-timezones": "1.3.3", + "city-timezones": "1.3.4", "cross-env": "10.1.0", "crypto-js": "4.2.0", "currency-symbol-map": "5.1.0", "dayjs": "1.11.20", "destr": "2.0.5", - "dotenv": "17.4.1", + "dotenv": "17.4.2", "entities": "8.0.0", "etag": "1.8.1", "fanfou-sdk": "6.0.0", - "form-data": "4.0.5", "google-play-scraper": "10.1.2", "googleapis": "171.4.0", "header-generator": "2.1.82", - "hono": "4.12.12", - "html-to-text": "9.0.5", - "http-cookie-agent": "7.0.3", + "hono": "4.12.18", + "html-to-text": "10.0.0", + "http-cookie-agent": "8.0.0", "https-proxy-agent": "9.0.0", "iconv-lite": "0.7.2", - "imapflow": "1.3.1", + "imapflow": "1.3.3", "instagram-private-api": "1.46.1", "ioredis": "5.10.1", "ip-regex": "5.0.0", - "jsdom": "29.0.2", + "jsdom": "29.1.1", "json-bigint": "1.0.0", "jsonpath-plus": "10.4.0", - "jsrsasign": "11.1.1", - "lru-cache": "11.3.3", + "jsrsasign": "11.1.3", + "lru-cache": "11.3.6", "lz-string": "1.5.0", "mailparser": "3.9.8", "markdown-it": "14.1.1", @@ -115,44 +115,42 @@ "otplib": "13.4.0", "p-map": "7.0.4", "pac-proxy-agent": "9.0.1", - "proxy-chain": "2.7.1", - "puppeteer-real-browser": "1.4.4", + "playwright": "1.59.1", "query-string": "9.3.1", - "rate-limiter-flexible": "11.0.0", - "re2js": "2.0.1", - "rebrowser-puppeteer": "24.8.1", + "rate-limiter-flexible": "11.1.0", + "re2js": "2.3.2", "rfc4648": "1.5.4", "rss-parser": "3.13.0", - "sanitize-html": "2.17.2", + "sanitize-html": "2.17.3", "simplecc-wasm": "1.1.1", "socks-proxy-agent": "10.0.0", "source-map": "0.7.6", "telegram": "2.26.22", "title": "4.0.1", - "tldts": "7.0.28", + "tldts": "7.0.30", "tosource": "2.0.0-alpha.3", "tough-cookie": "6.0.1", "tsx": "4.21.0", "twitter-api-v2": "1.29.0", - "ufo": "1.6.3", - "undici": "7.24.7", - "uuid": "13.0.0", + "ufo": "1.6.4", + "undici": "7.25.0", + "uuid": "14.0.0", "winston": "3.19.0", "xxhash-wasm": "1.1.0", "youtube-caption-extractor": "1.9.1", "youtubei.js": "17.0.1", - "zod": "4.3.6" + "zod": "4.4.3" }, "devDependencies": { - "@actions/core": "3.0.0", - "@actions/github": "9.1.0", + "@actions/core": "3.0.1", + "@actions/github": "9.1.1", "@bbob/types": "4.3.1", - "@cloudflare/containers": "0.2.4", - "@cloudflare/puppeteer": "1.0.6", - "@cloudflare/workers-types": "4.20260409.1", + "@cloudflare/containers": "0.3.3", + "@cloudflare/playwright": "1.3.0", + "@cloudflare/workers-types": "4.20260508.1", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", - "@oxlint/plugins": "1.59.0", + "@oxlint/plugins": "1.62.0", "@stylistic/eslint-plugin": "5.10.0", "@types/aes-js": "3.1.4", "@types/babel__preset-env": "7.10.0", @@ -168,58 +166,67 @@ "@types/mailparser": "3.4.6", "@types/markdown-it": "14.1.2", "@types/module-alias": "2.0.4", - "@types/node": "25.5.2", + "@types/node": "25.6.2", "@types/sanitize-html": "2.16.1", - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", "@vercel/nft": "1.5.0", - "@vitest/coverage-v8": "4.0.9", - "discord-api-types": "0.38.44", + "@vitest/coverage-v8": "4.1.5", + "discord-api-types": "0.38.47", "domhandler": "6.0.1", - "eslint": "10.2.0", + "eslint": "10.3.0", "eslint-nibble": "9.1.1", "eslint-plugin-import-x": "4.16.2", - "eslint-plugin-n": "17.24.0", + "eslint-plugin-n": "18.0.1", "eslint-plugin-simple-import-sort": "13.0.0", "eslint-plugin-unicorn": "64.0.0", - "eslint-plugin-yml": "3.3.1", - "fs-extra": "11.3.4", - "globals": "17.4.0", - "got": "14.6.6", + "eslint-plugin-yml": "3.3.2", + "fast-string-width": "3.0.2", + "fs-extra": "11.3.5", + "globals": "17.6.0", + "got": "15.0.5", "husky": "9.1.7", "js-beautify": "1.15.4", - "lint-staged": "16.4.0", + "lint-staged": "17.0.2", "magic-string": "0.30.21", "mockdate": "3.0.5", - "msw": "2.13.2", - "node-network-devtools": "1.0.29", - "oxfmt": "0.44.0", - "oxlint": "1.59.0", - "oxlint-plugin-eslint": "1.59.0", - "oxlint-tsgolint": "0.20.0", + "msw": "2.13.4", + "node-network-devtools": "1.0.30", + "oxfmt": "0.47.0", + "oxlint": "1.62.0", + "oxlint-plugin-eslint": "1.62.0", + "oxlint-tsgolint": "0.22.1", + "remark": "15.0.1", + "remark-gfm": "4.0.1", + "remark-pangu": "2.2.0", "remark-parse": "11.0.0", - "tsdown": "0.21.7", + "tsdown": "0.22.0", "typescript": "5.9.3", "unified": "11.0.5", "vite-tsconfig-paths": "6.1.1", - "vitest": "4.0.9", - "wrangler": "4.81.0", + "vitest": "4.1.5", + "wrangler": "4.90.0", "yaml-eslint-parser": "2.0.0" }, "lint-staged": { - "!(*.ts|*.tsx|*.js|*.yml)": [ + "!(*.ts|*.tsx|*.js|*.cjs|*.mjs|*.yml)": [ "oxfmt --no-error-on-unmatched-pattern" ], - "*.{ts,tsx,js,yml}": [ + "*.{ts,tsx,js,cjs,mjs}": [ + "tsx scripts/workflow/format-description.ts", "oxlint --type-aware --fix", "eslint --cache --fix --concurrency auto", "oxfmt --no-error-on-unmatched-pattern" + ], + "*.yml": [ + "eslint --cache --fix --concurrency auto", + "oxfmt --no-error-on-unmatched-pattern" ] }, "engines": { "node": "^22.20.0 || ^24" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", "pnpm": { "onlyBuiltDependencies": [ "bufferutil", @@ -228,9 +235,8 @@ "esbuild", "eslint-nibble", "msw", + "playwright", "protobufjs", - "puppeteer", - "rebrowser-puppeteer", "rolldown", "sharp", "sleep", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b708dbdc64df..1f98910ee6e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,38 +41,38 @@ importers: specifier: 4.3.1 version: 4.3.1 '@honeybadger-io/js': - specifier: 6.12.3 - version: 6.12.3 + specifier: 6.14.0 + version: 6.14.0 '@hono/node-server': - specifier: 1.19.13 - version: 1.19.13(hono@4.12.12) + specifier: 2.0.1 + version: 2.0.1(hono@4.12.18) '@hono/zod-openapi': - specifier: 1.2.4 - version: 1.2.4(hono@4.12.12)(zod@4.3.6) + specifier: 1.3.0 + version: 1.3.0(hono@4.12.18)(zod@4.4.3) '@jocmp/mercury-parser': - specifier: 3.0.7 - version: 3.0.7 + specifier: 3.0.8 + version: 3.0.8 '@notionhq/client': - specifier: 5.17.0 - version: 5.17.0 + specifier: 5.20.0 + version: 5.20.0 '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 '@opentelemetry/exporter-prometheus': - specifier: 0.214.0 - version: 0.214.0(@opentelemetry/api@1.9.1) + specifier: 0.217.0 + version: 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.214.0 - version: 0.214.0(@opentelemetry/api@1.9.1) + specifier: 0.217.0 + version: 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': - specifier: 2.6.1 - version: 2.6.1(@opentelemetry/api@1.9.1) + specifier: 2.7.1 + version: 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': - specifier: 2.6.1 - version: 2.6.1(@opentelemetry/api@1.9.1) + specifier: 2.7.1 + version: 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': - specifier: 2.6.1 - version: 2.6.1(@opentelemetry/api@1.9.1) + specifier: 2.7.1 + version: 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': specifier: 1.40.0 version: 1.40.0 @@ -80,11 +80,11 @@ importers: specifier: 0.0.25 version: 0.0.25 '@scalar/hono-api-reference': - specifier: 0.10.6 - version: 0.10.6(hono@4.12.12) + specifier: 0.10.14 + version: 0.10.14(hono@4.12.18) '@sentry/node': - specifier: 10.47.0 - version: 10.47.0(@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1)) + specifier: 10.52.0 + version: 10.52.0(@opentelemetry/exporter-trace-otlp-http@0.217.0(@opentelemetry/api@1.9.1)) aes-js: specifier: 3.1.2 version: 3.1.2 @@ -92,8 +92,8 @@ importers: specifier: 1.2.0 version: 1.2.0 city-timezones: - specifier: 1.3.3 - version: 1.3.3 + specifier: 1.3.4 + version: 1.3.4 cross-env: specifier: 10.1.0 version: 10.1.0 @@ -110,8 +110,8 @@ importers: specifier: 2.0.5 version: 2.0.5 dotenv: - specifier: 17.4.1 - version: 17.4.1 + specifier: 17.4.2 + version: 17.4.2 entities: specifier: 8.0.0 version: 8.0.0 @@ -121,9 +121,6 @@ importers: fanfou-sdk: specifier: 6.0.0 version: 6.0.0 - form-data: - specifier: 4.0.5 - version: 4.0.5 google-play-scraper: specifier: 10.1.2 version: 10.1.2 @@ -134,14 +131,14 @@ importers: specifier: 2.1.82 version: 2.1.82 hono: - specifier: 4.12.12 - version: 4.12.12 + specifier: 4.12.18 + version: 4.12.18 html-to-text: - specifier: 9.0.5 - version: 9.0.5 + specifier: 10.0.0 + version: 10.0.0 http-cookie-agent: - specifier: 7.0.3 - version: 7.0.3(tough-cookie@6.0.1)(undici@7.24.7) + specifier: 8.0.0 + version: 8.0.0(tough-cookie@6.0.1)(undici@7.25.0) https-proxy-agent: specifier: 9.0.0 version: 9.0.0 @@ -149,8 +146,8 @@ importers: specifier: 0.7.2 version: 0.7.2 imapflow: - specifier: 1.3.1 - version: 1.3.1 + specifier: 1.3.3 + version: 1.3.3 instagram-private-api: specifier: 1.46.1 version: 1.46.1 @@ -161,8 +158,8 @@ importers: specifier: 5.0.0 version: 5.0.0 jsdom: - specifier: 29.0.2 - version: 29.0.2(@noble/hashes@2.0.1) + specifier: 29.1.1 + version: 29.1.1(@noble/hashes@2.0.1) json-bigint: specifier: 1.0.0 version: 1.0.0 @@ -170,11 +167,11 @@ importers: specifier: 10.4.0 version: 10.4.0 jsrsasign: - specifier: 11.1.1 - version: 11.1.1 + specifier: 11.1.3 + version: 11.1.3 lru-cache: - specifier: 11.3.3 - version: 11.3.3 + specifier: 11.3.6 + version: 11.3.6 lz-string: specifier: 1.5.0 version: 1.5.0 @@ -208,24 +205,18 @@ importers: pac-proxy-agent: specifier: 9.0.1 version: 9.0.1 - proxy-chain: - specifier: 2.7.1 - version: 2.7.1 - puppeteer-real-browser: - specifier: 1.4.4 - version: 1.4.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + playwright: + specifier: 1.59.1 + version: 1.59.1 query-string: specifier: 9.3.1 version: 9.3.1 rate-limiter-flexible: - specifier: 11.0.0 - version: 11.0.0 + specifier: 11.1.0 + version: 11.1.0 re2js: - specifier: 2.0.1 - version: 2.0.1 - rebrowser-puppeteer: - specifier: 24.8.1 - version: 24.8.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + specifier: 2.3.2 + version: 2.3.2 rfc4648: specifier: 1.5.4 version: 1.5.4 @@ -233,8 +224,8 @@ importers: specifier: 3.13.0 version: 3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d) sanitize-html: - specifier: 2.17.2 - version: 2.17.2 + specifier: 2.17.3 + version: 2.17.3 simplecc-wasm: specifier: 1.1.1 version: 1.1.1 @@ -251,8 +242,8 @@ importers: specifier: 4.0.1 version: 4.0.1 tldts: - specifier: 7.0.28 - version: 7.0.28 + specifier: 7.0.30 + version: 7.0.30 tosource: specifier: 2.0.0-alpha.3 version: 2.0.0-alpha.3 @@ -266,14 +257,14 @@ importers: specifier: 1.29.0 version: 1.29.0 ufo: - specifier: 1.6.3 - version: 1.6.3 + specifier: 1.6.4 + version: 1.6.4 undici: - specifier: 7.24.7 - version: 7.24.7 + specifier: 7.25.0 + version: 7.25.0 uuid: - specifier: 13.0.0 - version: 13.0.0 + specifier: 14.0.0 + version: 14.0.0 winston: specifier: 3.19.0 version: 3.19.0 @@ -287,39 +278,39 @@ importers: specifier: 17.0.1 version: 17.0.1 zod: - specifier: 4.3.6 - version: 4.3.6 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@actions/core': - specifier: 3.0.0 - version: 3.0.0 + specifier: 3.0.1 + version: 3.0.1 '@actions/github': - specifier: 9.1.0 - version: 9.1.0 + specifier: 9.1.1 + version: 9.1.1 '@bbob/types': specifier: 4.3.1 version: 4.3.1 '@cloudflare/containers': - specifier: 0.2.4 - version: 0.2.4 - '@cloudflare/puppeteer': - specifier: 1.0.6 - version: 1.0.6(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 0.3.3 + version: 0.3.3 + '@cloudflare/playwright': + specifier: 1.3.0 + version: 1.3.0 '@cloudflare/workers-types': - specifier: 4.20260409.1 - version: 4.20260409.1 + specifier: 4.20260508.1 + version: 4.20260508.1 '@eslint/eslintrc': specifier: 3.3.5 version: 3.3.5 '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@10.2.0(jiti@2.6.1)) + version: 10.0.1(eslint@10.3.0(jiti@2.6.1)) '@oxlint/plugins': - specifier: 1.59.0 - version: 1.59.0 + specifier: 1.62.0 + version: 1.62.0 '@stylistic/eslint-plugin': specifier: 5.10.0 - version: 5.10.0(eslint@10.2.0(jiti@2.6.1)) + version: 5.10.0(eslint@10.3.0(jiti@2.6.1)) '@types/aes-js': specifier: 3.1.4 version: 3.1.4 @@ -363,59 +354,62 @@ importers: specifier: 2.0.4 version: 2.0.4 '@types/node': - specifier: 25.5.2 - version: 25.5.2 + specifier: 25.6.2 + version: 25.6.2 '@types/sanitize-html': specifier: 2.16.1 version: 2.16.1 '@typescript-eslint/eslint-plugin': - specifier: 8.58.1 - version: 8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.2 + version: 8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': - specifier: 8.58.1 - version: 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.59.2 + version: 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) '@vercel/nft': specifier: 1.5.0 - version: 1.5.0(rollup@4.60.1) + version: 1.5.0(rollup@4.60.3) '@vitest/coverage-v8': - specifier: 4.0.9 - version: 4.0.9(vitest@4.0.9(@edge-runtime/vm@3.2.0)(@types/debug@4.1.13)(@types/node@25.5.2)(jiti@2.6.1)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.5 + version: 4.1.5(vitest@4.1.5) discord-api-types: - specifier: 0.38.44 - version: 0.38.44 + specifier: 0.38.47 + version: 0.38.47 domhandler: specifier: 6.0.1 version: 6.0.1 eslint: - specifier: 10.2.0 - version: 10.2.0(jiti@2.6.1) + specifier: 10.3.0 + version: 10.3.0(jiti@2.6.1) eslint-nibble: specifier: 9.1.1 - version: 9.1.1(@types/node@25.5.2)(eslint@10.2.0(jiti@2.6.1)) + version: 9.1.1(@types/node@25.6.2)(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-import-x: specifier: 4.16.2 - version: 4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0(jiti@2.6.1)) + version: 4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-n: - specifier: 17.24.0 - version: 17.24.0(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 18.0.1 + version: 18.0.1(eslint@10.3.0(jiti@2.6.1))(ts-declaration-location@1.0.7(typescript@5.9.3))(typescript@5.9.3) eslint-plugin-simple-import-sort: specifier: 13.0.0 - version: 13.0.0(eslint@10.2.0(jiti@2.6.1)) + version: 13.0.0(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-unicorn: specifier: 64.0.0 - version: 64.0.0(eslint@10.2.0(jiti@2.6.1)) + version: 64.0.0(eslint@10.3.0(jiti@2.6.1)) eslint-plugin-yml: - specifier: 3.3.1 - version: 3.3.1(eslint@10.2.0(jiti@2.6.1)) + specifier: 3.3.2 + version: 3.3.2(eslint@10.3.0(jiti@2.6.1)) + fast-string-width: + specifier: 3.0.2 + version: 3.0.2 fs-extra: - specifier: 11.3.4 - version: 11.3.4 + specifier: 11.3.5 + version: 11.3.5 globals: - specifier: 17.4.0 - version: 17.4.0 + specifier: 17.6.0 + version: 17.6.0 got: - specifier: 14.6.6 - version: 14.6.6 + specifier: 15.0.5 + version: 15.0.5 husky: specifier: 9.1.7 version: 9.1.7 @@ -423,8 +417,8 @@ importers: specifier: 1.15.4 version: 1.15.4 lint-staged: - specifier: 16.4.0 - version: 16.4.0 + specifier: 17.0.2 + version: 17.0.2 magic-string: specifier: 0.30.21 version: 0.30.21 @@ -432,29 +426,38 @@ importers: specifier: 3.0.5 version: 3.0.5 msw: - specifier: 2.13.2 - version: 2.13.2(@types/node@25.5.2)(typescript@5.9.3) + specifier: 2.13.4 + version: 2.13.4(@types/node@25.6.2)(typescript@5.9.3) node-network-devtools: - specifier: 1.0.29 - version: 1.0.29(undici@7.24.7)(utf-8-validate@5.0.10) + specifier: 1.0.30 + version: 1.0.30(undici@7.25.0)(utf-8-validate@5.0.10) oxfmt: - specifier: 0.44.0 - version: 0.44.0 + specifier: 0.47.0 + version: 0.47.0 oxlint: - specifier: 1.59.0 - version: 1.59.0(oxlint-tsgolint@0.20.0) + specifier: 1.62.0 + version: 1.62.0(oxlint-tsgolint@0.22.1) oxlint-plugin-eslint: - specifier: 1.59.0 - version: 1.59.0 + specifier: 1.62.0 + version: 1.62.0 oxlint-tsgolint: - specifier: 0.20.0 - version: 0.20.0 + specifier: 0.22.1 + version: 0.22.1 + remark: + specifier: 15.0.1 + version: 15.0.1 + remark-gfm: + specifier: 4.0.1 + version: 4.0.1 + remark-pangu: + specifier: 2.2.0 + version: 2.2.0 remark-parse: specifier: 11.0.0 version: 11.0.0 tsdown: - specifier: 0.21.7 - version: 0.21.7(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)(synckit@0.11.12)(typescript@5.9.3) + specifier: 0.22.0 + version: 0.22.0(tsx@4.21.0)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)) typescript: specifier: 5.9.3 version: 5.9.3 @@ -463,43 +466,47 @@ importers: version: 11.0.5 vite-tsconfig-paths: specifier: 6.1.1 - version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)) vitest: - specifier: 4.0.9 - version: 4.0.9(@edge-runtime/vm@3.2.0)(@types/debug@4.1.13)(@types/node@25.5.2)(jiti@2.6.1)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.3) + specifier: 4.1.5 + version: 4.1.5(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)) wrangler: - specifier: 4.81.0 - version: 4.81.0(@cloudflare/workers-types@4.20260409.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + specifier: 4.90.0 + version: 4.90.0(@cloudflare/workers-types@4.20260508.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10) yaml-eslint-parser: specifier: 2.0.0 version: 2.0.0 packages: - '@actions/core@3.0.0': - resolution: {integrity: sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==} + '@actions/core@3.0.1': + resolution: {integrity: sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA==} '@actions/exec@3.0.0': resolution: {integrity: sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==} - '@actions/github@9.1.0': - resolution: {integrity: sha512-u0hDGQeCS+7VNoLA8hYG65RLdPLMaPGfka0sZ0up7P0AiShqfX6xcuXNteGkQ7X7Tod7AMNwHd4p7DS63i8zzA==} + '@actions/github@9.1.1': + resolution: {integrity: sha512-tL5JbYOBZHc0ngEnCsaDcryUizIUIlQyIMwy1Wkx93H5HzbBJ7TbiPx2PnFjBwZW0Vh05JmfFZhecE6gglYegA==} '@actions/http-client@3.0.2': resolution: {integrity: sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==} - '@actions/http-client@4.0.0': - resolution: {integrity: sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==} + '@actions/http-client@4.0.1': + resolution: {integrity: sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==} '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@asamuzakjp/css-color@5.1.6': - resolution: {integrity: sha512-BXWCh8dHs9GOfpo/fWGDJtDmleta2VePN9rn6WQt3GjEbxzutVF4t0x2pmH+7dbMCLtuv3MlwqRsAuxlzFXqFg==} + '@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.0.7': - resolution: {integrity: sha512-d2BgqDUOS1Hfp4IzKUZqCNz+Kg3Y88AkaBvJK/ZVSQPU1f7OpPNi7nQTH6/oI47Dkdg+Z3e8Yp6ynOu4UMINAQ==} + '@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/nwsapi@2.3.9': @@ -514,24 +521,24 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/generator@8.0.0-rc.3': - resolution: {integrity: sha512-em37/13/nR320G4jab/nIIHZgc2Wz2y/D39lxnTyxB4/D/omPQncl/lSdlnJY1OhQcRGugTSIF2l/69o31C9dA==} + '@babel/generator@8.0.0-rc.4': + resolution: {integrity: sha512-YZ+FuIgkj7KrIb2a2X1XiY0QYgDxAbVbYP64SjwJzOK3euCsUerzenh2oqdsmKuPSlhzmFOOklnxzHAzXagvpw==} engines: {node: ^20.19.0 || >=22.12.0} '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@8.0.0-rc.3': - resolution: {integrity: sha512-AmwWFx1m8G/a5cXkxLxTiWl+YEoWuoFLUCwqMlNuWO1tqAYITQAbCRPUkyBHv1VOFgfjVOqEj6L3u15J5ZCzTA==} + '@babel/helper-string-parser@8.0.0-rc.4': + resolution: {integrity: sha512-dluR3v287dp6YPF57kyKKrHPKffUeuxH1zQcF1WD30TeFzWXhDiVi1U6PkqaDB0++H1PeCwRhmYl4DvoerlPIw==} engines: {node: ^20.19.0 || >=22.12.0} '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@8.0.0-rc.3': - resolution: {integrity: sha512-8AWCJ2VJJyDFlGBep5GpaaQ9AAaE/FjAcrqI7jyssYhtL7WGV0DOKpJsQqM037xDbpRLHXsY8TwU7zDma7coOw==} + '@babel/helper-validator-identifier@8.0.0-rc.4': + resolution: {integrity: sha512-HTD3bskipk5MSm08twTW6832jzIXUhxMddy4NPPzIMuyMEsrs0ZgwAaMj5ubB5+6hMlUjDu17vNconEmwsmpYg==} engines: {node: ^20.19.0 || >=22.12.0} '@babel/parser@7.29.2': @@ -539,8 +546,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@8.0.0-rc.3': - resolution: {integrity: sha512-B20dvP3MfNc/XS5KKCHy/oyWl5IA6Cn9YjXRdDlCjNmUFrjvLXMNUfQq/QUy9fnG2gYkKKcrto2YaF9B32ToOQ==} + '@babel/parser@8.0.0-rc.4': + resolution: {integrity: sha512-0S/1yefMa15N4i2v3t8Fw9pgMHhf2gF6Lc1UEXI96Ls6FNAjqvHHZouZ2ZS/deqLhbMFtmfVeFac6iTsvFbLwA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -552,8 +559,8 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@babel/types@8.0.0-rc.3': - resolution: {integrity: sha512-mOm5ZrYmphGfqVWoH5YYMTITb3cDXsFgmvFlvkvWDMsR9X8RFnt7a0Wb6yNIdoFsiMO9WjYLq+U/FMtqIYAF8Q==} + '@babel/types@8.0.0-rc.4': + resolution: {integrity: sha512-bw30DV880P/VYtsjWWdoWmJpb9S2Vn1/PqayyccTELzRQ/HslIO7+BD9rNoZ4AAFOAjC1vrNeBCkAsyh6Ibfww==} engines: {node: ^20.19.0 || >=22.12.0} '@bbob/core@4.3.1': @@ -588,58 +595,57 @@ packages: '@bufbuild/protobuf@2.11.0': resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==} - '@cloudflare/containers@0.2.4': - resolution: {integrity: sha512-G/Wsl0EuLbSgXyr9zv7cetUO90NyinKIMZvXae/C5R5BY3XMl9ZUdplvcofylAwRZjBEUm+aUugWxIiiKQU0ZA==} + '@cloudflare/containers@0.3.3': + resolution: {integrity: sha512-ZSXmArCoo5bVTp8pGAJdl5WKmwtZDcffJqr4JcZEbSmMIFjU+AlBqgysuxXMgu03Rp239cOdqerbjK7H0K2krQ==} - '@cloudflare/kv-asset-handler@0.4.2': - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} - engines: {node: '>=18.0.0'} + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} - '@cloudflare/puppeteer@1.0.6': - resolution: {integrity: sha512-e/0s9Ac2IhyBrgnyTDrYEippqrXqKtllP8qezyunOb6icOJ2FzbfQwhWRIVwV3AgZtutQTF5Kb/gFAGXCuGRqQ==} - engines: {node: '>=18'} + '@cloudflare/playwright@1.3.0': + resolution: {integrity: sha512-WOlpXzfJ7noXmW8qE/qlU7MbVHyt9IzxxwPcTGeh2+YQS1gqNIe3fl99AZvBsheH0o7oCePYtL9wRV1KPCYghQ==} - '@cloudflare/unenv-preset@2.16.0': - resolution: {integrity: sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==} + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} peerDependencies: unenv: 2.0.0-rc.24 - workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 + workerd: '>1.20260305.0 <2.0.0-0' peerDependenciesMeta: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260405.1': - resolution: {integrity: sha512-EbmdBcmeIGogKG4V1odSWQe7z4rHssUD4iaXv0cXA22/MFrzH3iQT0R+FJFyhucGtih/9B9E+6j0QbSQD8xT3w==} + '@cloudflare/workerd-darwin-64@1.20260507.1': + resolution: {integrity: sha512-S85aMwcaPJUjKWDiG6iMMnioKWtPLACa6m0j/EhHR1GYfVpnxb974cBc6d25L+sf7jHWHJI2u5hGp0UTJ7MtXQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260405.1': - resolution: {integrity: sha512-r44r418bOQtoP+Odu+L/BQM9q5cRSXRd1N167PgZQIo4MlqzTwHO4L0wwXhxbcV/PF46rrQre/uTFS8R0R+xSQ==} + '@cloudflare/workerd-darwin-arm64@1.20260507.1': + resolution: {integrity: sha512-GMEBu8Zp9Q97HLnf7bWJN4KjWpN5MxpeqdvHjBGWNl8UYprJI0k+Jkp89+Wh5S8vIon+HoVbDfOzPa7VwgL6Eg==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260405.1': - resolution: {integrity: sha512-Aaq3RWnaTCzMBo77wC8fjOx+SFdO/rlcXa6HAf+PJs51LyMISFOBCJKqSlS6Irphen0WHHxFKPHUO9bjfj8g2g==} + '@cloudflare/workerd-linux-64@1.20260507.1': + resolution: {integrity: sha512-QlrKEBdgA3uVc0Ok0Q3+0/CW0CTjgj5ySir1i1YY5FXVv0X6GpwtnB5umjunjF2MFprss+L+iFGZzxcSvMC1nA==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260405.1': - resolution: {integrity: sha512-Lbp9Z2wiMzy3Sji3YwMHK5WDlejsH3jF4swAFEv7+jIf3NowZHga3GzwTypNRmcwnfz/XrqQ7Hc0Ul9OoU/lCw==} + '@cloudflare/workerd-linux-arm64@1.20260507.1': + resolution: {integrity: sha512-eGbbupEtK2nh9V9Dhcx3vv3GTKeXqSVNgAEYVCCN0NGS9tl9HbMoHRX/4JL181FKXROMigWBCQVL//qPhsAzBQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260405.1': - resolution: {integrity: sha512-FhE0kt93kj5JnSPVqi4BAXpQQENyKnuSOoJLd35mkMMGhtPrwv5EsReJdck0S8hUocCBlb+U0RmP8ta6k41HjQ==} + '@cloudflare/workerd-windows-64@1.20260507.1': + resolution: {integrity: sha512-dmClJ/E0BAcuDetQIZFqbeAXejWrG5pysGRMQ6T83Y0IW/7IAamY2zFEkAJ10I5xwZsdHuYsZtzlOxpEXpJs7A==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260409.1': - resolution: {integrity: sha512-0rGuppPeip6dqlI6013wC8tE+kbRK+tcaDfqCxKf9sEHDNfSWWUuKgIEDpt6IHHP2O0iYBQpngk5Siv4CL/HGQ==} + '@cloudflare/workers-types@4.20260508.1': + resolution: {integrity: sha512-0KNR+UkrYJYmtyQ5tOjUT/wt/U34FuE4Y8FLSbPMwFrGQQpmvR9wKghwRkL4d28y5t9jI9cvGqeAoo/cerTnCQ==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -656,15 +662,15 @@ packages: resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -676,8 +682,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.2': - resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -699,17 +705,14 @@ packages: resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==} engines: {node: '>=16'} - '@emnapi/core@1.9.1': - resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/runtime@1.9.1': - resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.9.2': - resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} - - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -1192,20 +1195,16 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.23.4': - resolution: {integrity: sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/config-helpers@0.5.4': - resolution: {integrity: sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==} + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@1.1.1': - resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} + '@eslint/config-helpers@0.5.5': + resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@1.2.0': - resolution: {integrity: sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==} + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/eslintrc@3.3.5': @@ -1221,16 +1220,12 @@ packages: eslint: optional: true - '@eslint/object-schema@3.0.4': - resolution: {integrity: sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/plugin-kit@0.6.1': - resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.7.0': - resolution: {integrity: sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==} + '@eslint/plugin-kit@0.7.1': + resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@exodus/bytes@1.15.0': @@ -1247,23 +1242,23 @@ packages: peerDependencies: '@opentelemetry/api': ^1.9.0 - '@honeybadger-io/core@6.7.2': - resolution: {integrity: sha512-4+hyrFI0S/Eni2cBgO40Lqyft5hyNXjgnuh1EbeH457kty8g4YatJYuHBffmrjwLiZHgFbWXRGv7SlG+NehC5Q==} + '@honeybadger-io/core@6.9.0': + resolution: {integrity: sha512-nHkTigMgqABQ6XuhZ4/2cVIArwImctdWfmu/ckWLoF5E4JlATe9WUL3hMRL8UZPmu/ThmbojxkLwDgHts1LyUA==} engines: {node: '>=14'} - '@honeybadger-io/js@6.12.3': - resolution: {integrity: sha512-CL+9A8tGpjawsJEKE74P94hmimHRsq+B90i5N/pXNgnJbgZG40bs9yZpfn2z3w0+3z9wtH12D2RqBjxsGm9DBg==} + '@honeybadger-io/js@6.14.0': + resolution: {integrity: sha512-ZQGop/Ik7vyRJzW/wzE1CL4611fz9+5arrMdS0ivrzDmm/4BQeK4OKZlwfMZqEZtODwfnl32ngh0ayW/jahoSg==} engines: {node: '>=14'} hasBin: true - '@hono/node-server@1.19.13': - resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} - engines: {node: '>=18.14.1'} + '@hono/node-server@2.0.1': + resolution: {integrity: sha512-jI9yMDyFpqBeSighf/zlXnQG/nl9AyBc6aAgy4XtxJMyt/CNyJpvPfzDD+bCc2zAOmhhqtF6TnmIaY+xV4mIrw==} + engines: {node: '>=20'} peerDependencies: hono: ^4 - '@hono/zod-openapi@1.2.4': - resolution: {integrity: sha512-cZu71bpODTbtIDoUsIIYPrs58wJ565Tbg6FE+JshU0irBAd6KxrP+k62Amm/mjA7tTOQ3+ingODHKGFOnv+Ibw==} + '@hono/zod-openapi@1.3.0': + resolution: {integrity: sha512-loDVevfMaaNa0slskhpMcqjSdidVXba2QJwNVmnS5Dp6L8AqSgtjJxWGJfRZtosyzYOb5gx4ZzXNCe+QhwY7xw==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.3.6' @@ -1275,12 +1270,16 @@ packages: hono: '>=3.9.0' zod: ^3.25.0 || ^4.0.0 - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -1448,6 +1447,10 @@ packages: resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/checkbox@4.3.2': resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} engines: {node: '>=18'} @@ -1466,6 +1469,15 @@ packages: '@types/node': optional: true + '@inquirer/confirm@6.0.11': + resolution: {integrity: sha512-pTpHjg0iEIRMYV/7oCZUMf27/383E6Wyhfc/MY+AVQGEoUobffIYWOK9YLP2XFRGz/9i6WlTQh1CkFVIo2Y7XA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/core@10.3.2': resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} @@ -1475,10 +1487,23 @@ packages: '@types/node': optional: true + '@inquirer/core@11.1.8': + resolution: {integrity: sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/figures@1.0.15': resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + '@inquirer/select@4.4.2': resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} engines: {node: '>=18'} @@ -1497,6 +1522,15 @@ packages: '@types/node': optional: true + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@ioredis/commands@1.5.1': resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} @@ -1508,8 +1542,8 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@jocmp/mercury-parser@3.0.7': - resolution: {integrity: sha512-DnZqzYrLFaOc6SPiCKQ1GuaqN1ZPi72JbheKmE4XDwRBr4eFF7FzMlv3eujO1+huBtnIyKYi4zOCMSYmMFAc1A==} + '@jocmp/mercury-parser@3.0.8': + resolution: {integrity: sha512-q3o1OFgrIY7gs/YtMdtzKjpQqk3838zh/+giNeE3gQCWAdUyYH+bhJ2yu36uoa9Xe1Jht6RS5enbs1dujApuvQ==} engines: {node: '>=22'} hasBin: true bundledDependencies: @@ -1564,8 +1598,8 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@napi-rs/wasm-runtime@1.1.2': - resolution: {integrity: sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==} + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1598,8 +1632,8 @@ packages: resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==} engines: {node: '>=12.4.0'} - '@notionhq/client@5.17.0': - resolution: {integrity: sha512-hSNm3VUW5+Qs9vPOmegS6r1RT8O1jtTE/22wmSmiJc9kkb2YyddWr8SBQruvp06rALn2r2RAh9mLPirUUonsvw==} + '@notionhq/client@5.20.0': + resolution: {integrity: sha512-MS0DFSfHPLZ0wi+e9mOP16gCCYbWAkaiMuu/rVK9KxDlKac4oAgHReOfTglcd77g/nlg78snp+KB7fNWP75bEw==} engines: {node: '>=18'} '@octokit/auth-token@6.0.0': @@ -1650,6 +1684,9 @@ packages: '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + '@open-draft/logger@0.3.0': resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} @@ -1668,30 +1705,34 @@ packages: resolution: {integrity: sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==} engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.217.0': + resolution: {integrity: sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==} + 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/context-async-hooks@2.6.1': - resolution: {integrity: sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==} + '@opentelemetry/core@2.6.1': + resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.6.1': - resolution: {integrity: sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==} + '@opentelemetry/core@2.7.1': + resolution: {integrity: sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-prometheus@0.214.0': - resolution: {integrity: sha512-4TGYoZKebUWVuYkV6r5wS2dUF4zH7EbWFw/Uqz1ZM1tGHQeFT9wzHGXq3iSIXMUrwu5jRdxjfMaXrYejPu2kpQ==} + '@opentelemetry/exporter-prometheus@0.217.0': + resolution: {integrity: sha512-U9MCXxJu0sBCh5aEkylYRR4xVIL8D1CW6dGwvYXbfFr0qveSorfD0XJchCAWoW6QfAAIcY/yxjf4Dj8OgkHBPw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-http@0.214.0': - resolution: {integrity: sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw==} + '@opentelemetry/exporter-trace-otlp-http@0.217.0': + resolution: {integrity: sha512-38YQoqtYjglz2GV94LGUN/djLvxtvGIQO68o6qAFPVshjmwSdX1F2i0c7vn3lEl1L5B/YqjB/bgKXaVx7KO+RQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -1714,12 +1755,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.62.0': - resolution: {integrity: sha512-Tvx+vgAZKEQxU3Rx+xWLiR0mLxHwmk69/8ya04+VsV9WYh8w6Lhx5hm5yAMvo1wy0KqWgFKBLwSeo3sHCwdOww==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fs@0.33.0': resolution: {integrity: sha512-sCZWXGalQ01wr3tAhSR9ucqFJ0phidpAle6/17HVjD6gN8FLmZMK/8sKxdXYHy3PbnlV1P4zeiSVFNKpbFMNLA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1750,12 +1785,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.62.0': - resolution: {integrity: sha512-ZYt//zcPve8qklaZX+5Z4MkU7UpEkFRrxsf2cnaKYBitqDnsCN69CPAuuMOX6NYdW2rG9sFy7V/QWtBlP5XiNQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-kafkajs@0.23.0': resolution: {integrity: sha512-4K+nVo+zI+aDz0Z85SObwbdixIbzS9moIuKJaYsdlzcHYnKOPtB7ya8r8Ezivy/GVIBHiKJVq4tv+BEkgOMLaQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1810,24 +1839,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-redis@0.62.0': - resolution: {integrity: sha512-y3pPpot7WzR/8JtHcYlTYsyY8g+pbFhAqbwAuG5bLPnR6v6pt1rQc0DpH0OlGP/9CZbWBP+Zhwp9yFoygf/ZXQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-tedious@0.33.0': resolution: {integrity: sha512-Q6WQwAD01MMTub31GlejoiFACYNw26J426wyjvU7by7fDIr2nZXNW4vhTGs7i7F0TnXBO3xN688g1tdUgYwJ5w==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-undici@0.24.0': - resolution: {integrity: sha512-oKzZ3uvqP17sV0EsoQcJgjEfIp0kiZRbYu/eD8p13Cbahumf8lb/xpYeNr/hfAJ4owzEtIDcGIjprfLcYbIKBQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.7.0 - '@opentelemetry/instrumentation@0.207.0': resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -1846,42 +1863,38 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.214.0': - resolution: {integrity: sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==} + '@opentelemetry/otlp-exporter-base@0.217.0': + resolution: {integrity: sha512-eYfqnB3UhKu/5frhd1R6+FprKygbhkomuaceMXDyzxbfXB9tKgZOVmjaJ02CkLA6Tdzumxl+e2H+vo2a8jiMPQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.214.0': - resolution: {integrity: sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==} + '@opentelemetry/otlp-transformer@0.217.0': + resolution: {integrity: sha512-MKK8UHKFUOGAvbZRWh90MhwHG+Fxm6OROBdjKPCF+HQobjuJ/Kuf8Chs8CR45X1aqotxrMj7OxTdsXe8sXuGVA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/redis-common@0.38.2': - resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} - engines: {node: ^18.19.0 || >=20.6.0} - - '@opentelemetry/resources@2.6.1': - resolution: {integrity: sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==} + '@opentelemetry/resources@2.7.1': + resolution: {integrity: sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.214.0': - resolution: {integrity: sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==} + '@opentelemetry/sdk-logs@0.217.0': + resolution: {integrity: sha512-BB+PcHItcZDL63dPMW+mJvwN9rk37wuIDjRxbVlg6pPDvDR/7GL7UJHbGsllgoggOoTimsKgENaWPoGch/oE1A==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@2.6.1': - resolution: {integrity: sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==} + '@opentelemetry/sdk-metrics@2.7.1': + resolution: {integrity: sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.6.1': - resolution: {integrity: sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==} + '@opentelemetry/sdk-trace-base@2.7.1': + resolution: {integrity: sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -1918,285 +1931,288 @@ packages: '@otplib/uri@13.4.0': resolution: {integrity: sha512-x1ozBa5bPbdZCrrTL/HK21qchiK7jYElTu+0ft22abeEhiLYgH1+SIULvOcVk3CK8YwF4kdcidvkq4ciejucJA==} - '@oxc-project/types@0.122.0': - resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==} + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxfmt/binding-android-arm-eabi@0.44.0': - resolution: {integrity: sha512-5UvghMd9SA/yvKTWCAxMAPXS1d2i054UeOf4iFjZjfayTwCINcC3oaSXjtbZfCaEpxgJod7XiOjTtby5yEv/BQ==} + '@oxc-project/types@0.129.0': + resolution: {integrity: sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==} + + '@oxfmt/binding-android-arm-eabi@0.47.0': + resolution: {integrity: sha512-KrMQRdMi/upr81qT4ijK6X6BNp6jqpMY7FwILQnwIy9QLc3qpnhUx5rsCLGzn4ewsCQ0CNAspN2ogmP1GXLyLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.44.0': - resolution: {integrity: sha512-IVudM1BWfvrYO++Khtzr8q9n5Rxu7msUvoFMqzGJVdX7HfUXUDHwaH2zHZNB58svx2J56pmCUzophyaPFkcG/A==} + '@oxfmt/binding-android-arm64@0.47.0': + resolution: {integrity: sha512-r4ixS/PeUpAFKgrpDoZ5pSkthjZzVzKd95525Aazj+aOv9H4ulK5zYHGb7wFY5n5kZxHK8TbOJUZgoEb1ohddQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.44.0': - resolution: {integrity: sha512-eWCLAIKAHfx88EqEP1Ga2yz7qVcqDU5lemn4xck+07bH182hDdprOHjbogyk0In1Djys3T0/pO2JepFnRJ41Mg==} + '@oxfmt/binding-darwin-arm64@0.47.0': + resolution: {integrity: sha512-CLWxiKpMl+195cm09CuaWEhJK0CirRkoMa07aR9+9AFPat2LfIKtwx1JqxZM0MTvcMe6+adlJNdVL6jdInvq3g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.44.0': - resolution: {integrity: sha512-eHTBznHLM49++dwz07MblQ2cOXyIgeedmE3Wgy4ptUESj38/qYZyRi1MPwC9olQJWssMeY6WI3UZ7YmU5ggvyQ==} + '@oxfmt/binding-darwin-x64@0.47.0': + resolution: {integrity: sha512-Xq5fjTYDC50faUeLSm0rZdBqoTgleXEdD7NpJdARtQIczkCJn3xNjMUSQQkUmh4CtxkKTNL68lytcOK3e/osgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.44.0': - resolution: {integrity: sha512-jLMmbj0u0Ft43QpkUVr/0v1ZfQCGWAvU+WznEHcN3wZC/q6ox7XeSJtk9P36CCpiDSUf3sGnzbIuG1KdEMEDJQ==} + '@oxfmt/binding-freebsd-x64@0.47.0': + resolution: {integrity: sha512-QOU9ZIJ52p5askcEC0QJvvr8trHAWoonul8bgISo6gYUL3s50zkqafBYcNAr9LJZQbsZtPfIWHk9+5+nUp1qJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.44.0': - resolution: {integrity: sha512-n+A/u/ByK1qV8FVGOwyaSpw5NPNl0qlZfgTBqHeGIqr8Qzq1tyWZ4lAaxPoe5mZqE3w88vn3+jZtMxriHPE7tg==} + '@oxfmt/binding-linux-arm-gnueabihf@0.47.0': + resolution: {integrity: sha512-oJxDM1aBhPvz9gmElBv8UpxyiqhwfjcbrSxT5F0xtuUzY6dQI27/AQPIt3eu3Z5Yvn0kQl5R7MA3Z+MbnRvCBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.44.0': - resolution: {integrity: sha512-5eax+FkxyCqAi3Rw0mrZFr7+KTt/XweFsbALR+B5ljWBLBl8nHe4ADrUnb1gLEfQCJLl+Ca5FIVD4xEt95AwIw==} + '@oxfmt/binding-linux-arm-musleabihf@0.47.0': + resolution: {integrity: sha512-g8Lh50VS4ibGz2q6v7r9UZY4D0dM16SdrFYOMzhqIoCwGcai8VMIRUAcqn1/jlCsOOzUXJ741+kCeJt0cofakQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.44.0': - resolution: {integrity: sha512-58l8JaHxSGOmOMOG2CIrNsnkRJAj0YcHQCmvNACniOa/vd1iRHhlPajczegzS5jwMENlqgreyiTR9iNlke8qCw==} + '@oxfmt/binding-linux-arm64-gnu@0.47.0': + resolution: {integrity: sha512-YrNT1vQ0asaXoRbrvYENPqmBfOQ9Xr8enPNOULeYfg44VjCcrUowFy5QZr+WawE0zyP8cH9e9Gxxg0fDEFzhcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.44.0': - resolution: {integrity: sha512-AlObQIXyVRZ96LbtVljtFq0JqH5B92NU+BQeDFrXWBUWlCKAM0wF5GLfIhCLT5kQ3Sl+U0YjRJ7Alqj5hGQaCg==} + '@oxfmt/binding-linux-arm64-musl@0.47.0': + resolution: {integrity: sha512-IxtQC/sbBi4ubbY+MdwdanRWrG9InQJVZqyMsBa5IUaQcnSg86gQme574HxXMC1p4bo4YhV99zQ+wNnGCvEgzw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.44.0': - resolution: {integrity: sha512-YcFE8/q/BbrCiIiM5piwbkA6GwJc5QqhMQp2yDrqQ2fuVkZ7CInb1aIijZ/k8EXc72qXMSwKpVlBv1w/MsGO/A==} + '@oxfmt/binding-linux-ppc64-gnu@0.47.0': + resolution: {integrity: sha512-EWXEhOMbWO0q6eJSbu0QLkU8cKi0ljlYLngeDs2Ocu/pm1rrLwyQiYzlFbdnMRURI4w9ndr1sI9rSbhlJ5o23Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.44.0': - resolution: {integrity: sha512-eOdzs6RqkRzuqNHUX5C8ISN5xfGh4xDww8OEd9YAmc3OWN8oAe5bmlIqQ+rrHLpv58/0BuU48bxkhnIGjA/ATQ==} + '@oxfmt/binding-linux-riscv64-gnu@0.47.0': + resolution: {integrity: sha512-tZrjS11TUiDuEpRaqdk8K9F9xETRyKXfuZKmdeW+Gj7coBnm7+8sBEfyt033EAFEQSlkniAXvBLh+Qja2ioGBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.44.0': - resolution: {integrity: sha512-YBgNTxntD/QvlFUfgvh8bEdwOhXiquX8gaofZJAwYa/Xp1S1DQrFVZEeck7GFktr24DztsSp8N8WtWCBwxs0Hw==} + '@oxfmt/binding-linux-riscv64-musl@0.47.0': + resolution: {integrity: sha512-KBFy+2CFKUCZzYwX2ZOPQKck1vjQbz+hextuc19G4r0WRJwadfAeuQMQRQvB+Ivc8brlbOVg7et8K7E467440g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.44.0': - resolution: {integrity: sha512-GLIh1R6WHWshl/i4QQDNgj0WtT25aRO4HNUWEoitxiywyRdhTFmFEYT2rXlcl9U6/26vhmOqG5cRlMLG3ocaIA==} + '@oxfmt/binding-linux-s390x-gnu@0.47.0': + resolution: {integrity: sha512-REUPFKVGSiK99B+9eaPhluEVglzaoj/SMykNC5SUiV2RSsBfV5lWN7Y0iCIc251Wz3GaeAGZsJ/zj3gjarxdFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.44.0': - resolution: {integrity: sha512-gZOpgTlOsLcLfAF9qgpTr7FIIFSKnQN3hDf/0JvQ4CIwMY7h+eilNjxq/CorqvYcEOu+LRt1W4ZS7KccEHLOdA==} + '@oxfmt/binding-linux-x64-gnu@0.47.0': + resolution: {integrity: sha512-KVftVSVEDeIfRW3TIeLe3aNI/iY4m1fu5mDwHcisKMZSCMKLkrhFsjowC7o9RoqNPxbbglm2+/6KAKBIts2t0Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.44.0': - resolution: {integrity: sha512-1CyS9JTB+pCUFYFI6pkQGGZaT/AY5gnhHVrQQLhFba6idP9AzVYm1xbdWfywoldTYvjxQJV6x4SuduCIfP3W+A==} + '@oxfmt/binding-linux-x64-musl@0.47.0': + resolution: {integrity: sha512-DTsmGEaA2860Aq5VUyDO8/MT9NFxwVL93RnRYmpMwK6DsSkThmvEpqoUDDljziEpAedMRG19SCogrNbINSbLUQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.44.0': - resolution: {integrity: sha512-bmEv70Ak6jLr1xotCbF5TxIKjsmQaiX+jFRtnGtfA03tJPf6VG3cKh96S21boAt3JZc+Vjx8PYcDuLj39vM2Pw==} + '@oxfmt/binding-openharmony-arm64@0.47.0': + resolution: {integrity: sha512-8r5BDro7fLOBoq1JXHLVSs55OlrxQhEso4HVo0TcY7OXJUPYfjPoOaYL5us+yIwqyP9rQwN+rxuiNFSmaxSuOQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.44.0': - resolution: {integrity: sha512-yWzB+oCpSnP/dmw85eFLAT5o35Ve5pkGS2uF/UCISpIwDqf1xa7OpmtomiqY/Vzg8VyvMbuf6vroF2khF/+1Vg==} + '@oxfmt/binding-win32-arm64-msvc@0.47.0': + resolution: {integrity: sha512-qtz/gzm8IjSPUlseZ0ofW8zyHLoZsuP5HTfcGGkWkUblB89JT8GNYH3ICqjbDsqsGqXum0/ZndXTFplSdXFIcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.44.0': - resolution: {integrity: sha512-TcWpo18xEIE3AmIG2kpr3kz5IEhQgnx0lazl2+8L+3eTopOAUevQcmlr4nhguImNWz0OMeOZrYZOhJNCf16nlQ==} + '@oxfmt/binding-win32-ia32-msvc@0.47.0': + resolution: {integrity: sha512-5vIcdcIDE7nCx+MXN6sm8kbC4zajDB31E86rez4i45iHNH/2NjdKlJ720xcHTr3eeiMcttCGPHPhE1TjtBDGZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.44.0': - resolution: {integrity: sha512-oj8aLkPJZppIM4CMQNsyir9ybM1Xw/CfGPTSsTnzpVGyljgfbdP0EVUlURiGM0BDrmw5psQ6ArmGCcUY/yABaQ==} + '@oxfmt/binding-win32-x64-msvc@0.47.0': + resolution: {integrity: sha512-Sr59Y5ms54ONBjxFeWhVlGyQcHXxcl9DxC23f6yXlRkcos7LXBLoO+KDfxexjHIOZh7cWqrWduzvUjJ+pHp8cQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.20.0': - resolution: {integrity: sha512-KKQcIHZHMxqpHUA1VXIbOG6chNCFkUWbQy6M+AFVtPKkA/3xAeJkJ3njoV66bfzwPHRcWQO+kcj5XqtbkjakoA==} + '@oxlint-tsgolint/darwin-arm64@0.22.1': + resolution: {integrity: sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.20.0': - resolution: {integrity: sha512-7HeVMuclGfG+NLZi2ybY0T4fMI7/XxO/208rJk+zEIloKkVnlh11Wd241JMGwgNFXn+MLJbOqOfojDb2Dt4L1g==} + '@oxlint-tsgolint/darwin-x64@0.22.1': + resolution: {integrity: sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.20.0': - resolution: {integrity: sha512-zxhUwz+WSxE6oWlZLK2z2ps9yC6ebmgoYmjAl0Oa48+GqkZ56NVgo+wb8DURNv6xrggzHStQxqQxe3mK51HZag==} + '@oxlint-tsgolint/linux-arm64@0.22.1': + resolution: {integrity: sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.20.0': - resolution: {integrity: sha512-/1l6FnahC9im8PK+Ekkx/V3yetO/PzZnJegE2FXcv/iXEhbeVxP/ouiTYcUQu9shT1FWJCSNti1VJHH+21Y1dg==} + '@oxlint-tsgolint/linux-x64@0.22.1': + resolution: {integrity: sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.20.0': - resolution: {integrity: sha512-oPZ5Yz8sVdo7P/5q+i3IKeix31eFZ55JAPa1+RGPoe9PoaYVsdMvR6Jvib6YtrqoJnFPlg3fjEjlEPL8VBKYJA==} + '@oxlint-tsgolint/win32-arm64@0.22.1': + resolution: {integrity: sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.20.0': - resolution: {integrity: sha512-4stx8RHj3SP9vQyRF/yZbz5igtPvYMEUR8CUoha4BVNZihi39DpCR8qkU7lpjB5Ga1DRMo2pHaA4bdTOMaY4mw==} + '@oxlint-tsgolint/win32-x64@0.22.1': + resolution: {integrity: sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.59.0': - resolution: {integrity: sha512-etYDw/UaEv936AQUd/CRMBVd+e+XuuU6wC+VzOv1STvsTyZenLChepLWqLtnyTTp4YMlM22ypzogDDwqYxv5cg==} + '@oxlint/binding-android-arm-eabi@1.62.0': + resolution: {integrity: sha512-pKsthNECyvJh8lPTICz6VcwVy2jOqdhhsp1rlxCkhgZR47aKvXPmaRWQDv+zlXpRae4qm1MaaTnutkaOk5aofg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.59.0': - resolution: {integrity: sha512-TgLc7XVLKH2a4h8j3vn1MDjfK33i9MY60f/bKhRGWyVzbk5LCZ4X01VZG7iHrMmi5vYbAp8//Ponigx03CLsdw==} + '@oxlint/binding-android-arm64@1.62.0': + resolution: {integrity: sha512-b1AUNViByvgmR2xJDubvLIr+dSuu3uraG7bsAoKo+xrpspPvu6RIn6Fhr2JUhobfep3jwUTy18Huco6GkwdvGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.59.0': - resolution: {integrity: sha512-DXyFPf5ZKldMLloRHx/B9fsxsiTQomaw7cmEW3YIJko2HgCh+GUhp9gGYwHrqlLJPsEe3dYj9JebjX92D3j3AA==} + '@oxlint/binding-darwin-arm64@1.62.0': + resolution: {integrity: sha512-iG+Tvf70UJ6otfwFYIHk36Sjq9cpPP5YLxkoggANNRtzgi3Tj3g8q6Ybqi6AtkU3+yg9QwF7bDCkCS6bbL4PCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.59.0': - resolution: {integrity: sha512-LgvrsdgVLX1qWqIEmNsSmMXJhpAWdtUQ0M+oR0CySwi+9IHWyOGuIL8w8+u/kbZNMyZr4WUyYB5i0+D+AKgkLg==} + '@oxlint/binding-darwin-x64@1.62.0': + resolution: {integrity: sha512-oOWI6YPPr5AJUx+yIDlxmuUbQjS5gZX3OH3QisawYvsZgLiQVvZtR0rPBcJTxLWqt2ClrWg0DlSrlUiG5SQNHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.59.0': - resolution: {integrity: sha512-bOJhqX/ny4hrFuTPlyk8foSRx/vLRpxJh0jOOKN2NWW6FScXHPAA5rQbrwdQPcgGB5V8Ua51RS03fke8ssBcug==} + '@oxlint/binding-freebsd-x64@1.62.0': + resolution: {integrity: sha512-dLP33T7VLCmLVv4cvjkVX+rmkcwNk2UfxmsZPNur/7BQHoQR60zJ7XLiRvNUawlzn0u8ngCa3itjEG73MAMa/w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.59.0': - resolution: {integrity: sha512-vVUXxYMF9trXCsz4m9H6U0IjehosVHxBzVgJUxly1uz4W1PdDyicaBnpC0KRXsHYretLVe+uS9pJy8iM57Kujw==} + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': + resolution: {integrity: sha512-fl//LWNks6qo9chNY60UDYyIwtp7a5cEx4Y/rHPjaarhuwqx6jtbzEpD5V5AqmdL4a6Y5D8zeXg5HF2Cr0QmSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.59.0': - resolution: {integrity: sha512-TULQW8YBPGRWg5yZpFPL54HLOnJ3/HiX6VenDPi6YfxB/jlItwSMFh3/hCeSNbh+DAMaE1Py0j5MOaivHkI/9Q==} + '@oxlint/binding-linux-arm-musleabihf@1.62.0': + resolution: {integrity: sha512-i5vkAuxvueTODV3J2dL61/TXewDHhMFKvtD156cIsk7GsdfiAu7zW7kY0NJXhKeFHeiMZIh7eFNjkPYH6J47HQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.59.0': - resolution: {integrity: sha512-Gt54Y4eqSgYJ90xipm24xeyaPV854706o/kiT8oZvUt3VDY7qqxdqyGqchMaujd87ib+/MXvnl9WkK8Cc1BExg==} + '@oxlint/binding-linux-arm64-gnu@1.62.0': + resolution: {integrity: sha512-QwN19LLuIGuOjEflSeJkZmOTfBdBMlTmW8xbMf8TZhjd//cxVNYQPq75q7oKZBJc6hRx3gY7sX0Egc8cEIFZYg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.59.0': - resolution: {integrity: sha512-3CtsKp7NFB3OfqQzbuAecrY7GIZeiv7AD+xutU4tefVQzlfmTI7/ygWLrvkzsDEjTlMq41rYHxgsn6Yh8tybmA==} + '@oxlint/binding-linux-arm64-musl@1.62.0': + resolution: {integrity: sha512-8eCy3FCDuWUM5hWujAv6heMvfZPbcCOU3SdQUAkixZLu5bSzOkNfirJiLGoQFO943xceOKkiQRMQNzH++jM3WA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.59.0': - resolution: {integrity: sha512-K0diOpT3ncDmOfl9I1HuvpEsAuTxkts0VYwIv/w6Xiy9CdwyPBVX88Ga9l8VlGgMrwBMnSY4xIvVlVY/fkQk7Q==} + '@oxlint/binding-linux-ppc64-gnu@1.62.0': + resolution: {integrity: sha512-NjQ7K7tpTPDe9J+yq8p/s/J0E7lRCkK2uDBDqvT4XIT6f4Z0tlnr59OBg/WcrmVHER1AbrcfyxhGTXgcG8ytWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.59.0': - resolution: {integrity: sha512-xAU7+QDU6kTJJ7mJLOGgo7oOjtAtkKyFZ0Yjdb5cEo3DiCCPFLvyr08rWiQh6evZ7RiUTf+o65NY/bqttzJiQQ==} + '@oxlint/binding-linux-riscv64-gnu@1.62.0': + resolution: {integrity: sha512-oKZed9gmSwze29dEt3/Wnsv6l/Ygw/FUst+8Kfpv2SGeS/glEoTGZAMQw37SVyzFV76UTHJN2snGgxK2t2+8ow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.59.0': - resolution: {integrity: sha512-KUmZmKlTTyauOnvUNVxK7G40sSSx0+w5l1UhaGsC6KPpOYHenx2oqJTnabmpLJicok7IC+3Y6fXAUOMyexaeJQ==} + '@oxlint/binding-linux-riscv64-musl@1.62.0': + resolution: {integrity: sha512-gBjBxQ+9lGpAYq+ELqw0w8QXsBnkZclFc7GRX2r0LnEVn3ZTEqeIKpKcGjucmp76Q53bvJD0i4qBWBhcfhSfGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.59.0': - resolution: {integrity: sha512-4usRxC8gS0PGdkHnRmwJt/4zrQNZyk6vL0trCxwZSsAKM+OxhB8nKiR+mhjdBbl8lbMh2gc3bZpNN/ik8c4c2A==} + '@oxlint/binding-linux-s390x-gnu@1.62.0': + resolution: {integrity: sha512-Ew2Kxs9EQ9/mbAIJ2hvocMC0wsOu6YKzStI2eFBDt+Td5O8seVC/oxgRIHqCcl5sf5ratA1nozQBAuv7tphkHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.59.0': - resolution: {integrity: sha512-s/rNE2gDmbwAOOP493xk2X7M8LZfI1LJFSSW1+yanz3vuQCFPiHkx4GY+O1HuLUDtkzGlhtMrIcxxzyYLv308w==} + '@oxlint/binding-linux-x64-gnu@1.62.0': + resolution: {integrity: sha512-5z25jcAA0gfKyVwz71A0VXgaPlocPoTAxhlv/hgoK6tlCrfoNuw7haWbDHvGMfjXhdic4EqVXGRv5XsTqFnbRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.59.0': - resolution: {integrity: sha512-+yYj1udJa2UvvIUmEm0IcKgc0UlPMgz0nsSTvkPL2y6n0uU5LgIHSwVu4AHhrve6j9BpVSoRksnz8c9QcvITJA==} + '@oxlint/binding-linux-x64-musl@1.62.0': + resolution: {integrity: sha512-IWpHmMB6ZDllPvqWDkG6AmXrN7JF5e/c4g/0PuURsmlK+vHoYZPB70rr4u1bn3I4LsKCSpqqfveyx6UCOC8wdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.59.0': - resolution: {integrity: sha512-bUplUb48LYsB3hHlQXP2ZMOenpieWoOyppLAnnAhuPag3MGPnt+7caxE3w/Vl9wpQsTA3gzLntQi9rxWrs7Xqg==} + '@oxlint/binding-openharmony-arm64@1.62.0': + resolution: {integrity: sha512-fjlSxxrD5pA594vkyikCS9MnPRjQawW6/BLgyTYkO+73wwPlYjkcZ7LSd974l0Q2zkHQmu4DPvJFLYA7o8xrxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.59.0': - resolution: {integrity: sha512-/HLsLuz42rWl7h7ePdmMTpHm2HIDmPtcEMYgm5BBEHiEiuNOrzMaUpd2z7UnNni5LGN9obJy2YoAYBLXQwazrA==} + '@oxlint/binding-win32-arm64-msvc@1.62.0': + resolution: {integrity: sha512-EiFXr8loNS0Ul3Gu80+9nr1T8jRmnKocqmHHg16tj5ZqTgUXyb97l2rrspVHdDluyFn9JfR4PoJFdNzw4paHww==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.59.0': - resolution: {integrity: sha512-rUPy+JnanpPwV/aJCPnxAD1fW50+XPI0VkWr7f0vEbqcdsS8NpB24Rw6RsS7SdpFv8Dw+8ugCwao5nCFbqOUSg==} + '@oxlint/binding-win32-ia32-msvc@1.62.0': + resolution: {integrity: sha512-IgOFvL73li1bFgab+hThXYA0N2Xms2kV2MvZN95cebV+fmrZ9AVui1JSxfeeqRLo3CpPxKZlzhyq4G0cnaAvIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.59.0': - resolution: {integrity: sha512-xkE7puteDS/vUyRngLXW0t8WgdWoS/tfxXjhP/P7SMqPDx+hs44SpssO3h3qmTqECYEuXBUPzcAw5257Ka+ofA==} + '@oxlint/binding-win32-x64-msvc@1.62.0': + resolution: {integrity: sha512-6hMpyDWQ2zGA1OXFKBrdYMUveUCO8UJhkO6JdwZPd78xIdHZNhjx+pib+4fC2Cljuhjyl0QwA2F3df/bs4Bp6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/plugins@1.59.0': - resolution: {integrity: sha512-KIEedExMc60HKrkZi79403NbSgnJUyC9w6dCsqrY45ygIPzEyFdpxboiTyI8ZjeEw6Os03bspgZBfm6/fZbkOg==} + '@oxlint/plugins@1.62.0': + resolution: {integrity: sha512-BgKRw631mImmxuGuRoWaRT8AvNSJCfJvR7lDzP1jR3sBAa7q8P/Ii+lcEOEFZsfcfpkXLyUKGEQLYXWqb60wvw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@package-json/types@0.0.12': @@ -2247,6 +2263,9 @@ packages: '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} @@ -2259,6 +2278,9 @@ packages: '@protobufjs/inquire@1.1.0': resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + '@protobufjs/inquire@1.1.1': + resolution: {integrity: sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==} + '@protobufjs/path@1.1.2': resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} @@ -2268,121 +2290,207 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@puppeteer/browsers@2.10.3': - resolution: {integrity: sha512-iPpnFpX25gKIVsHsqVjHV+/GzW36xPgsscWkCnrrETndcdxNsXLdCrTwhkCJNR/FGWr122dJUBeyV4niz/j3TA==} - engines: {node: '>=18'} - hasBin: true - - '@puppeteer/browsers@2.2.4': - resolution: {integrity: sha512-BdG2qiI1dn89OTUUsx2GZSpUzW+DRffR1wlMJyKxVHYrhnKoELSDxDd+2XImUkuWPEKk76H5FcM/gPFrEK1Tfw==} - engines: {node: '>=18'} - hasBin: true - - '@puppeteer/browsers@2.6.1': - resolution: {integrity: sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==} - engines: {node: '>=18'} - hasBin: true + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} '@quansync/fs@1.0.0': resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} - '@rolldown/binding-android-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} + '@rolldown/binding-android-arm64@1.0.0': + resolution: {integrity: sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@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-darwin-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} + '@rolldown/binding-darwin-arm64@1.0.0': + resolution: {integrity: sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@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-x64@1.0.0-rc.12': - resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} + '@rolldown/binding-darwin-x64@1.0.0': + resolution: {integrity: sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + 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-freebsd-x64@1.0.0-rc.12': - resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} + '@rolldown/binding-freebsd-x64@1.0.0': + resolution: {integrity: sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@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-linux-arm-gnueabihf@1.0.0-rc.12': - resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': + resolution: {integrity: sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@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-arm64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0': + resolution: {integrity: sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@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-musl@1.0.0-rc.12': - resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} + '@rolldown/binding-linux-arm64-musl@1.0.0': + resolution: {integrity: sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} + '@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-ppc64-gnu@1.0.0': + resolution: {integrity: sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@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-s390x-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} + '@rolldown/binding-linux-s390x-gnu@1.0.0': + resolution: {integrity: sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + 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-x64-gnu@1.0.0-rc.12': - resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} + '@rolldown/binding-linux-x64-gnu@1.0.0': + resolution: {integrity: sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + 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-musl@1.0.0-rc.12': - resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} + '@rolldown/binding-linux-x64-musl@1.0.0': + resolution: {integrity: sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@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-openharmony-arm64@1.0.0-rc.12': - resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} + '@rolldown/binding-openharmony-arm64@1.0.0': + resolution: {integrity: sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': - resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} - engines: {node: '>=14.0.0'} + '@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-wasm32-wasi@1.0.0': + resolution: {integrity: sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@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-win32-arm64-msvc@1.0.0-rc.12': - resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} + '@rolldown/binding-win32-arm64-msvc@1.0.0': + resolution: {integrity: sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@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-x64-msvc@1.0.0-rc.12': - resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} + '@rolldown/binding-win32-x64-msvc@1.0.0': + resolution: {integrity: sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + 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/pluginutils@1.0.0-rc.12': - resolution: {integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==} + '@rolldown/pluginutils@1.0.0': + resolution: {integrity: sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==} + + '@rolldown/pluginutils@1.0.0-rc.17': + resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -2393,141 +2501,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.60.1': - resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} + '@rollup/rollup-android-arm-eabi@4.60.3': + resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.1': - resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} + '@rollup/rollup-android-arm64@4.60.3': + resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.1': - resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} + '@rollup/rollup-darwin-arm64@4.60.3': + resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.1': - resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} + '@rollup/rollup-darwin-x64@4.60.3': + resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.1': - resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} + '@rollup/rollup-freebsd-arm64@4.60.3': + resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.1': - resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} + '@rollup/rollup-freebsd-x64@4.60.3': + resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': - resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.1': - resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.1': - resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} + '@rollup/rollup-linux-arm64-gnu@4.60.3': + resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.1': - resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} + '@rollup/rollup-linux-arm64-musl@4.60.3': + resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.1': - resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} + '@rollup/rollup-linux-loong64-gnu@4.60.3': + resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.1': - resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} + '@rollup/rollup-linux-loong64-musl@4.60.3': + resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.1': - resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.1': - resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} + '@rollup/rollup-linux-ppc64-musl@4.60.3': + resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.1': - resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.1': - resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} + '@rollup/rollup-linux-riscv64-musl@4.60.3': + resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.1': - resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} + '@rollup/rollup-linux-s390x-gnu@4.60.3': + resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.1': - resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} + '@rollup/rollup-linux-x64-gnu@4.60.3': + resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.1': - resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} + '@rollup/rollup-linux-x64-musl@4.60.3': + resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.1': - resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} + '@rollup/rollup-openbsd-x64@4.60.3': + resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.1': - resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} + '@rollup/rollup-openharmony-arm64@4.60.3': + resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.1': - resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} + '@rollup/rollup-win32-arm64-msvc@4.60.3': + resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.1': - resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} + '@rollup/rollup-win32-ia32-msvc@4.60.3': + resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.1': - resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} + '@rollup/rollup-win32-x64-gnu@4.60.3': + resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.1': - resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} + '@rollup/rollup-win32-x64-msvc@4.60.3': + resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} cpu: [x64] os: [win32] @@ -2540,22 +2648,22 @@ packages: '@rss3/sdk@0.0.25': resolution: {integrity: sha512-jyXT4YTwefxxRZ0tt5xjbnw8e7zPg2OGdo/0xb+h/7qWnMNhLtWpc95DsYs/1C/I0rIyiDpZBhLI2DieQ9y+tw==} - '@scalar/core@0.4.6': - resolution: {integrity: sha512-lIlCUx0O18ei63j7Z7QTAVAuh66sdms9zoDPhpDOzHTVObnb7n0f3J306sT3H9KQsoWKQrEsnKx4LcZLw/TlPw==} + '@scalar/client-side-rendering@0.1.7': + resolution: {integrity: sha512-IDzjKF93jrOljlvKBsLHXT1FPWgz56jFrMPC+iLihREp1qH8wF92mG8Zpakw8cURkEuw5WijRk0xNBP2moGyuw==} engines: {node: '>=22'} - '@scalar/helpers@0.4.3': - resolution: {integrity: sha512-Gv2V7SFreLx3DltzF2lKXdaJSH5cP1LOyt9PxON1cSWGxkrs3sg93c1taEJsW24E9ckfYXkL5hjCAVLfAN3wQw==} + '@scalar/helpers@0.6.0': + resolution: {integrity: sha512-pfSamAgBxqFeE8IpEG6uGkHlnPhY1CLeOTttV9+vKQbrBk5b7vvyTsUXv0Hz4kNU1TFrxcTTPE+Akn5S+jlTtQ==} engines: {node: '>=22'} - '@scalar/hono-api-reference@0.10.6': - resolution: {integrity: sha512-9/sZvJ2P0TCMnTqQHKC7RGT6qBauJ1Z+diiFRtLuJhCk4e+6uzPOD0QmRwpUARK7NXZp4oNPm2hXHHT6GqZZAQ==} + '@scalar/hono-api-reference@0.10.14': + resolution: {integrity: sha512-LCIT4ul3c4MyD7shhxsWcvvOABt0fEHNQID2n+2TPeItc/MR2qCjjp/QfqD+JoQ7zbc0nnzh1kwRR06MVBmnUA==} engines: {node: '>=22'} peerDependencies: hono: ^4.12.5 - '@scalar/types@0.7.6': - resolution: {integrity: sha512-n5d7neeGTY/yS36AwAv3FAyCnJbbLjMPwyuEXBLgxHcb5i8DXnNfwy1nzz8jx/EJ88i1zmQ8BuMs+207saHgNA==} + '@scalar/types@0.9.6': + resolution: {integrity: sha512-UaCQQcscFTJdxZREE8KhUdSJgaDlc44TZbmWcZffs4m1hzqOvEI7lEBS13iBpLq7/cxUXFgyJdecywvNqJ0PkA==} engines: {node: '>=22'} '@scure/base@2.0.0': @@ -2567,50 +2675,48 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sentry/core@10.47.0': - resolution: {integrity: sha512-nsYRAx3EWezDut+Zl+UwwP07thh9uY7CfSAi2whTdcJl5hu1nSp2z8bba7Vq/MGbNLnazkd3A+GITBEML924JA==} + '@selderee/plugin-htmlparser2@0.12.0': + resolution: {integrity: sha512-oELmoyA6ML9jDRMV3kgcMQFKxUfBU0yFVn6yTctVaLT5ygXnxH52I3TZEgV9EhXJC68/uFvE5Daj1/25c0Xa/A==} + peerDependencies: + selderee: ~0.12.0 + + '@sentry/core@10.52.0': + resolution: {integrity: sha512-VA/kAqLhkMnRWY2RXdBLyTemR9D4m7MVRy/gyapoq9yvllVPx9WXbvKgnMP2LQp7mFgT/oLFvw58aQKaYTGn3A==} engines: {node: '>=18'} - '@sentry/node-core@10.47.0': - resolution: {integrity: sha512-qv6LsqHbkQmd0aQEUox/svRSz26J+l4gGjFOUNEay2armZu9XLD+Ct89jpFgZD5oIPNAj2jraodTRqydXiwS5w==} + '@sentry/node-core@10.52.0': + resolution: {integrity: sha512-IG7MBtLRPQ2LuU+kbD14AFZroZgAeUmJQTP1FI/F8n56O31+p+9R703LuBTpvZr6sm+eRYDMWcGYYkfLHRVjwg==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/exporter-trace-otlp-http': '>=0.57.0 <1' '@opentelemetry/instrumentation': '>=0.57.1 <1' - '@opentelemetry/resources': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 peerDependenciesMeta: '@opentelemetry/api': optional: true - '@opentelemetry/context-async-hooks': - optional: true '@opentelemetry/core': optional: true '@opentelemetry/exporter-trace-otlp-http': optional: true '@opentelemetry/instrumentation': optional: true - '@opentelemetry/resources': - optional: true '@opentelemetry/sdk-trace-base': optional: true '@opentelemetry/semantic-conventions': optional: true - '@sentry/node@10.47.0': - resolution: {integrity: sha512-R+btqPepv88o635G6HtVewLjqCLUedBg5HBs7Nq1qbbKvyti01uArUF2f+3DsLenk5B9LUNiRlE+frZA44Ahmw==} + '@sentry/node@10.52.0': + resolution: {integrity: sha512-9+p3KJUk3rHO1HOEZuSknP2RgKCJZONDm4HWgkVDtVBtocb66KLtVlMjc59d2/bWP7tM3wc877tpG30quFfU9g==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.47.0': - resolution: {integrity: sha512-f6Hw2lrpCjlOksiosP0Z2jK/+l+21SIdoNglVeG/sttMyx8C8ywONKh0Ha50sFsvB1VaB8n94RKzzf3hkh9V3g==} + '@sentry/opentelemetry@10.52.0': + resolution: {integrity: sha512-Sc7StsvC0bwhMcgDfTRWUIexO5cNzzKUurvUwtpgQUnxO7AzexU3lkY3yHYDsCbWYAEQMXAgQYQtbcqoh+Ie7g==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 @@ -2623,6 +2729,10 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} + '@sindresorhus/is@8.0.0': + resolution: {integrity: sha512-YvOsokBE5NsyHuXMnwEVB7lgFk6lX8F4OXCruYVgFII0hUHof0RGUvhMoabVt9hRqbg+qVzayzEG24RCcxcYeA==} + engines: {node: '>=22'} + '@so-ric/colorspace@1.1.6': resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} @@ -2638,12 +2748,12 @@ packages: peerDependencies: eslint: ^9.0.0 || ^10.0.0 - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@types/aes-js@3.1.4': resolution: {integrity: sha512-v3D66IptpUqh+pHKVNRxY8yvp2ESSZXe0rTzsGdzUhEwag7ljVfgCllkWv2YgiYXDhWFBrEywll4A5JToyTNFA==} @@ -2653,9 +2763,6 @@ packages: '@types/babel__preset-env@7.10.0': resolution: {integrity: sha512-LS8hRb/8TQir2f8W9/s5enDtrRS2F/6fsdkVw5ePHp6Q8SrSJHOGtWnP93ryaYMmg2du03vOsiGrl5mllz4uDA==} - '@types/bezier-js@4.1.3': - resolution: {integrity: sha512-FNVVCu5mx/rJCWBxLTcL7oOajmGtWtBTDjq6DSUWUI12GeePivrZZXz+UgE0D6VYsLEjvExRO03z4hVtu3pTEQ==} - '@types/bluebird@3.5.42': resolution: {integrity: sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==} @@ -2758,8 +2865,8 @@ packages: '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} - '@types/node@25.5.2': - resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} + '@types/node@25.6.2': + resolution: {integrity: sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==} '@types/pg-pool@2.0.7': resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} @@ -2767,8 +2874,8 @@ packages: '@types/pg@8.15.6': resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} - '@types/qs@6.15.0': - resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -2788,6 +2895,9 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} @@ -2800,45 +2910,45 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - - '@typescript-eslint/eslint-plugin@8.58.1': - resolution: {integrity: sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==} + '@typescript-eslint/eslint-plugin@8.59.2': + resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.1 + '@typescript-eslint/parser': ^8.59.2 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.58.1': - resolution: {integrity: sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==} + '@typescript-eslint/parser@8.59.2': + resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} 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.58.1': - resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.58.1': - resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.58.1': - resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.58.1': - resolution: {integrity: sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==} + '@typescript-eslint/type-utils@8.59.2': + resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2848,25 +2958,25 @@ packages: resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.58.1': - resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.58.1': - resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.58.1': - resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} 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.58.1': - resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -2977,47 +3087,50 @@ packages: engines: {node: '>=20'} hasBin: true - '@vitest/coverage-v8@4.0.9': - resolution: {integrity: sha512-70oyhP+Q0HlWBIeGSP74YBw5KSjYhNgSCQjvmuQFciMqnyF36WL2cIkcT7XD85G4JPmBQitEMUsx+XMFv2AzQA==} + '@vitest/coverage-v8@4.1.5': + resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==} peerDependencies: - '@vitest/browser': 4.0.9 - vitest: 4.0.9 + '@vitest/browser': 4.1.5 + vitest: 4.1.5 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.0.9': - resolution: {integrity: sha512-C2vyXf5/Jfj1vl4DQYxjib3jzyuswMi/KHHVN2z+H4v16hdJ7jMZ0OGe3uOVIt6LyJsAofDdaJNIFEpQcrSTFw==} + '@vitest/expect@4.1.5': + resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} - '@vitest/mocker@4.0.9': - resolution: {integrity: sha512-PUyaowQFHW+9FKb4dsvvBM4o025rWMlEDXdWRxIOilGaHREYTi5Q2Rt9VCgXgPy/hHZu1LeuXtrA/GdzOatP2g==} + '@vitest/mocker@4.1.5': + resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.0.9': - resolution: {integrity: sha512-Hor0IBTwEi/uZqB7pvGepyElaM8J75pYjrrqbC8ZYMB9/4n5QA63KC15xhT+sqHpdGWfdnPo96E8lQUxs2YzSQ==} + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} - '@vitest/runner@4.0.9': - resolution: {integrity: sha512-aF77tsXdEvIJRkj9uJZnHtovsVIx22Ambft9HudC+XuG/on1NY/bf5dlDti1N35eJT+QZLb4RF/5dTIG18s98w==} + '@vitest/runner@4.1.5': + resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} - '@vitest/snapshot@4.0.9': - resolution: {integrity: sha512-r1qR4oYstPbnOjg0Vgd3E8ADJbi4ditCzqr+Z9foUrRhIy778BleNyZMeAJ2EjV+r4ASAaDsdciC9ryMy8xMMg==} + '@vitest/snapshot@4.1.5': + resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} - '@vitest/spy@4.0.9': - resolution: {integrity: sha512-J9Ttsq0hDXmxmT8CUOWUr1cqqAj2FJRGTdyEjSR+NjoOGKEqkEWj+09yC0HhI8t1W6t4Ctqawl1onHgipJve1A==} + '@vitest/spy@4.1.5': + resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} - '@vitest/utils@4.0.9': - resolution: {integrity: sha512-cEol6ygTzY4rUPvNZM19sDf7zGa35IYTm9wfzkHoT/f5jX10IOY7QleWSOh5T0e3I3WVozwK5Asom79qW8DiuQ==} + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} '@zone-eu/mailsplit@5.4.8': resolution: {integrity: sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==} + '@zone-eu/mailsplit@5.4.9': + resolution: {integrity: sha512-Qq7k6FzA5SmGf5HFPcr17gE7M+O1gttlmWn7tlGUlhGsbbjUaBL/4cEWIwExeCzqu5+kyZJ91mcBZbQ9zEwwYA==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3108,8 +3221,8 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} - ast-v8-to-istanbul@0.3.12: - resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} async-mutex@0.3.2: resolution: {integrity: sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==} @@ -3133,14 +3246,6 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - b4a@1.8.0: - resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3151,47 +3256,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - bare-events@2.8.2: - resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - bare-fs@4.5.6: - resolution: {integrity: sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==} - engines: {bare: '>=1.16.0'} - peerDependencies: - bare-buffer: '*' - peerDependenciesMeta: - bare-buffer: - optional: true - - bare-os@3.8.6: - resolution: {integrity: sha512-l8xaNWWb/bXuzgsrlF5jaa5QYDJ9S0ddd54cP6CH+081+5iPrbJiCfBWQqrWYzmUhCbsH+WR6qxo9MeHVCr0MQ==} - engines: {bare: '>=1.14.0'} - - bare-path@3.0.0: - resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - - bare-stream@2.11.0: - resolution: {integrity: sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==} - peerDependencies: - bare-abort-controller: '*' - bare-buffer: '*' - bare-events: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - bare-buffer: - optional: true - bare-events: - optional: true - - bare-url@2.4.0: - resolution: {integrity: sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==} - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3200,10 +3264,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - basic-ftp@5.2.0: - resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} + basic-ftp@5.3.0: + resolution: {integrity: sha512-5K9eNNn7ywHPsYnFwjKgYH8Hf8B5emh7JKcPaVjjrMJFQQwGpwowEnZNEtHs7DfR7hCZsmaK3VA4HUK0YarT+w==} engines: {node: '>=10.0.0'} - deprecated: Security vulnerability fixed in 5.2.1, please upgrade bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} @@ -3211,9 +3274,6 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - bezier-js@6.1.4: - resolution: {integrity: sha512-PA0FW9ZpcHbojUCMu28z9Vg/fNkwTj5YhusSAjHHDfHDGLxJ6YUKrAN2vk1fP2MMOxVw4Oko16FMlRGVBGqLKg==} - bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} @@ -3248,8 +3308,8 @@ packages: brace-expansion@1.1.13: resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} - brace-expansion@2.0.3: - resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} brace-expansion@5.0.5: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} @@ -3260,15 +3320,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -3317,6 +3371,9 @@ packages: caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -3345,27 +3402,16 @@ packages: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} - chrome-launcher@1.2.1: - resolution: {integrity: sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==} - engines: {node: '>=12.13.0'} - hasBin: true - - chromium-bidi@0.8.0: - resolution: {integrity: sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==} - peerDependencies: - devtools-protocol: '*' - - chromium-bidi@5.1.0: - resolution: {integrity: sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==} - peerDependencies: - devtools-protocol: '*' + chunk-data@0.1.0: + resolution: {integrity: sha512-zFyPtyC0SZ6Zu79b9sOYtXZcgrsXe0RpePrzRyj52hYVFG1+Rk6rBqjjOEk+GNQwc3PIX+86teQMok970pod1g==} + engines: {node: '>=20'} ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} - city-timezones@1.3.3: - resolution: {integrity: sha512-tyH1Tje3mee1mWkjerhx/8CLOfTJn6A5L6swAqLRceoToj9bvKNkfcKESoxG9rApXBKxKeZQUQbbzYcoRSJbZw==} + city-timezones@1.3.4: + resolution: {integrity: sha512-yLmtCxU4y5HLAw9XGIMcGEXO+R2KH1sb595wEQC/E9BxSDpazCD7v9ZQRySO7367IXWsWqt3z2xFVh3OkZdgEQ==} cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -3424,9 +3470,6 @@ packages: resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} engines: {node: '>=18'} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3435,10 +3478,6 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} - comment-parser@1.4.6: resolution: {integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==} engines: {node: '>= 12.0.0'} @@ -3453,6 +3492,9 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} @@ -3467,15 +3509,6 @@ packages: core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - cosmiconfig@9.0.1: - resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -3514,10 +3547,6 @@ packages: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - data-uri-to-buffer@8.0.0: resolution: {integrity: sha512-6UHfyCux51b8PTGDgveqtz1tvphBku5DrMKKJbFAZAJOI2zsjDpDoYE1+QGj7FOMS4BdTFNJsJiR3zEB0xH0yQ==} engines: {node: '>= 20'} @@ -3579,6 +3608,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -3587,12 +3620,8 @@ packages: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} degenerator@7.0.1: resolution: {integrity: sha512-ABErK0IefDSyHjlPH7WUEenIAX2rPPnrDcDM+TS3z3+zu9TfyKKi07BQM+8rmxpdE2y1v5fjjdoAS/x4D2U60w==} @@ -3622,15 +3651,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - devtools-protocol@0.0.1299070: - resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} - - devtools-protocol@0.0.1367902: - resolution: {integrity: sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==} - - devtools-protocol@0.0.1439962: - resolution: {integrity: sha512-jJF48UdryzKiWhJ1bLKr7BFWUQCEIT5uCNbDLqkQJBtkFxYzILJH44WN0PDKMIlGDN7Utb8vyUY85C3w4R/t2g==} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3639,8 +3659,8 @@ packages: resolution: {tarball: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed} version: 0.2.6 - discord-api-types@0.38.44: - resolution: {integrity: sha512-q91MgBzP/gRaCLIbQTaOrOhbD8uVIaPKxpgX2sfFB2nZ9nSiTYM9P3NFQ7cbO6NCxctI6ODttc67MI+YhIfILg==} + discord-api-types@0.38.47: + resolution: {integrity: sha512-XgXQodHQBAE6kfD7kMvVo30863iHX1LHSqNq6MGUTDwIFCCvHva13+rwxyxVXDqudyApMNAd32PGjgVETi5rjA==} dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -3677,13 +3697,13 @@ packages: resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} engines: {node: '>=10'} - dotenv@17.4.1: - resolution: {integrity: sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} engines: {node: '>=12'} - dts-resolver@2.1.3: - resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} - engines: {node: '>=20.19.0'} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} peerDependencies: oxc-resolver: '>=11.0.0' peerDependenciesMeta: @@ -3707,9 +3727,6 @@ packages: electron-to-chromium@1.5.330: resolution: {integrity: sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA==} - ellipsize@0.6.0: - resolution: {integrity: sha512-oest45O39yGzIKJj3gJv/Gt6jtYN6PHw/IbCXHPEd2KwC0dgxKqXOhTU0EGwRXWhGKoBJk7eJQkb3sp8W0px2g==} - emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3719,8 +3736,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - empathic@2.0.0: - resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} enabled@2.0.0: @@ -3733,14 +3750,11 @@ packages: encoding-sniffer@0.2.1: resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - endpoint@0.4.5: resolution: {integrity: sha512-oA2ALUF+d4Y0I8/WMV/0BuAZGHxfIdAygr9ZXP4rfzmp5zpYZmYKHKAbqRQnrE1YGdPhVg4D24CQkyx2qYEoHg==} - enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + enhanced-resolve@5.21.0: + resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -3762,22 +3776,19 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} @@ -3879,11 +3890,18 @@ packages: eslint-import-resolver-node: optional: true - eslint-plugin-n@17.24.0: - resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-plugin-n@18.0.1: + resolution: {integrity: sha512-q3ARhk+eZRc7myR0KHx+R3/GJeOHF+Ir6PK95Pu2tEX8Sl/4BIpmmVLva2kPrjC2gCmn6WHlHm+3yeo6Rxhycw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: '>=8.23.0' + eslint: '>=8.57.1' + ts-declaration-location: ^1.0.6 + typescript: '>=5.0.0' + peerDependenciesMeta: + ts-declaration-location: + optional: true + typescript: + optional: true eslint-plugin-simple-import-sort@13.0.0: resolution: {integrity: sha512-McAc+/Nlvcg4byY/CABGH8kqnefWBj8s3JA2okEtz8ixbECQgU46p0HkTUKa4YS7wvgGceimlc34p1nXqbWqtA==} @@ -3896,8 +3914,8 @@ packages: peerDependencies: eslint: '>=9.38.0' - eslint-plugin-yml@3.3.1: - resolution: {integrity: sha512-isntsZchaTqDMNNkD+CakrgA/pdUoJ45USWBKpuqfAW1MCuw731xX/vrXfoJFZU3tTFr24nCbDYmDfT2+g4QtQ==} + eslint-plugin-yml@3.3.2: + resolution: {integrity: sha512-XjmOB/fLBwYHqevnpclPL938V+9ExX7xw1hPaM3IPePGyMFRV1giS16RjSTNhIyCv/Oh0G0PEdmmZPATJ02YCw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: eslint: '>=9.38.0' @@ -3918,8 +3936,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.2.0: - resolution: {integrity: sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==} + eslint@10.3.0: + resolution: {integrity: sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -3977,9 +3995,6 @@ packages: eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3994,11 +4009,6 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} @@ -4013,17 +4023,20 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -4096,10 +4109,15 @@ packages: forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} - fs-extra@11.3.4: - resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} 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} @@ -4124,10 +4142,6 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -4139,9 +4153,12 @@ packages: get-tsconfig@4.13.7: resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} get-uri@8.0.0: resolution: {integrity: sha512-CqtZlMKvfJeY0Zxv8wazDwXmSKmnMnsmNy8j8+wudi8EyG/pMUB1NqHc+Tv1QaNtpYsK9nOYjb7r7Ufu32RPSw==} @@ -4150,9 +4167,6 @@ packages: getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - ghost-cursor@1.4.2: - resolution: {integrity: sha512-NPMuH05Ik9h99zrAb4fvAzhB4T6MqKwdPoI+Me0IJWN3676j4UoAsjKN/cJq5AG4zyZwAEPPggZXHVU0P57/5g==} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -4174,8 +4188,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@17.4.0: - resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} engines: {node: '>=18'} globrex@0.1.2: @@ -4204,6 +4218,10 @@ packages: resolution: {integrity: sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==} engines: {node: '>=20'} + got@15.0.5: + resolution: {integrity: sha512-PMIMaZuYUCK43+Z9JWEXea4kkX2b3301m81D5TS6QpfG4PmNyirzEdO/Oa2OHAN4GsjnPfvWCWsshKN2rq4/gQ==} + engines: {node: '>=22'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -4232,8 +4250,8 @@ packages: resolution: {integrity: sha512-4NjPB0+bAKjPoponSmTOkK58IEF2W22sOJA5O48k/MxbCZgOm+jrU4WVR53Z2I6xFgIPkVrQmKtt1LAbWtfqXw==} engines: {node: '>=16.0.0'} - headers-polyfill@4.0.3: - resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} heap@0.2.7: resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} @@ -4241,12 +4259,12 @@ packages: hmacsha1@1.0.0: resolution: {integrity: sha512-4FP6J0oI8jqb6gLLl9tSwVdosWJ/AKSGJ+HwYf6Ixe4MUcEkst4uWzpVQrNOCin0fzTRQbXV8ePheU8WiiDYBw==} - hono@4.12.12: - resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} + hono@4.12.18: + resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} engines: {node: '>=16.9.0'} - hookable@6.1.0: - resolution: {integrity: sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} @@ -4255,6 +4273,10 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-to-text@10.0.0: + resolution: {integrity: sha512-2OH59Gtprdczel+7Rxgpz9hGVJREaf8Lt1H4kZwWHpEn70VQKRuMNGsb2eDbwaTzrYzb0hheiOG1P7Dim0B4dQ==} + engines: {node: '>=20.19.0'} + html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -4271,20 +4293,16 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - http-cookie-agent@7.0.3: - resolution: {integrity: sha512-EeZo7CGhfqPW6R006rJa4QtZZUpBygDa2HZH3DJqsTzTjyRE6foDBVQIv/pjVsxHC8z2GIdbB1Hvn9SRorP3WQ==} - engines: {node: '>=20.0.0'} + http-cookie-agent@8.0.0: + resolution: {integrity: sha512-c5A2W4NXYRQR+ZOEA+nzLlWj0K7zXAG21UZaDKfZrFSlmmb1MMXBVDhQJNUNYkYJZwFi0ptlNu4r37tEM45tvg==} + engines: {node: '>=22.0.0'} peerDependencies: tough-cookie: ^4.0.0 || ^5.0.0 || ^6.0.0 - undici: ^7.0.0 + undici: ^7.0.0 || ^8.0.0 peerDependenciesMeta: undici: optional: true - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - http-proxy-agent@9.0.0: resolution: {integrity: sha512-FcF8VhXYLQcxWCnt/cCpT2apKsRDUGeVEeMqGu4HSTu29U8Yw0TLOjdYIlDsYk3IkUh+taX4IDWpPcCqKDhCjA==} engines: {node: '>= 20'} @@ -4342,8 +4360,8 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - imapflow@1.3.1: - resolution: {integrity: sha512-DKwpMDR1EWXpV5T7adqQAccN7n684AX3poEZ5F3YoPlm2MyGeKavpRgNr3qptdEQaK+x5SlZ9jigT+cMs4geBA==} + imapflow@1.3.3: + resolution: {integrity: sha512-lx7nWcUDfNgITEKYYfunUDqJ3LT6ImuiA1ReqJepVEA2nqBQNUqa3ppF7Yz5CNjuDYG95pmzsCcNqRjMrwh/Vg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -4352,13 +4370,13 @@ packages: import-in-the-middle@2.0.6: resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==} - import-in-the-middle@3.0.0: - resolution: {integrity: sha512-OnGy+eYT7wVejH2XWgLRgbmzujhhVIATQH0ztIeRilwHBjTeG3pD+XnH3PKX0r9gJ0BuJmJ68q/oh9qgXnNDQg==} + import-in-the-middle@3.0.1: + resolution: {integrity: sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==} engines: {node: '>=18'} - import-without-cache@0.2.5: - resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} - engines: {node: '>=20.19.0'} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -4390,8 +4408,8 @@ packages: resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} engines: {node: '>=12.22.0'} - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} engines: {node: '>= 12'} ip-regex@4.3.0: @@ -4402,9 +4420,6 @@ packages: resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} @@ -4502,10 +4517,6 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - istanbul-reports@3.2.0: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} @@ -4539,8 +4550,8 @@ packages: jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdom@29.0.2: - resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==} + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -4566,9 +4577,6 @@ packages: json-nd@1.0.0: resolution: {integrity: sha512-8TIp0HZAY0VVrwRQJJPb4+nOTSPoOWZeEKBTLizUfQO4oym5Fc/MKqN8vEbLCxcyxDf2vwNxOQ1q84O49GWPyQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -4584,8 +4592,8 @@ packages: json-with-bigint@3.5.8: resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} jsonpath-plus@10.4.0: resolution: {integrity: sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==} @@ -4600,8 +4608,8 @@ packages: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} engines: {'0': node >=0.6.0} - jsrsasign@11.1.1: - resolution: {integrity: sha512-6w95OOXH8DNeGxakqLndBEqqwQ6A70zGaky1oxfg8WVLWOnghTfJsc5Tknx+Z88MHSb1bGLcqQHImOF8Lk22XA==} + jsrsasign@11.1.3: + resolution: {integrity: sha512-nPnK5D/4lv0Dwr7TlzrKtAd8JlLZwFTqTUUB3NQCbtdobcRcohGFxjbPySDVh74iWUudcCsapYT6OxoyhJLhhA==} jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} @@ -4625,6 +4633,9 @@ packages: leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + leac@0.7.0: + resolution: {integrity: sha512-qMrZeyEekgdRQ9o6a4NAB2EQZrv827GJdn1vnapwSJ90hWRB4TzUSunvacPkxQ2TnNqHNI1/zSt0hlo0crG8Jw==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -4641,23 +4652,17 @@ packages: libqp@2.1.1: resolution: {integrity: sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==} - lighthouse-logger@2.0.2: - resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - lint-staged@16.4.0: - resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} - engines: {node: '>=20.17'} + lint-staged@17.0.2: + resolution: {integrity: sha512-Rbr6rdmbCn1fIDHBZpn0madg0hEkdlh+QwajnL3Qq0ZUq/icAJfLGj9BVBajAXi7657ZzKQ7kobGP9S5XOHYRw==} + engines: {node: '>=22.22.1'} hasBin: true - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -4673,8 +4678,8 @@ packages: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} @@ -4687,21 +4692,24 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lowercase-keys@4.0.1: + resolution: {integrity: sha512-wI9Nui/L8VfADa/cr/7NQruaASk1k23/Uh1khQ02BCVYiiy8F4AhOGnQzJy3Fl/c44GnYSbZHv8g7EcG3kJ1Qg==} + engines: {node: '>=20'} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.3.3: - resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -4740,12 +4748,39 @@ packages: markdown-table@2.0.0: resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} - marky@1.3.0: - resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} mdast-util-from-markdown@2.0.3: resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} @@ -4769,6 +4804,27 @@ packages: micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} @@ -4854,9 +4910,9 @@ 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.20260405.0: - resolution: {integrity: sha512-tpr4XdWMq7zFdsHH+CS0XS47nQzlRZH0rMJ1vobOZbkrs3cIj7qbD40ON616hDnzHxwqwB2qKHzmmuj6oRisSQ==} - engines: {node: '>=18.0.0'} + miniflare@4.20260507.1: + resolution: {integrity: sha512-PSXBiLExTdZ4UGO/raKCHQauUpYL7F880ZRB7j0+78Rv8h7TsdN2E/iEDK9sK2Y+SPQ5wJSeAa+rDeVKoZZoEw==} + engines: {node: '>=22.0.0'} hasBin: true minimatch@10.2.5: @@ -4878,9 +4934,6 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mixi2@0.2.2: resolution: {integrity: sha512-8rgPNsuKIMzLctZ8oeo9idLTahvJCamljcdVth1Tx/Gqp7d3vAWzKYUsBNCr+wqD5zf/0rrYJPZCo0WZqadcbQ==} @@ -4896,8 +4949,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.13.2: - resolution: {integrity: sha512-go2H1TIERKkC48pXiwec5l6sbNqYuvqOk3/vHGo1Zd+pq/H63oFawDQerH+WQdUw/flJFHDG7F+QdWMwhntA/A==} + msw@2.13.4: + resolution: {integrity: sha512-fPlKBeFe+8rpcyR3umUmmHuNwu6gc6T3STvkgEa9WDX/HEgal9wDeflpCUAIRtmvaLZM2igfI5y1bZ9G5J26KA==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -4910,19 +4963,25 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + nan@1.8.4: resolution: {integrity: sha512-609zQ1h3ApgH/94qmbbEklSrjcYYXCHnsWk4MAojq4OUk3tidhDYhPaMasMFKsZPZ96r4eQA1hbR2W4H7/77XA==} - nan@2.26.2: - resolution: {integrity: sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.7: - resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.11: + resolution: {integrity: sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==} engines: {node: ^18 || >=20} hasBin: true @@ -4974,8 +5033,8 @@ packages: resolution: {integrity: sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==} engines: {node: '>=0.12'} - node-network-devtools@1.0.29: - resolution: {integrity: sha512-Cz5aqlga55yZjJ1nAUYdjwh3njGUNJmUPrbCGBKuEQ+w/yQqXeGwRQCH3IBlzWuHC19Y1/kNlg6tzgqSubimRA==} + node-network-devtools@1.0.30: + resolution: {integrity: sha512-KgB5X7RklW1G3hE1pT+kRfG1yqvhF1h+feyYBMbFsA9wlqk4WhiWAJrNVcCAgTl2dKSCmA5FaYxYcp37aCh2/Q==} peerDependencies: undici: ^6 @@ -4986,6 +5045,10 @@ packages: resolution: {integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==} engines: {node: '>=6.0.0'} + nodemailer@8.0.7: + resolution: {integrity: sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow==} + engines: {node: '>=6.0.0'} + nopt@7.2.1: resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -5027,9 +5090,6 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - one-time@1.0.0: resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} @@ -5072,21 +5132,21 @@ packages: resolution: {integrity: sha512-dD4UpyBh/9m4X2NVjA+73/ZPBRF+uF4zIMFvvQsabMiEK8x41L3rQ8EENOi35kyyoaJwNxEeJcP6Fj1H4U409Q==} engines: {node: '>=12'} - oxfmt@0.44.0: - resolution: {integrity: sha512-lnncqvHewyRvaqdrnntVIrZV2tEddz8lbvPsQzG/zlkfvgZkwy0HP1p/2u1aCDToeg1jb9zBpbJdfkV73Itw+w==} + oxfmt@0.47.0: + resolution: {integrity: sha512-OFbkbzxKCpooQEnRmpTDnuwTX8KHXzZTQ4Df/hz85fpS67Pl+lxPEFvUtin56HIIS0B1k4X8oIzTXRZPufA2CA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint-plugin-eslint@1.59.0: - resolution: {integrity: sha512-8es8JMwVyo8d8/f/xqOW1Xosvr25w8+6lZA8yB9d/Ipn6zogu2XBtQ+lBic6m8UNj7F8ak9BzjbOrB/JVSM3Sg==} + oxlint-plugin-eslint@1.62.0: + resolution: {integrity: sha512-O3P73cvhqepcccIscD0PJ0/JaJG/WJYm9Z7R3JUS0jcQggUSGTWE0ybNKMrZfsTsW+16qZ6etHPXn1Ce6ZLELQ==} engines: {node: ^20.19.0 || >=22.12.0} - oxlint-tsgolint@0.20.0: - resolution: {integrity: sha512-/Uc9TQyN1l8w9QNvXtVHYtz+SzDJHKpb5X0UnHodl0BVzijUPk0LPlDOHAvogd1UI+iy9ZSF6gQxEqfzUxCULQ==} + oxlint-tsgolint@0.22.1: + resolution: {integrity: sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==} hasBin: true - oxlint@1.59.0: - resolution: {integrity: sha512-0xBLeGGjP4vD9pygRo8iuOkOzEU1MqOnfiOl7KYezL/QvWL8NUg6n03zXc7ZVqltiOpUxBk2zgHI3PnRIEdAvw==} + oxlint@1.62.0: + resolution: {integrity: sha512-1uFkg6HakjsGIpW9wNdeW4/2LOHW9MEkoWjZUTUfQtIHyLIZPYt00w3Sg+H3lH+206FgBPHBbW5dVE5l2ExECQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5111,18 +5171,10 @@ packages: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - pac-proxy-agent@9.0.1: resolution: {integrity: sha512-3ZOSpLboOlpW4yp8Cuv21KlTULRqyJ5Uuad3wXpSKFrxdNgcHEyoa22GRaZ2UlgCVuR6z+5BiavtYVvbajL/Yw==} engines: {node: '>= 20'} - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - pac-resolver@9.0.1: resolution: {integrity: sha512-lJbS008tmkj08VhoM8Hzuv/VE5tK9MS0OIQ/7+s0lIF+BYhiQWFYzkSpML7lXs9iBu2jfmzBTLzhe9n6BX+dYw==} engines: {node: '>= 20'} @@ -5135,14 +5187,14 @@ packages: pako@2.1.0: resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + pangu@4.0.7: + 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-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse-srcset@1.0.2: resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} @@ -5155,12 +5207,15 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} parseley@0.12.1: resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + parseley@0.13.1: + resolution: {integrity: sha512-uNBJZzmb60l6p6VWLTmevizNAGnE0xoSf1n0B4q3ntegDNzcS68NRCcBDZTcyXHxt2XhBChsCuqj4M+nChvE/A==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -5193,12 +5248,12 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + peberminta@0.10.0: + resolution: {integrity: sha512-80B2AsU+I4Qdb0ZAPSfe9UwvGzwkM37IKIFEvdS3D/3Ndgv2bsuJ0bfG1+iEYO+l7Gfd4EUJmuRyq7efLgRMzQ==} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -5230,12 +5285,26 @@ packages: resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} hasBin: true + playwright-core@1.59.1: + resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.59.1: + resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} + engines: {node: '>=18'} + hasBin: true + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -5265,34 +5334,20 @@ packages: process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + protobufjs@7.5.5: + resolution: {integrity: sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==} engines: {node: '>=12.0.0'} - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-chain@2.7.1: - resolution: {integrity: sha512-LtXu0miohJYrHWJxv8wA6EoGreRcX1hxKb7qlE1pMFH+BXE7bqMvpyhzR/JvR6M5SzYKzyHFpvfmYJrZeMtwAg==} - engines: {node: '>=14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + protobufjs@8.0.1: + resolution: {integrity: sha512-NWWCCscLjs+cOKF/s/XVNFRW7Yih0fdH+9brffR5NZCy8k42yRdl5KlWKMVXuI1vfCoy4o1z80XR/W/QUb3V3w==} + engines: {node: '>=12.0.0'} psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} @@ -5301,24 +5356,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-extra@3.3.6: - resolution: {integrity: sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A==} - engines: {node: '>=8'} - peerDependencies: - '@types/puppeteer': '*' - puppeteer: '*' - puppeteer-core: '*' - peerDependenciesMeta: - '@types/puppeteer': - optional: true - puppeteer: - optional: true - puppeteer-core: - optional: true - - puppeteer-real-browser@1.4.4: - resolution: {integrity: sha512-1CYGlL1Y0SdxP55byi9WQ8dtLkyIYBmRpGr+D+cB6uZDrW17+ZxWMnc7CDZfNuNuJaF15DWW5zvChS/ikymdmg==} - qs@6.14.2: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} @@ -5358,11 +5395,11 @@ packages: ramda@0.32.0: resolution: {integrity: sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==} - rate-limiter-flexible@11.0.0: - resolution: {integrity: sha512-UhN3xVeU6Az3y6hAuxMUwFsKcKD1HffhMGK0MknbSxH9vkwslS/p19ovCvJqIVT97pE778nKu2sUgYAcxj4dmQ==} + rate-limiter-flexible@11.1.0: + resolution: {integrity: sha512-lyyC0SqKz+dE5JoHZ4JMqdrM3LSZKBxzuAFAyKCYAnmHnPz/Rb6iDquxoL4CMipDXoR0G+QRhOzYWL3JKihbNw==} - re2js@2.0.1: - resolution: {integrity: sha512-X59sWAF3UaMKfsVZT5/kJk1kBK10artA4jxScW4dPYtIuyDAWITcTDgkgEf9GPaqCKMVPgL2oEJGib9jN4WnkQ==} + re2js@2.3.2: + resolution: {integrity: sha512-Y7oXKIPeei9mNLgi0kCbFrE3w+3sybHFh8UD3hE1EKU3/eY1ODjb4EBHUnZGL2TG3rnmqCex81Cevoy3wf8wEg==} readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -5375,19 +5412,6 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} - rebrowser-puppeteer-core@23.10.3: - resolution: {integrity: sha512-oWwuFg3XoZUkAt6Te4zTU6sQeS39I9tctjdSEiDPa76MF47R0IfLX8VQhyRwwzMySqD5L1wambYcWyEAN0b9AA==} - engines: {node: '>=18'} - - rebrowser-puppeteer-core@24.8.1: - resolution: {integrity: sha512-CifPf47KTG0jajTyn/dWpvf3kpW5W1QesURCWaUGzNTN8Kkki2IBUKJaMS+R+eNKhfv1JcsCZ5glYI0RAyJPTg==} - engines: {node: '>=18'} - - rebrowser-puppeteer@24.8.1: - resolution: {integrity: sha512-FdFiin2n/zzWf+RTeQ8D3AhoSqtK284Kro7rrDqcYTyD+9KXvPPx6VP6vl0cF+adGIr1A1aMyhgHHPoWFvAybg==} - engines: {node: '>=18'} - hasBin: true - redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -5407,9 +5431,22 @@ packages: resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-pangu@2.2.0: + resolution: {integrity: sha512-3h2UiRIbnEQR+IxYPhQ5SMbAZPobIV8ddza8U1hJLsfLranbv/cdFiwnZEgMOJYKmi+9XHLMxrAIipmIsLrzPg==} + engines: {node: '>=10'} + remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + repeat-string@1.6.1: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} @@ -5461,8 +5498,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -5474,8 +5511,8 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - rettime@0.10.1: - resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==} + rettime@0.11.7: + resolution: {integrity: sha512-DoAm1WjR1eH7z8sHPtvvUMIZh4/CSKkGCz6CxPqOrEAnOGtOuHSnSE9OC+razqxKuf4ub7pAYyl/vZV0vGs5tg==} rfc4648@1.5.4: resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} @@ -5483,14 +5520,14 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown-plugin-dts@0.23.2: - resolution: {integrity: sha512-PbSqLawLgZBGcOGT3yqWBGn4cX+wh2nt5FuBGdcMHyOhoukmjbhYAl8NT9sE4U38Cm9tqLOIQeOrvzeayM0DLQ==} - engines: {node: '>=20.19.0'} + rolldown-plugin-dts@0.25.0: + resolution: {integrity: sha512-GE3uDZgUuA9l6g+1u928TRmadd5IVhaWiwpWast2kCyLv9tYJJCC6E5HHkV0HGmwC5ZL73xh12/PRZI+KZ2vdQ==} + engines: {node: ^22.18.0 || >=24.0.0} peerDependencies: '@ts-macro/tsc': ^0.3.6 '@typescript/native-preview': '>=7.0.0-dev.20260325.1' - rolldown: ^1.0.0-rc.12 - typescript: ^5.0.0 || ^6.0.0 + rolldown: ^1.0.0 + typescript: ^6.0.0 vue-tsc: ~3.2.0 peerDependenciesMeta: '@ts-macro/tsc': @@ -5502,13 +5539,18 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-rc.12: - resolution: {integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==} + rolldown@1.0.0: + resolution: {integrity: sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.0.0-rc.17: + resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.1: - resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} + rollup@4.60.3: + resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5523,8 +5565,8 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - sanitize-html@2.17.2: - resolution: {integrity: sha512-EnffJUl46VE9uvZ0XeWzObHLurClLlT12gsOk1cHyP2Ol1P0BnBnsXmShlBmWVJM+dKieQI68R0tsPY5m/B+Jg==} + sanitize-html@2.17.3: + resolution: {integrity: sha512-Kn4srCAo2+wZyvCNKCSyB2g8RQ8IkX/gQs2uqoSRNu5t9I2qvUyAVvRDiFUVAiX3N3PNuwStY0eNr+ooBHVWEg==} sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} @@ -5537,11 +5579,17 @@ packages: selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + 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 + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -5564,10 +5612,6 @@ packages: simplecc-wasm@1.1.1: resolution: {integrity: sha512-NHBvSBlnOk7nqiNKvNpG/3DqD4XBkls3CqmTl4t3At4v7N9z1HO2t9g7vK//0o33zcgVOKE59vFcyCcgCuw6zQ==} - sleep@6.1.0: - resolution: {integrity: sha512-Z1x4JjJxsru75Tqn8F4tnOFeEu3HjtITTsumYUiuz54sGKdISgLCek9AUlXlVVrkhltRFhNUsJDJE76SFHTDIQ==} - engines: {node: '>=0.8.0'} - slice-ansi@7.1.2: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} @@ -5599,6 +5643,10 @@ packages: resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.8: + resolution: {integrity: sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -5648,8 +5696,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} stealthy-require@1.1.1: resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} @@ -5661,9 +5709,6 @@ packages: stream-length@1.0.2: resolution: {integrity: sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==} - streamx@2.25.0: - resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} - strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -5686,8 +5731,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} string_decoder@1.3.0: @@ -5743,29 +5788,17 @@ packages: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} - tapable@2.3.2: - resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - tar-fs@3.1.2: - resolution: {integrity: sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==} - - tar-stream@3.1.8: - resolution: {integrity: sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==} - tar@7.5.13: resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} engines: {node: '>=18'} - teex@1.0.1: - resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} - telegram@2.26.22: resolution: {integrity: sha512-EIj7Yrjiu0Yosa3FZ/7EyPg9s6UiTi/zDQrFmR/2Mg7pIUU+XjAit1n1u9OU9h2oRnRM5M+67/fxzQluZpaJJg==} - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -5776,9 +5809,6 @@ packages: resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} engines: {node: '>=20'} - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - timers-ext@0.1.8: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} @@ -5786,16 +5816,13 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} @@ -5817,11 +5844,11 @@ packages: resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} hasBin: true - tldts-core@7.0.28: - resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + tldts-core@7.0.30: + resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==} - tldts@7.0.28: - resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + tldts@7.0.30: + resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==} hasBin: true to-no-case@1.0.2: @@ -5899,18 +5926,20 @@ packages: typescript: optional: true - tsdown@0.21.7: - resolution: {integrity: sha512-ukKIxKQzngkWvOYJAyptudclkm4VQqbjq+9HF5K5qDO8GJsYtMh8gIRwicbnZEnvFPr6mquFwYAVZ8JKt3rY2g==} - engines: {node: '>=20.19.0'} + tsdown@0.22.0: + resolution: {integrity: sha512-FgW0hHb27nGQA/+F3d5+U9wKXkfilk9DVkc5+7x/ZqF03g+Hoz/eeApT32jqxATt9eRoR+1jxk7MUMON+O4CXw==} + engines: {node: ^22.18.0 || >=24.0.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.21.7 - '@tsdown/exe': 0.21.7 + '@tsdown/css': 0.22.0 + '@tsdown/exe': 0.22.0 '@vitejs/devtools': '*' - publint: ^0.3.0 + publint: ^0.3.8 + tsx: '*' typescript: ^5.0.0 || ^6.0.0 unplugin-unused: ^0.5.0 + unrun: '*' peerDependenciesMeta: '@arethetypeswrong/core': optional: true @@ -5922,10 +5951,14 @@ packages: optional: true publint: optional: true + tsx: + optional: true typescript: optional: true unplugin-unused: optional: true + unrun: + optional: true tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -5945,8 +5978,9 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turndown@7.2.2: - resolution: {integrity: sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==} + turndown@7.2.4: + resolution: {integrity: sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==} + engines: {node: '>=18', npm: '>=9'} tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} @@ -5974,12 +6008,13 @@ packages: resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} engines: {node: '>=20'} + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} - typed-query-selector@2.12.1: - resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==} - typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} @@ -5991,34 +6026,35 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} ultron@1.0.2: resolution: {integrity: sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow==} - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.24.1: - resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} + undici@6.25.0: + resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} - undici@7.24.4: - resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} + undici@7.24.8: + resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} engines: {node: '>=20.18.1'} - undici@7.24.7: - resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==} + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -6027,9 +6063,27 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unist-util-is@4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-parents@3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@2.0.3: + resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} @@ -6044,8 +6098,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - unrun@0.2.34: - resolution: {integrity: sha512-LyaghRBR++r7svhDK6tnDz2XaYHWdneBOA0jbS8wnRsHerI9MFljX4fIiTgbbNbEVzZ0C9P1OjWLLe1OqoaaEw==} + unrun@0.2.37: + resolution: {integrity: sha512-AA7vDuYsgeSYVzJMm16UKA+aXFKhy7nFqW9z5l7q44K4ppFWZAMqYS58ePRZbugMLPH0fwwMzD5A8nP0avxwZQ==} engines: {node: '>=20.19.0'} hasBin: true peerDependencies: @@ -6081,9 +6135,6 @@ packages: url-template@2.0.8: resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} - urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} - utf-8-validate@1.1.0: resolution: {integrity: sha512-Qsgu1u2akdyOneurUEVf/tXhkqBQMoE3x0GY5+P7ayPHvVzTqOkkgBhtl26wm6ap10Amhwy0jIhPwMGywx76QQ==} @@ -6098,17 +6149,18 @@ packages: resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} engines: {node: '>= 4'} - uuid@13.0.0: - resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true vali-date@1.0.0: @@ -6170,24 +6222,27 @@ packages: yaml: optional: true - vitest@4.0.9: - resolution: {integrity: sha512-E0Ja2AX4th+CG33yAFRC+d1wFx2pzU5r6HtG6LiPSE04flaE0qB6YyjSw9ZcpJAtVPfsvZGtJlKWZpuW7EHRxg==} + vitest@4.1.5: + resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 + '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.9 - '@vitest/browser-preview': 4.0.9 - '@vitest/browser-webdriverio': 4.0.9 - '@vitest/ui': 4.0.9 + '@vitest/browser-playwright': 4.1.5 + '@vitest/browser-preview': 4.1.5 + '@vitest/browser-webdriverio': 4.1.5 + '@vitest/coverage-istanbul': 4.1.5 + '@vitest/coverage-v8': 4.1.5 + '@vitest/ui': 4.1.5 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true @@ -6197,6 +6252,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -6265,21 +6324,25 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260405.1: - resolution: {integrity: sha512-bSaRWCv9iO8/FWpgZRjHLGZLolX5s1AErRSYaTECMMHOZKuCbl2+ehnSyc+ZZ/70y+9owADmN6HoYEWvBlJdYw==} + workerd@1.20260507.1: + resolution: {integrity: sha512-z7JhsFSe6+X1b5fUHaVpo15VM1IRMJiLofEkq8iKdCo+Veqc+FUg5lIsuz8NwePxuSKrXtO4ZQpGkQLbPVXFhg==} engines: {node: '>=16'} hasBin: true - wrangler@4.81.0: - resolution: {integrity: sha512-9fLPDuDcb8Nu6iXrl5E3HGYt3TVhQr/UvqtTvWr9Nl1X7PlQrmWMwQCfSioqN8VHYyQCyESV5jQsoKg8Sx+sEA==} - engines: {node: '>=20.3.0'} + wrangler@4.90.0: + resolution: {integrity: sha512-bmNIykl59TfCUn5xQgU7IWylSsPx3LQaPLMSAq2VQHt89CBrcj9qXQ0eYfjBCWA5XTBVgten391evt7xxtXwcA==} + engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260405.1 + '@cloudflare/workers-types': ^4.20260507.1 peerDependenciesMeta: '@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'} @@ -6296,9 +6359,6 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@1.3.4: resolution: {integrity: sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==} @@ -6351,9 +6411,6 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - xvfb@0.4.0: - resolution: {integrity: sha512-g55AbjcBL4Bztfn7kiUrR0ne8mMUsFODDJ+HFGf5OuHJqKKccpExX2Qgn7VF2eImw1eoh6+riXHser1J4agrFA==} - xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} @@ -6379,6 +6436,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.8.4: + resolution: {integrity: sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -6391,9 +6453,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -6419,27 +6478,24 @@ packages: youtubei.js@17.0.1: resolution: {integrity: sha512-1lO4b8UqMDzE0oh2qEGzbBOd4UYRdxn/4PdpRM7BGTHxM6ddsEsKZTu90jp8V9FHVgC2h1UirQyqoqLiKwl+Zg==} - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - '@actions/core@3.0.0': + '@actions/core@3.0.1': dependencies: '@actions/exec': 3.0.0 - '@actions/http-client': 4.0.0 + '@actions/http-client': 4.0.1 '@actions/exec@3.0.0': dependencies: '@actions/io': 3.0.2 - '@actions/github@9.1.0': + '@actions/github@9.1.1': dependencies: '@actions/http-client': 3.0.2 '@octokit/core': 7.0.6 @@ -6447,40 +6503,44 @@ 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.24.1 + undici: 6.25.0 '@actions/http-client@3.0.2': dependencies: tunnel: 0.0.6 - undici: 6.24.1 + undici: 6.25.0 - '@actions/http-client@4.0.0': + '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 - undici: 6.24.1 + undici: 6.25.0 '@actions/io@3.0.2': {} - '@asamuzakjp/css-color@5.1.6': + '@asamuzakjp/css-color@5.1.11': dependencies: - '@csstools/css-calc': 3.1.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.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.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.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 - '@asamuzakjp/dom-selector@7.0.7': + '@asamuzakjp/dom-selector@7.1.1': 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': {} - '@asteasolutions/zod-to-openapi@8.5.0(zod@4.3.6)': + '@asteasolutions/zod-to-openapi@8.5.0(zod@4.4.3)': dependencies: openapi3-ts: 4.5.0 - zod: 4.3.6 + zod: 4.4.3 '@babel/code-frame@7.29.0': dependencies: @@ -6488,10 +6548,10 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/generator@8.0.0-rc.3': + '@babel/generator@8.0.0-rc.4': dependencies: - '@babel/parser': 8.0.0-rc.3 - '@babel/types': 8.0.0-rc.3 + '@babel/parser': 8.0.0-rc.4 + '@babel/types': 8.0.0-rc.4 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 '@types/jsesc': 2.5.1 @@ -6499,19 +6559,19 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-string-parser@8.0.0-rc.3': {} + '@babel/helper-string-parser@8.0.0-rc.4': {} '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-identifier@8.0.0-rc.3': {} + '@babel/helper-validator-identifier@8.0.0-rc.4': {} '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 - '@babel/parser@8.0.0-rc.3': + '@babel/parser@8.0.0-rc.4': dependencies: - '@babel/types': 8.0.0-rc.3 + '@babel/types': 8.0.0-rc.4 '@babel/runtime-corejs2@7.29.2': dependencies: @@ -6522,10 +6582,10 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/types@8.0.0-rc.3': + '@babel/types@8.0.0-rc.4': dependencies: - '@babel/helper-string-parser': 8.0.0-rc.3 - '@babel/helper-validator-identifier': 8.0.0-rc.3 + '@babel/helper-string-parser': 8.0.0-rc.4 + '@babel/helper-validator-identifier': 8.0.0-rc.4 '@bbob/core@4.3.1': dependencies: @@ -6569,46 +6629,34 @@ snapshots: '@bufbuild/protobuf@2.11.0': {} - '@cloudflare/containers@0.2.4': {} + '@cloudflare/containers@0.3.3': {} - '@cloudflare/kv-asset-handler@0.4.2': {} + '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/puppeteer@1.0.6(bufferutil@4.1.0)(utf-8-validate@5.0.10)': - dependencies: - '@puppeteer/browsers': 2.2.4 - debug: 4.4.3 - devtools-protocol: 0.0.1299070 - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - utf-8-validate + '@cloudflare/playwright@1.3.0': {} - '@cloudflare/unenv-preset@2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260405.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260507.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260405.1 + workerd: 1.20260507.1 - '@cloudflare/workerd-darwin-64@1.20260405.1': + '@cloudflare/workerd-darwin-64@1.20260507.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260405.1': + '@cloudflare/workerd-darwin-arm64@1.20260507.1': optional: true - '@cloudflare/workerd-linux-64@1.20260405.1': + '@cloudflare/workerd-linux-64@1.20260507.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260405.1': + '@cloudflare/workerd-linux-arm64@1.20260507.1': optional: true - '@cloudflare/workerd-windows-64@1.20260405.1': + '@cloudflare/workerd-windows-64@1.20260507.1': optional: true - '@cloudflare/workers-types@4.20260409.1': {} + '@cloudflare/workers-types@4.20260508.1': {} '@colors/colors@1.6.0': {} @@ -6620,15 +6668,15 @@ snapshots: '@csstools/color-helpers@6.0.2': {} - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.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.0.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.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.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 @@ -6636,7 +6684,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -6656,23 +6704,18 @@ snapshots: '@edge-runtime/primitives': 4.1.0 optional: true - '@emnapi/core@1.9.1': - dependencies: - '@emnapi/wasi-threads': 1.2.0 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.9.1': + '@emnapi/core@1.10.0': dependencies: + '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.2': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.0': + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true @@ -6913,30 +6956,26 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.2.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.6.1))': dependencies: - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.23.4': + '@eslint/config-array@0.23.5': dependencies: - '@eslint/object-schema': 3.0.4 + '@eslint/object-schema': 3.0.5 debug: 4.4.3 minimatch: 10.2.5 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.5.4': - dependencies: - '@eslint/core': 1.2.0 - - '@eslint/core@1.1.1': + '@eslint/config-helpers@0.5.5': dependencies: - '@types/json-schema': 7.0.15 + '@eslint/core': 1.2.1 - '@eslint/core@1.2.0': + '@eslint/core@1.2.1': dependencies: '@types/json-schema': 7.0.15 @@ -6954,20 +6993,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.2.0(jiti@2.6.1))': + '@eslint/js@10.0.1(eslint@10.3.0(jiti@2.6.1))': optionalDependencies: - eslint: 10.2.0(jiti@2.6.1) - - '@eslint/object-schema@3.0.4': {} + eslint: 10.3.0(jiti@2.6.1) - '@eslint/plugin-kit@0.6.1': - dependencies: - '@eslint/core': 1.1.1 - levn: 0.4.1 + '@eslint/object-schema@3.0.5': {} - '@eslint/plugin-kit@0.7.0': + '@eslint/plugin-kit@0.7.1': dependencies: - '@eslint/core': 1.2.0 + '@eslint/core': 1.2.1 levn: 0.4.1 '@exodus/bytes@1.15.0(@noble/hashes@2.0.1)': @@ -6977,48 +7011,53 @@ snapshots: '@fastify/otel@0.18.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.212.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 minimatch: 10.2.5 transitivePeerDependencies: - supports-color - '@honeybadger-io/core@6.7.2': + '@honeybadger-io/core@6.9.0': dependencies: json-nd: 1.0.0 stacktrace-parser: 0.1.11 - '@honeybadger-io/js@6.12.3': + '@honeybadger-io/js@6.14.0': dependencies: - '@honeybadger-io/core': 6.7.2 + '@honeybadger-io/core': 6.9.0 '@types/aws-lambda': 8.10.161 '@types/express': 5.0.6 - '@hono/node-server@1.19.13(hono@4.12.12)': + '@hono/node-server@2.0.1(hono@4.12.18)': dependencies: - hono: 4.12.12 + hono: 4.12.18 - '@hono/zod-openapi@1.2.4(hono@4.12.12)(zod@4.3.6)': + '@hono/zod-openapi@1.3.0(hono@4.12.18)(zod@4.4.3)': dependencies: - '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.3.6) - '@hono/zod-validator': 0.7.6(hono@4.12.12)(zod@4.3.6) - hono: 4.12.12 + '@asteasolutions/zod-to-openapi': 8.5.0(zod@4.4.3) + '@hono/zod-validator': 0.7.6(hono@4.12.18)(zod@4.4.3) + hono: 4.12.18 openapi3-ts: 4.5.0 - zod: 4.3.6 + zod: 4.4.3 - '@hono/zod-validator@0.7.6(hono@4.12.12)(zod@4.3.6)': + '@hono/zod-validator@0.7.6(hono@4.12.18)(zod@4.4.3)': dependencies: - hono: 4.12.12 - zod: 4.3.6 + hono: 4.12.18 + zod: 4.4.3 - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -7107,7 +7146,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.9.2 + '@emnapi/runtime': 1.10.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -7121,51 +7160,78 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.5.2)': + '@inquirer/ansi@2.0.5': {} + + '@inquirer/checkbox@4.3.2(@types/node@25.6.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 - '@inquirer/confirm@5.1.21(@types/node@25.5.2)': + '@inquirer/confirm@5.1.21(@types/node@25.6.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.2) + '@inquirer/type': 3.0.10(@types/node@25.6.2) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 - '@inquirer/core@10.3.2(@types/node@25.5.2)': + '@inquirer/confirm@6.0.11(@types/node@25.6.2)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@25.6.2) + '@inquirer/type': 4.0.5(@types/node@25.6.2) + optionalDependencies: + '@types/node': 25.6.2 + + '@inquirer/core@10.3.2(@types/node@25.6.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.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': 25.5.2 + '@types/node': 25.6.2 + + '@inquirer/core@11.1.8(@types/node@25.6.2)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@25.6.2) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 25.6.2 '@inquirer/figures@1.0.15': {} - '@inquirer/select@4.4.2(@types/node@25.5.2)': + '@inquirer/figures@2.0.5': {} + + '@inquirer/select@4.4.2(@types/node@25.6.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 + + '@inquirer/type@3.0.10(@types/node@25.6.2)': + optionalDependencies: + '@types/node': 25.6.2 - '@inquirer/type@3.0.10(@types/node@25.5.2)': + '@inquirer/type@4.0.5(@types/node@25.6.2)': optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@ioredis/commands@1.5.1': {} @@ -7182,17 +7248,16 @@ snapshots: dependencies: minipass: 7.1.3 - '@jocmp/mercury-parser@3.0.7': + '@jocmp/mercury-parser@3.0.8': dependencies: '@babel/runtime-corejs2': 7.29.2 cheerio: 1.2.0 dayjs: 1.11.20 difflib: https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed - ellipsize: 0.6.0 iconv-lite: 0.7.2 postman-request: 2.88.1-postman.48 string-direction: 0.1.2 - turndown: 7.2.2 + turndown: 7.2.4 wuzzy: 0.1.8 yargs-parser: 22.0.0 transitivePeerDependencies: @@ -7255,16 +7320,16 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.9.1 - '@emnapi/runtime': 1.9.1 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)': + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@emnapi/core': 1.9.1 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.1 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 optional: true '@noble/hashes@2.0.1': {} @@ -7282,7 +7347,7 @@ snapshots: '@nolyfill/side-channel@1.0.44': {} - '@notionhq/client@5.17.0': {} + '@notionhq/client@5.20.0': {} '@octokit/auth-token@6.0.0': {} @@ -7340,6 +7405,8 @@ snapshots: '@open-draft/deferred-promise@2.2.0': {} + '@open-draft/deferred-promise@3.0.0': {} + '@open-draft/logger@0.3.0': dependencies: is-node-process: 1.2.0 @@ -7359,38 +7426,43 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs@0.217.0': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api@1.9.1': {} - '@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/exporter-prometheus@0.214.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-prometheus@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-trace-otlp-http@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-amqplib@0.61.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -7399,7 +7471,7 @@ snapshots: '@opentelemetry/instrumentation-connect@0.57.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 @@ -7413,19 +7485,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.62.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation-fs@0.33.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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) transitivePeerDependencies: - supports-color @@ -7447,7 +7510,7 @@ snapshots: '@opentelemetry/instrumentation-hapi@0.60.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -7463,15 +7526,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.62.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation-kafkajs@0.23.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -7491,7 +7545,7 @@ snapshots: '@opentelemetry/instrumentation-koa@0.62.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -7515,7 +7569,7 @@ snapshots: '@opentelemetry/instrumentation-mongoose@0.60.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 transitivePeerDependencies: @@ -7542,7 +7596,7 @@ snapshots: '@opentelemetry/instrumentation-pg@0.66.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@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/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.1) @@ -7551,15 +7605,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis@0.62.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation-tedious@0.33.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -7569,15 +7614,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.24.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.40.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -7600,55 +7636,53 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.214.0 - import-in-the-middle: 3.0.0 + import-in-the-middle: 3.0.1 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.214.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-exporter-base@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.217.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer@0.214.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-transformer@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.214.0 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.1) - protobufjs: 7.5.4 - - '@opentelemetry/redis-common@0.38.2': {} + '@opentelemetry/api-logs': 0.217.0 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) + protobufjs: 8.0.1 - '@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/sdk-logs@0.214.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-logs@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.214.0 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.217.0 + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/sdk-metrics@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-metrics@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/semantic-conventions@1.40.0': {} @@ -7656,7 +7690,7 @@ snapshots: '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@ota-meshi/ast-token-store@0.3.0': {} @@ -7687,141 +7721,144 @@ snapshots: dependencies: '@otplib/core': 13.4.0 - '@oxc-project/types@0.122.0': {} + '@oxc-project/types@0.127.0': + optional: true + + '@oxc-project/types@0.129.0': {} - '@oxfmt/binding-android-arm-eabi@0.44.0': + '@oxfmt/binding-android-arm-eabi@0.47.0': optional: true - '@oxfmt/binding-android-arm64@0.44.0': + '@oxfmt/binding-android-arm64@0.47.0': optional: true - '@oxfmt/binding-darwin-arm64@0.44.0': + '@oxfmt/binding-darwin-arm64@0.47.0': optional: true - '@oxfmt/binding-darwin-x64@0.44.0': + '@oxfmt/binding-darwin-x64@0.47.0': optional: true - '@oxfmt/binding-freebsd-x64@0.44.0': + '@oxfmt/binding-freebsd-x64@0.47.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.44.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.47.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.44.0': + '@oxfmt/binding-linux-arm-musleabihf@0.47.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.44.0': + '@oxfmt/binding-linux-arm64-gnu@0.47.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.44.0': + '@oxfmt/binding-linux-arm64-musl@0.47.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.44.0': + '@oxfmt/binding-linux-ppc64-gnu@0.47.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.44.0': + '@oxfmt/binding-linux-riscv64-gnu@0.47.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.44.0': + '@oxfmt/binding-linux-riscv64-musl@0.47.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.44.0': + '@oxfmt/binding-linux-s390x-gnu@0.47.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.44.0': + '@oxfmt/binding-linux-x64-gnu@0.47.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.44.0': + '@oxfmt/binding-linux-x64-musl@0.47.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.44.0': + '@oxfmt/binding-openharmony-arm64@0.47.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.44.0': + '@oxfmt/binding-win32-arm64-msvc@0.47.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.44.0': + '@oxfmt/binding-win32-ia32-msvc@0.47.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.44.0': + '@oxfmt/binding-win32-x64-msvc@0.47.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.20.0': + '@oxlint-tsgolint/darwin-arm64@0.22.1': optional: true - '@oxlint-tsgolint/darwin-x64@0.20.0': + '@oxlint-tsgolint/darwin-x64@0.22.1': optional: true - '@oxlint-tsgolint/linux-arm64@0.20.0': + '@oxlint-tsgolint/linux-arm64@0.22.1': optional: true - '@oxlint-tsgolint/linux-x64@0.20.0': + '@oxlint-tsgolint/linux-x64@0.22.1': optional: true - '@oxlint-tsgolint/win32-arm64@0.20.0': + '@oxlint-tsgolint/win32-arm64@0.22.1': optional: true - '@oxlint-tsgolint/win32-x64@0.20.0': + '@oxlint-tsgolint/win32-x64@0.22.1': optional: true - '@oxlint/binding-android-arm-eabi@1.59.0': + '@oxlint/binding-android-arm-eabi@1.62.0': optional: true - '@oxlint/binding-android-arm64@1.59.0': + '@oxlint/binding-android-arm64@1.62.0': optional: true - '@oxlint/binding-darwin-arm64@1.59.0': + '@oxlint/binding-darwin-arm64@1.62.0': optional: true - '@oxlint/binding-darwin-x64@1.59.0': + '@oxlint/binding-darwin-x64@1.62.0': optional: true - '@oxlint/binding-freebsd-x64@1.59.0': + '@oxlint/binding-freebsd-x64@1.62.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.59.0': + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.59.0': + '@oxlint/binding-linux-arm-musleabihf@1.62.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.59.0': + '@oxlint/binding-linux-arm64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.59.0': + '@oxlint/binding-linux-arm64-musl@1.62.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.59.0': + '@oxlint/binding-linux-ppc64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.59.0': + '@oxlint/binding-linux-riscv64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.59.0': + '@oxlint/binding-linux-riscv64-musl@1.62.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.59.0': + '@oxlint/binding-linux-s390x-gnu@1.62.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.59.0': + '@oxlint/binding-linux-x64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-x64-musl@1.59.0': + '@oxlint/binding-linux-x64-musl@1.62.0': optional: true - '@oxlint/binding-openharmony-arm64@1.59.0': + '@oxlint/binding-openharmony-arm64@1.62.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.59.0': + '@oxlint/binding-win32-arm64-msvc@1.62.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.59.0': + '@oxlint/binding-win32-ia32-msvc@1.62.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.59.0': + '@oxlint/binding-win32-x64-msvc@1.62.0': optional: true - '@oxlint/plugins@1.59.0': {} + '@oxlint/plugins@1.62.0': {} '@package-json/types@0.0.12': {} @@ -7875,6 +7912,8 @@ snapshots: '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} + '@protobufjs/eventemitter@1.1.0': {} '@protobufjs/fetch@1.1.0': @@ -7886,196 +7925,204 @@ snapshots: '@protobufjs/inquire@1.1.0': {} + '@protobufjs/inquire@1.1.1': {} + '@protobufjs/path@1.1.2': {} '@protobufjs/pool@1.1.0': {} '@protobufjs/utf8@1.1.0': {} - '@puppeteer/browsers@2.10.3': - dependencies: - debug: 4.4.3 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.5.0 - semver: 7.7.4 - tar-fs: 3.1.2 - yargs: 17.7.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - - '@puppeteer/browsers@2.2.4': - dependencies: - debug: 4.4.3 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.5.0 - semver: 7.7.4 - tar-fs: 3.1.2 - unbzip2-stream: 1.4.3 - yargs: 17.7.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color - - '@puppeteer/browsers@2.6.1': - dependencies: - debug: 4.4.3 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.5.0 - semver: 7.7.4 - tar-fs: 3.1.2 - unbzip2-stream: 1.4.3 - yargs: 17.7.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color + '@protobufjs/utf8@1.1.1': {} '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 - '@rolldown/binding-android-arm64@1.0.0-rc.12': + '@rolldown/binding-android-arm64@1.0.0': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.12': + '@rolldown/binding-android-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.12': + '@rolldown/binding-darwin-arm64@1.0.0': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.12': + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': + '@rolldown/binding-darwin-x64@1.0.0': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': + '@rolldown/binding-darwin-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': + '@rolldown/binding-freebsd-x64@1.0.0': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': + '@rolldown/binding-linux-arm64-gnu@1.0.0': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)': - dependencies: - '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@rolldown/binding-linux-arm64-musl@1.0.0': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': + '@rolldown/binding-linux-ppc64-gnu@1.0.0': optional: true - '@rolldown/pluginutils@1.0.0-rc.12': {} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + optional: true - '@rollup/pluginutils@5.3.0(rollup@4.60.1)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.4 - optionalDependencies: - rollup: 4.60.1 + '@rolldown/binding-linux-s390x-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@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.0-rc.17': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + optional: true + + '@rolldown/pluginutils@1.0.0': {} + + '@rolldown/pluginutils@1.0.0-rc.17': + optional: true - '@rollup/rollup-android-arm-eabi@4.60.1': + '@rollup/pluginutils@5.3.0(rollup@4.60.3)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.3 + + '@rollup/rollup-android-arm-eabi@4.60.3': optional: true - '@rollup/rollup-android-arm64@4.60.1': + '@rollup/rollup-android-arm64@4.60.3': optional: true - '@rollup/rollup-darwin-arm64@4.60.1': + '@rollup/rollup-darwin-arm64@4.60.3': optional: true - '@rollup/rollup-darwin-x64@4.60.1': + '@rollup/rollup-darwin-x64@4.60.3': optional: true - '@rollup/rollup-freebsd-arm64@4.60.1': + '@rollup/rollup-freebsd-arm64@4.60.3': optional: true - '@rollup/rollup-freebsd-x64@4.60.1': + '@rollup/rollup-freebsd-x64@4.60.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.1': + '@rollup/rollup-linux-arm-musleabihf@4.60.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.1': + '@rollup/rollup-linux-arm64-gnu@4.60.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.1': + '@rollup/rollup-linux-arm64-musl@4.60.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.1': + '@rollup/rollup-linux-loong64-gnu@4.60.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.1': + '@rollup/rollup-linux-loong64-musl@4.60.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.1': + '@rollup/rollup-linux-ppc64-gnu@4.60.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.1': + '@rollup/rollup-linux-ppc64-musl@4.60.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.1': + '@rollup/rollup-linux-riscv64-gnu@4.60.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.1': + '@rollup/rollup-linux-riscv64-musl@4.60.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.1': + '@rollup/rollup-linux-s390x-gnu@4.60.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.1': + '@rollup/rollup-linux-x64-gnu@4.60.3': optional: true - '@rollup/rollup-linux-x64-musl@4.60.1': + '@rollup/rollup-linux-x64-musl@4.60.3': optional: true - '@rollup/rollup-openbsd-x64@4.60.1': + '@rollup/rollup-openbsd-x64@4.60.3': optional: true - '@rollup/rollup-openharmony-arm64@4.60.1': + '@rollup/rollup-openharmony-arm64@4.60.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.1': + '@rollup/rollup-win32-arm64-msvc@4.60.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.1': + '@rollup/rollup-win32-ia32-msvc@4.60.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.1': + '@rollup/rollup-win32-x64-gnu@4.60.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.1': + '@rollup/rollup-win32-x64-msvc@4.60.3': optional: true '@rss3/api-core@0.0.25': @@ -8093,23 +8140,23 @@ snapshots: '@rss3/api-core': 0.0.25 '@rss3/api-utils': 0.0.25 - '@scalar/core@0.4.6': + '@scalar/client-side-rendering@0.1.7': dependencies: - '@scalar/types': 0.7.6 + '@scalar/types': 0.9.6 - '@scalar/helpers@0.4.3': {} + '@scalar/helpers@0.6.0': {} - '@scalar/hono-api-reference@0.10.6(hono@4.12.12)': + '@scalar/hono-api-reference@0.10.14(hono@4.12.18)': dependencies: - '@scalar/core': 0.4.6 - hono: 4.12.12 + '@scalar/client-side-rendering': 0.1.7 + hono: 4.12.18 - '@scalar/types@0.7.6': + '@scalar/types@0.9.6': dependencies: - '@scalar/helpers': 0.4.3 - nanoid: 5.1.7 - type-fest: 5.5.0 - zod: 4.3.6 + '@scalar/helpers': 0.6.0 + nanoid: 5.1.11 + type-fest: 5.6.0 + zod: 4.4.3 '@scure/base@2.0.0': {} @@ -8120,40 +8167,41 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sentry/core@10.47.0': {} + '@selderee/plugin-htmlparser2@0.12.0(selderee@0.12.0)': + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + selderee: 0.12.0 + + '@sentry/core@10.52.0': {} - '@sentry/node-core@10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/node-core@10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.217.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.40.0)': dependencies: - '@sentry/core': 10.47.0 - '@sentry/opentelemetry': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) - import-in-the-middle: 3.0.0 + '@sentry/core': 10.52.0 + '@sentry/opentelemetry': 10.52.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.40.0) + import-in-the-middle: 3.0.1 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-trace-otlp-http': 0.214.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 - '@sentry/node@10.47.0(@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.52.0(@opentelemetry/exporter-trace-otlp-http@0.217.0(@opentelemetry/api@1.9.1))': dependencies: '@fastify/otel': 0.18.0(@opentelemetry/api@1.9.1) '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.6.1(@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/instrumentation-amqplib': 0.61.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-connect': 0.57.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-dataloader': 0.31.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-express': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-fs': 0.33.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-generic-pool': 0.57.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-graphql': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-hapi': 0.60.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-http': 0.214.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-ioredis': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-kafkajs': 0.23.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-knex': 0.58.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-koa': 0.62.0(@opentelemetry/api@1.9.1) @@ -8163,34 +8211,32 @@ snapshots: '@opentelemetry/instrumentation-mysql': 0.60.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-mysql2': 0.60.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-pg': 0.66.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-redis': 0.62.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-tedious': 0.33.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-undici': 0.24.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 '@prisma/instrumentation': 7.6.0(@opentelemetry/api@1.9.1) - '@sentry/core': 10.47.0 - '@sentry/node-core': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.214.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) - '@sentry/opentelemetry': 10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) - import-in-the-middle: 3.0.0 + '@sentry/core': 10.52.0 + '@sentry/node-core': 10.52.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.217.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.40.0) + '@sentry/opentelemetry': 10.52.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.40.0) + import-in-the-middle: 3.0.1 transitivePeerDependencies: - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.47.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/opentelemetry@10.52.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.40.0)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.6.1(@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.40.0 - '@sentry/core': 10.47.0 + '@sentry/core': 10.52.0 '@sindresorhus/is@4.6.0': {} '@sindresorhus/is@7.2.0': {} + '@sindresorhus/is@8.0.0': {} + '@so-ric/colorspace@1.1.6': dependencies: color: 5.0.3 @@ -8200,37 +8246,38 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.2.0(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.3.0(jiti@2.6.1))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@typescript-eslint/types': 8.58.0 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.4 - '@tootallnate/quickjs-emscripten@0.23.0': {} - '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aes-js@3.1.4': {} '@types/aws-lambda@8.10.161': {} '@types/babel__preset-env@7.10.0': {} - '@types/bezier-js@4.1.3': {} - '@types/bluebird@3.5.42': {} '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/caseless@0.12.5': {} @@ -8243,7 +8290,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/crypto-js@4.2.2': {} @@ -8264,12 +8311,12 @@ snapshots: '@types/etag@1.8.4': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 25.5.2 - '@types/qs': 6.15.0 + '@types/node': 25.6.2 + '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -8282,7 +8329,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/html-to-text@9.0.4': {} @@ -8294,7 +8341,7 @@ snapshots: '@types/jsdom@28.0.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 undici-types: 7.24.6 @@ -8307,7 +8354,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/jsrsasign@10.5.15': {} @@ -8315,7 +8362,7 @@ snapshots: '@types/mailparser@3.4.6': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 iconv-lite: 0.6.3 '@types/markdown-it@14.1.2': @@ -8335,11 +8382,11 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 - '@types/node@25.5.2': + '@types/node@25.6.2': dependencies: - undici-types: 7.18.2 + undici-types: 7.19.2 '@types/pg-pool@2.0.7': dependencies: @@ -8347,11 +8394,11 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 pg-protocol: 1.13.0 pg-types: 2.2.0 - '@types/qs@6.15.0': {} + '@types/qs@6.15.1': {} '@types/range-parser@1.2.7': {} @@ -8363,7 +8410,7 @@ snapshots: '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/tough-cookie': 4.0.5 form-data: 2.5.5 @@ -8373,39 +8420,40 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.5.2 + '@types/node': 25.6.2 + + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 25.6.2 '@types/statuses@2.0.6': {} '@types/tedious@4.0.14': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 '@types/tough-cookie@4.0.5': {} '@types/triple-beam@1.3.5': {} - '@types/unist@3.0.3': {} + '@types/unist@2.0.11': {} - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 25.5.2 - optional: true + '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/type-utils': 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.1 - eslint: 10.2.0(jiti@2.6.1) + '@typescript-eslint/parser': 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/type-utils': 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 + eslint: 10.3.0(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -8413,43 +8461,43 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 debug: 4.4.3 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.58.1': + '@typescript-eslint/scope-manager@8.59.2': dependencies: - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 - '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -8457,14 +8505,14 @@ snapshots: '@typescript-eslint/types@8.58.0': {} - '@typescript-eslint/types@8.58.1': {} + '@typescript-eslint/types@8.59.2': {} - '@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.58.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/project-service': 8.59.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 debug: 4.4.3 minimatch: 10.2.5 semver: 7.7.4 @@ -8474,20 +8522,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - eslint: 10.2.0(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(typescript@5.9.3) + eslint: 10.3.0(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.58.1': + '@typescript-eslint/visitor-keys@8.59.2': dependencies: - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/types': 8.59.2 eslint-visitor-keys: 5.0.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -8549,10 +8597,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vercel/nft@1.5.0(rollup@4.60.1)': + '@vercel/nft@1.5.0(rollup@4.60.3)': dependencies: '@mapbox/node-pre-gyp': 2.0.3 - '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + '@rollup/pluginutils': 5.3.0(rollup@4.60.3) acorn: 8.16.0 acorn-import-attributes: 1.9.5(acorn@8.16.0) async-sema: 3.1.1 @@ -8568,61 +8616,60 @@ snapshots: - rollup - supports-color - '@vitest/coverage-v8@4.0.9(vitest@4.0.9(@edge-runtime/vm@3.2.0)(@types/debug@4.1.13)(@types/node@25.5.2)(jiti@2.6.1)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/coverage-v8@4.1.5(vitest@4.1.5)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.9 - ast-v8-to-istanbul: 0.3.12 - debug: 4.4.3 + '@vitest/utils': 4.1.5 + ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 magicast: 0.5.2 - std-env: 3.10.0 + obug: 2.1.1 + std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.0.9(@edge-runtime/vm@3.2.0)(@types/debug@4.1.13)(@types/node@25.5.2)(jiti@2.6.1)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - supports-color + vitest: 4.1.5(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)) - '@vitest/expect@4.0.9': + '@vitest/expect@4.1.5': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.9 - '@vitest/utils': 4.0.9 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.0.9(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.5(msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4))': dependencies: - '@vitest/spy': 4.0.9 + '@vitest/spy': 4.1.5 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.13.2(@types/node@25.5.2)(typescript@5.9.3) - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + msw: 2.13.4(@types/node@25.6.2)(typescript@5.9.3) + vite: 7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4) - '@vitest/pretty-format@4.0.9': + '@vitest/pretty-format@4.1.5': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.0.9': + '@vitest/runner@4.1.5': dependencies: - '@vitest/utils': 4.0.9 + '@vitest/utils': 4.1.5 pathe: 2.0.3 - '@vitest/snapshot@4.0.9': + '@vitest/snapshot@4.1.5': dependencies: - '@vitest/pretty-format': 4.0.9 + '@vitest/pretty-format': 4.1.5 + '@vitest/utils': 4.1.5 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.9': {} + '@vitest/spy@4.1.5': {} - '@vitest/utils@4.0.9': + '@vitest/utils@4.1.5': dependencies: - '@vitest/pretty-format': 4.0.9 + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 tinyrainbow: 3.1.0 '@zone-eu/mailsplit@5.4.8': @@ -8631,6 +8678,12 @@ snapshots: libmime: 5.3.7 libqp: 2.1.1 + '@zone-eu/mailsplit@5.4.9': + dependencies: + libbase64: 1.3.0 + libmime: 5.3.8 + libqp: 2.1.1 + abbrev@2.0.0: {} abbrev@3.0.1: {} @@ -8690,7 +8743,7 @@ snapshots: ast-kit@3.0.0-beta.1: dependencies: - '@babel/parser': 8.0.0-rc.3 + '@babel/parser': 8.0.0-rc.4 estree-walker: 3.0.3 pathe: 2.0.3 @@ -8698,7 +8751,7 @@ snapshots: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@0.3.12: + ast-v8-to-istanbul@1.0.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -8720,51 +8773,17 @@ snapshots: aws4@1.13.2: {} - b4a@1.8.0: {} - bail@2.0.2: {} balanced-match@1.0.2: {} balanced-match@4.0.4: {} - bare-events@2.8.2: {} - - bare-fs@4.5.6: - dependencies: - bare-events: 2.8.2 - bare-path: 3.0.0 - bare-stream: 2.11.0(bare-events@2.8.2) - bare-url: 2.4.0 - fast-fifo: 1.3.2 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - bare-os@3.8.6: {} - - bare-path@3.0.0: - dependencies: - bare-os: 3.8.6 - - bare-stream@2.11.0(bare-events@2.8.2): - dependencies: - streamx: 2.25.0 - teex: 1.0.1 - optionalDependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - react-native-b4a - - bare-url@2.4.0: - dependencies: - bare-path: 3.0.0 - base64-js@1.5.1: {} baseline-browser-mapping@2.10.13: {} - basic-ftp@5.2.0: {} + basic-ftp@5.3.0: {} bcrypt-pbkdf@1.0.2: dependencies: @@ -8772,8 +8791,6 @@ snapshots: before-after-hook@4.0.0: {} - bezier-js@6.1.4: {} - bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 @@ -8804,7 +8821,7 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.3: + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -8820,15 +8837,8 @@ snapshots: node-releases: 2.0.36 update-browserslist-db: 1.2.3(browserslist@4.28.2) - buffer-crc32@0.2.13: {} - buffer-equal-constant-time@1.0.1: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -8877,6 +8887,8 @@ snapshots: caseless@0.12.0: {} + ccount@2.0.1: {} + chai@6.2.2: {} chalk@5.6.2: {} @@ -8907,36 +8919,16 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.24.7 + undici: 7.25.0 whatwg-mimetype: 4.0.0 chownr@3.0.0: {} - chrome-launcher@1.2.1: - dependencies: - '@types/node': 25.5.2 - escape-string-regexp: 4.0.0 - is-wsl: 2.2.0 - lighthouse-logger: 2.0.2 - transitivePeerDependencies: - - supports-color - - chromium-bidi@0.8.0(devtools-protocol@0.0.1367902): - dependencies: - devtools-protocol: 0.0.1367902 - mitt: 3.0.1 - urlpattern-polyfill: 10.0.0 - zod: 3.23.8 - - chromium-bidi@5.1.0(devtools-protocol@0.0.1439962): - dependencies: - devtools-protocol: 0.0.1439962 - mitt: 3.0.1 - zod: 3.25.76 + chunk-data@0.1.0: {} ci-info@4.4.0: {} - city-timezones@1.3.3: {} + city-timezones@1.3.4: {} cjs-module-lexer@2.2.0: {} @@ -8953,7 +8945,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.0 + string-width: 8.2.1 cli-width@4.1.0: {} @@ -8992,16 +8984,12 @@ snapshots: color-convert: 3.1.3 color-string: 2.1.4 - colorette@2.0.20: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 commander@10.0.1: {} - commander@14.0.3: {} - comment-parser@1.4.6: {} concat-map@0.0.1: {} @@ -9013,6 +9001,8 @@ snapshots: consola@3.4.2: {} + convert-source-map@2.0.0: {} + cookie@1.1.1: {} core-js-compat@3.49.0: @@ -9023,15 +9013,6 @@ snapshots: core-util-is@1.0.2: {} - cosmiconfig@9.0.1(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 - cross-env@10.1.0: dependencies: '@epic-web/invariant': 1.0.0 @@ -9073,8 +9054,6 @@ snapshots: data-uri-to-buffer@4.0.1: {} - data-uri-to-buffer@6.0.2: {} - data-uri-to-buffer@8.0.0: {} data-urls@7.0.0(@noble/hashes@2.0.1): @@ -9121,17 +9100,13 @@ snapshots: deep-is@0.1.4: {} + deepmerge-ts@7.1.5: {} + deepmerge@4.3.1: {} define-lazy-prop@2.0.0: {} - defu@6.1.4: {} - - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 + defu@6.1.7: {} degenerator@7.0.1(quickjs-wasi@2.2.0): dependencies: @@ -9154,19 +9129,13 @@ snapshots: dependencies: dequal: 2.0.3 - devtools-protocol@0.0.1299070: {} - - devtools-protocol@0.0.1367902: {} - - devtools-protocol@0.0.1439962: {} - diff-sequences@29.6.3: {} difflib@https://codeload.github.com/postlight/difflib.js/tar.gz/32e8e38c7fcd935241b9baab71bb432fd9b166ed: dependencies: heap: 0.2.7 - discord-api-types@0.38.44: {} + discord-api-types@0.38.47: {} dom-serializer@1.4.1: dependencies: @@ -9212,9 +9181,9 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@17.4.1: {} + dotenv@17.4.2: {} - dts-resolver@2.1.3: {} + dts-resolver@3.0.0: {} eastasianwidth@0.2.0: {} @@ -9236,15 +9205,13 @@ snapshots: electron-to-chromium@1.5.330: {} - ellipsize@0.6.0: {} - emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} - empathic@2.0.0: {} + empathic@2.0.1: {} enabled@2.0.0: {} @@ -9255,18 +9222,14 @@ snapshots: iconv-lite: 0.6.3 whatwg-encoding: 3.1.1 - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - endpoint@0.4.5: dependencies: inherits: 2.0.4 - enhanced-resolve@5.20.1: + enhanced-resolve@5.21.0: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.2 + tapable: 2.3.3 entities@2.2.0: {} @@ -9278,17 +9241,14 @@ snapshots: entities@8.0.0: {} - env-paths@2.2.1: {} - environment@1.1.0: {} - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - error-stack-parser-es@1.0.5: {} - es-module-lexer@1.7.0: {} + es-errors@1.3.0: + optional: true + + es-module-lexer@2.0.0: {} es5-ext@0.10.64: dependencies: @@ -9418,19 +9378,19 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-compat-utils@0.5.1(eslint@10.2.0(jiti@2.6.1)): + eslint-compat-utils@0.5.1(eslint@10.3.0(jiti@2.6.1)): dependencies: - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) semver: 7.7.4 - eslint-filtered-fix@0.3.0(eslint@10.2.0(jiti@2.6.1)): + eslint-filtered-fix@0.3.0(eslint@10.3.0(jiti@2.6.1)): dependencies: - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) optionator: 0.9.4 eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - get-tsconfig: 4.13.7 + get-tsconfig: 4.14.0 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.11.1 @@ -9439,39 +9399,39 @@ snapshots: dependencies: debug: 3.2.7 is-core-module: '@nolyfill/is-core-module@1.0.39' - resolve: 1.22.11 + resolve: 1.22.12 transitivePeerDependencies: - supports-color optional: true - eslint-nibble@9.1.1(@types/node@25.5.2)(eslint@10.2.0(jiti@2.6.1)): + eslint-nibble@9.1.1(@types/node@25.6.2)(eslint@10.3.0(jiti@2.6.1)): dependencies: '@babel/code-frame': 7.29.0 - '@inquirer/checkbox': 4.3.2(@types/node@25.5.2) - '@inquirer/confirm': 5.1.21(@types/node@25.5.2) - '@inquirer/select': 4.4.2(@types/node@25.5.2) - eslint: 10.2.0(jiti@2.6.1) - eslint-filtered-fix: 0.3.0(eslint@10.2.0(jiti@2.6.1)) + '@inquirer/checkbox': 4.3.2(@types/node@25.6.2) + '@inquirer/confirm': 5.1.21(@types/node@25.6.2) + '@inquirer/select': 4.4.2(@types/node@25.6.2) + eslint: 10.3.0(jiti@2.6.1) + eslint-filtered-fix: 0.3.0(eslint@10.3.0(jiti@2.6.1)) 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.2.0(jiti@2.6.1)): + eslint-plugin-es-x@7.8.0(eslint@10.3.0(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - eslint: 10.2.0(jiti@2.6.1) - eslint-compat-utils: 0.5.1(eslint@10.2.0(jiti@2.6.1)) + eslint: 10.3.0(jiti@2.6.1) + eslint-compat-utils: 0.5.1(eslint@10.3.0(jiti@2.6.1)) - eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.2.0(jiti@2.6.1)): + eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@10.3.0(jiti@2.6.1)): dependencies: '@package-json/types': 0.0.12 '@typescript-eslint/types': 8.58.0 comment-parser: 1.4.6 debug: 4.4.3 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 minimatch: 10.2.5 @@ -9479,41 +9439,41 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.58.1(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@10.3.0(jiti@2.6.1))(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-n@17.24.0(eslint@10.2.0(jiti@2.6.1))(typescript@5.9.3): + eslint-plugin-n@18.0.1(eslint@10.3.0(jiti@2.6.1))(ts-declaration-location@1.0.7(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) - enhanced-resolve: 5.20.1 - eslint: 10.2.0(jiti@2.6.1) - eslint-plugin-es-x: 7.8.0(eslint@10.2.0(jiti@2.6.1)) - get-tsconfig: 4.13.7 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) + enhanced-resolve: 5.21.0 + eslint: 10.3.0(jiti@2.6.1) + eslint-plugin-es-x: 7.8.0(eslint@10.3.0(jiti@2.6.1)) + get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 semver: 7.7.4 + optionalDependencies: ts-declaration-location: 1.0.7(typescript@5.9.3) - transitivePeerDependencies: - - typescript + typescript: 5.9.3 - eslint-plugin-simple-import-sort@13.0.0(eslint@10.2.0(jiti@2.6.1)): + eslint-plugin-simple-import-sort@13.0.0(eslint@10.3.0(jiti@2.6.1)): dependencies: - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) - eslint-plugin-unicorn@64.0.0(eslint@10.2.0(jiti@2.6.1)): + eslint-plugin-unicorn@64.0.0(eslint@10.3.0(jiti@2.6.1)): dependencies: '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) change-case: 5.4.4 ci-info: 4.4.0 clean-regexp: 1.0.0 core-js-compat: 3.49.0 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) find-up-simple: 1.0.1 - globals: 17.4.0 + globals: 17.6.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 jsesc: 3.1.0 @@ -9523,19 +9483,16 @@ snapshots: semver: 7.7.4 strip-indent: 4.1.1 - eslint-plugin-yml@3.3.1(eslint@10.2.0(jiti@2.6.1)): + eslint-plugin-yml@3.3.2(eslint@10.3.0(jiti@2.6.1)): dependencies: - '@eslint/core': 1.1.1 - '@eslint/plugin-kit': 0.6.1 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.1 '@ota-meshi/ast-token-store': 0.3.0 - debug: 4.4.3 diff-sequences: 29.6.3 escape-string-regexp: 5.0.0 - eslint: 10.2.0(jiti@2.6.1) + eslint: 10.3.0(jiti@2.6.1) natural-compare: 1.4.0 yaml-eslint-parser: 2.0.0 - transitivePeerDependencies: - - supports-color eslint-scope@9.1.2: dependencies: @@ -9550,15 +9507,15 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.2.0(jiti@2.6.1): + eslint@10.3.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.4 - '@eslint/config-helpers': 0.5.4 - '@eslint/core': 1.2.0 - '@eslint/plugin-kit': 0.7.0 - '@humanfs/node': 0.16.7 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.5.5 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.1 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -9635,12 +9592,6 @@ snapshots: eventemitter3@5.0.4: {} - events-universal@1.0.1: - dependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - bare-abort-controller - execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -9661,16 +9612,6 @@ snapshots: extend@3.0.2: {} - extract-zip@2.0.1: - dependencies: - debug: 4.4.3 - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - extsprintf@1.3.0: {} fanfou-sdk@6.0.0: @@ -9688,15 +9629,19 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-fifo@1.3.2: {} - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fd-slicer@1.1.0: + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.0: dependencies: - pend: 1.2.0 + fast-string-width: 3.0.2 fdir@6.5.0(picomatch@4.0.4): optionalDependencies: @@ -9765,12 +9710,15 @@ snapshots: forwarded-parse@2.1.2: {} - fs-extra@11.3.4: + fs-extra@11.3.5: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.2.0 + jsonfile: 6.2.1 universalify: 2.0.1 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -9799,10 +9747,6 @@ snapshots: get-east-asian-width@1.5.0: {} - get-stream@5.2.0: - dependencies: - pump: 3.0.4 - get-stream@8.0.1: {} get-stream@9.0.1: @@ -9814,17 +9758,17 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-uri@6.0.5: + get-tsconfig@4.14.0: dependencies: - basic-ftp: 5.2.0 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + resolve-pkg-maps: 1.0.0 + + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 get-uri@8.0.0: dependencies: - basic-ftp: 5.2.0 + basic-ftp: 5.3.0 data-uri-to-buffer: 8.0.0 debug: 4.4.3 transitivePeerDependencies: @@ -9834,14 +9778,6 @@ snapshots: dependencies: assert-plus: 1.0.0 - ghost-cursor@1.4.2: - dependencies: - '@types/bezier-js': 4.1.3 - bezier-js: 6.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -9865,7 +9801,7 @@ snapshots: globals@15.15.0: {} - globals@17.4.0: {} + globals@17.6.0: {} globrex@0.1.2: {} @@ -9925,6 +9861,21 @@ snapshots: responselike: 4.0.2 type-fest: 4.41.0 + got@15.0.5: + dependencies: + '@sindresorhus/is': 8.0.0 + byte-counter: 0.1.0 + cacheable-lookup: 7.0.0 + cacheable-request: 13.0.18 + 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 + uint8array-extras: 1.5.0 + graceful-fs@4.2.11: {} graphql@16.13.2: {} @@ -9947,15 +9898,18 @@ snapshots: ow: 0.28.2 tslib: 2.8.1 - headers-polyfill@4.0.3: {} + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.0 heap@0.2.7: {} hmacsha1@1.0.0: {} - hono@4.12.12: {} + hono@4.12.18: {} - hookable@6.1.0: {} + hookable@6.1.1: {} html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: @@ -9965,6 +9919,14 @@ snapshots: html-escaper@2.0.2: {} + html-to-text@10.0.0: + dependencies: + '@selderee/plugin-htmlparser2': 0.12.0(selderee@0.12.0) + deepmerge-ts: 7.1.5 + dom-serializer: 2.0.0 + htmlparser2: 10.1.0 + selderee: 0.12.0 + html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -9996,19 +9958,12 @@ snapshots: http-cache-semantics@4.2.0: {} - http-cookie-agent@7.0.3(tough-cookie@6.0.1)(undici@7.24.7): + http-cookie-agent@8.0.0(tough-cookie@6.0.1)(undici@7.25.0): dependencies: - agent-base: 7.1.4 + agent-base: 9.0.0 tough-cookie: 6.0.1 optionalDependencies: - undici: 7.24.7 - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + undici: 7.25.0 http-proxy-agent@9.0.0: dependencies: @@ -10068,17 +10023,17 @@ snapshots: image-size@0.7.5: {} - imapflow@1.3.1: + imapflow@1.3.3: dependencies: - '@zone-eu/mailsplit': 5.4.8 + '@zone-eu/mailsplit': 5.4.9 encoding-japanese: 2.2.0 iconv-lite: 0.7.2 libbase64: 1.3.0 libmime: 5.3.8 libqp: 2.1.1 - nodemailer: 8.0.5 + nodemailer: 8.0.7 pino: 10.3.1 - socks: 2.8.7 + socks: 2.8.8 import-fresh@3.3.1: dependencies: @@ -10092,14 +10047,14 @@ snapshots: cjs-module-lexer: 2.2.0 module-details-from-path: 1.0.4 - import-in-the-middle@3.0.0: + import-in-the-middle@3.0.1: dependencies: acorn: 8.16.0 acorn-import-attributes: 1.9.5(acorn@8.16.0) cjs-module-lexer: 2.2.0 module-details-from-path: 1.0.4 - import-without-cache@0.2.5: {} + import-without-cache@0.4.0: {} imurmurhash@0.1.4: {} @@ -10125,7 +10080,7 @@ snapshots: debug: 4.4.3 image-size: 0.7.5 json-bigint: 1.0.0 - lodash: 4.17.23 + lodash: 4.18.1 luxon: 1.28.1 reflect-metadata: 0.1.14 request: 2.88.2 @@ -10154,14 +10109,12 @@ snapshots: transitivePeerDependencies: - supports-color - ip-address@10.1.0: {} + ip-address@10.2.0: {} ip-regex@4.3.0: {} ip-regex@5.0.0: {} - is-arrayish@0.2.1: {} - is-builtin-module@5.0.0: dependencies: builtin-modules: 5.0.0 @@ -10230,14 +10183,6 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 @@ -10272,24 +10217,24 @@ snapshots: jsbn@0.1.1: {} - jsdom@29.0.2(@noble/hashes@2.0.1): + jsdom@29.1.1(@noble/hashes@2.0.1): dependencies: - '@asamuzakjp/css-color': 5.1.6 - '@asamuzakjp/dom-selector': 7.0.7 + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.0.1) decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0(@noble/hashes@2.0.1) is-potential-custom-element-name: 1.0.1 - lru-cache: 11.3.3 - parse5: 8.0.0 + lru-cache: 11.3.6 + parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.1 - undici: 7.24.7 + undici: 7.25.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10310,8 +10255,6 @@ snapshots: json-nd@1.0.0: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-schema@0.4.0: {} @@ -10322,7 +10265,7 @@ snapshots: json-with-bigint@3.5.8: {} - jsonfile@6.2.0: + jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: @@ -10348,7 +10291,7 @@ snapshots: json-schema: 0.4.0 verror: 1.10.0 - jsrsasign@11.1.1: {} + jsrsasign@11.1.3: {} jwa@2.0.1: dependencies: @@ -10375,6 +10318,8 @@ snapshots: leac@0.6.0: {} + leac@0.7.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -10398,36 +10343,26 @@ snapshots: libqp@2.1.1: {} - lighthouse-logger@2.0.2: - dependencies: - debug: 4.4.3 - marky: 1.3.0 - transitivePeerDependencies: - - supports-color - - lines-and-columns@1.2.4: {} - linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 - lint-staged@16.4.0: + lint-staged@17.0.2: dependencies: - commander: 14.0.3 - listr2: 9.0.5 + listr2: 10.2.1 picomatch: 4.0.4 string-argv: 0.3.2 - tinyexec: 1.0.4 - yaml: 2.8.3 + tinyexec: 1.1.2 + optionalDependencies: + yaml: 2.8.4 - listr2@9.0.5: + listr2@10.2.1: dependencies: cli-truncate: 5.2.0 - colorette: 2.0.20 eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.0 locate-path@6.0.0: dependencies: @@ -10439,7 +10374,7 @@ snapshots: lodash.isequal@4.5.0: {} - lodash@4.17.23: {} + lodash@4.18.1: {} log-update@6.1.0: dependencies: @@ -10460,13 +10395,15 @@ snapshots: long@5.3.2: {} + longest-streak@3.1.0: {} + lowercase-keys@3.0.0: {} - lru-cache@10.4.3: {} + lowercase-keys@4.0.1: {} - lru-cache@11.3.3: {} + lru-cache@10.4.3: {} - lru-cache@7.18.3: {} + lru-cache@11.3.6: {} lru-queue@0.1.0: dependencies: @@ -10520,7 +10457,14 @@ snapshots: dependencies: repeat-string: 1.6.1 - marky@1.3.0: {} + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.1 mdast-util-from-markdown@2.0.3: dependencies: @@ -10539,6 +10483,80 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + mdast-util-to-string@4.0.0: dependencies: '@types/mdast': 4.0.4 @@ -10581,6 +10599,64 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 @@ -10709,12 +10785,12 @@ snapshots: mimic-response@4.0.0: {} - miniflare@4.20260405.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + miniflare@4.20260507.1(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.4 - workerd: 1.20260405.1 + undici: 7.24.8 + workerd: 1.20260507.1 ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -10731,7 +10807,7 @@ snapshots: minimatch@9.0.9: dependencies: - brace-expansion: 2.0.3 + brace-expansion: 2.1.0 minipass@7.1.3: {} @@ -10739,11 +10815,9 @@ snapshots: dependencies: minipass: 7.1.3 - mitt@3.0.1: {} - mixi2@0.2.2: dependencies: - protobufjs: 7.5.4 + protobufjs: 7.5.5 mockdate@3.0.5: {} @@ -10753,20 +10827,20 @@ snapshots: ms@2.1.3: {} - msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3): + msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3): dependencies: - '@inquirer/confirm': 5.1.21(@types/node@25.5.2) + '@inquirer/confirm': 6.0.11(@types/node@25.6.2) '@mswjs/interceptors': 0.41.3(patch_hash=5027fcc424409c41c41147fc6c90b36166061522e0b03e73b45f9a973fcd2a28) - '@open-draft/deferred-promise': 2.2.0 + '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 cookie: 1.1.1 graphql: 16.13.2 - headers-polyfill: 4.0.3 + headers-polyfill: 5.0.1 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 picocolors: 1.1.1 - rettime: 0.10.1 + rettime: 0.11.7 statuses: 2.0.2 strict-event-emitter: 0.5.1 tough-cookie: 6.0.1 @@ -10780,15 +10854,16 @@ snapshots: mute-stream@2.0.0: {} - nan@1.8.4: - optional: true + mute-stream@3.0.0: {} - nan@2.26.2: + nan@1.8.4: optional: true nanoid@3.3.11: {} - nanoid@5.1.7: {} + nanoid@3.3.12: {} + + nanoid@5.1.11: {} napi-postinstall@0.3.4: {} @@ -10820,13 +10895,13 @@ snapshots: dependencies: write-file-atomic: 1.3.4 - node-network-devtools@1.0.29(undici@7.24.7)(utf-8-validate@5.0.10): + node-network-devtools@1.0.30(undici@7.25.0)(utf-8-validate@5.0.10): dependencies: bufferutil: 4.1.0 - iconv-lite: 0.6.3 + iconv-lite: 0.7.2 inspector: 0.5.0 open: 8.4.2 - undici: 7.24.7 + undici: 7.25.0 ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - utf-8-validate @@ -10835,6 +10910,8 @@ snapshots: nodemailer@8.0.5: {} + nodemailer@8.0.7: {} + nopt@7.2.1: dependencies: abbrev: 2.0.0 @@ -10870,14 +10947,10 @@ snapshots: dependencies: destr: 2.0.5 node-fetch-native: 1.6.7 - ufo: 1.6.3 + ufo: 1.6.4 on-exit-leak-free@2.1.2: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - one-time@1.0.0: dependencies: fn.name: 1.1.0 @@ -10904,7 +10977,7 @@ snapshots: openapi3-ts@4.5.0: dependencies: - yaml: 2.8.3 + yaml: 2.8.4 optionator@0.9.4: dependencies: @@ -10936,63 +11009,63 @@ snapshots: lodash.isequal: 4.5.0 vali-date: 1.0.0 - oxfmt@0.44.0: + oxfmt@0.47.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.44.0 - '@oxfmt/binding-android-arm64': 0.44.0 - '@oxfmt/binding-darwin-arm64': 0.44.0 - '@oxfmt/binding-darwin-x64': 0.44.0 - '@oxfmt/binding-freebsd-x64': 0.44.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.44.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.44.0 - '@oxfmt/binding-linux-arm64-gnu': 0.44.0 - '@oxfmt/binding-linux-arm64-musl': 0.44.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.44.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.44.0 - '@oxfmt/binding-linux-riscv64-musl': 0.44.0 - '@oxfmt/binding-linux-s390x-gnu': 0.44.0 - '@oxfmt/binding-linux-x64-gnu': 0.44.0 - '@oxfmt/binding-linux-x64-musl': 0.44.0 - '@oxfmt/binding-openharmony-arm64': 0.44.0 - '@oxfmt/binding-win32-arm64-msvc': 0.44.0 - '@oxfmt/binding-win32-ia32-msvc': 0.44.0 - '@oxfmt/binding-win32-x64-msvc': 0.44.0 - - oxlint-plugin-eslint@1.59.0: {} - - oxlint-tsgolint@0.20.0: + '@oxfmt/binding-android-arm-eabi': 0.47.0 + '@oxfmt/binding-android-arm64': 0.47.0 + '@oxfmt/binding-darwin-arm64': 0.47.0 + '@oxfmt/binding-darwin-x64': 0.47.0 + '@oxfmt/binding-freebsd-x64': 0.47.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.47.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.47.0 + '@oxfmt/binding-linux-arm64-gnu': 0.47.0 + '@oxfmt/binding-linux-arm64-musl': 0.47.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.47.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.47.0 + '@oxfmt/binding-linux-riscv64-musl': 0.47.0 + '@oxfmt/binding-linux-s390x-gnu': 0.47.0 + '@oxfmt/binding-linux-x64-gnu': 0.47.0 + '@oxfmt/binding-linux-x64-musl': 0.47.0 + '@oxfmt/binding-openharmony-arm64': 0.47.0 + '@oxfmt/binding-win32-arm64-msvc': 0.47.0 + '@oxfmt/binding-win32-ia32-msvc': 0.47.0 + '@oxfmt/binding-win32-x64-msvc': 0.47.0 + + oxlint-plugin-eslint@1.62.0: {} + + oxlint-tsgolint@0.22.1: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.20.0 - '@oxlint-tsgolint/darwin-x64': 0.20.0 - '@oxlint-tsgolint/linux-arm64': 0.20.0 - '@oxlint-tsgolint/linux-x64': 0.20.0 - '@oxlint-tsgolint/win32-arm64': 0.20.0 - '@oxlint-tsgolint/win32-x64': 0.20.0 - - oxlint@1.59.0(oxlint-tsgolint@0.20.0): + '@oxlint-tsgolint/darwin-arm64': 0.22.1 + '@oxlint-tsgolint/darwin-x64': 0.22.1 + '@oxlint-tsgolint/linux-arm64': 0.22.1 + '@oxlint-tsgolint/linux-x64': 0.22.1 + '@oxlint-tsgolint/win32-arm64': 0.22.1 + '@oxlint-tsgolint/win32-x64': 0.22.1 + + oxlint@1.62.0(oxlint-tsgolint@0.22.1): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.59.0 - '@oxlint/binding-android-arm64': 1.59.0 - '@oxlint/binding-darwin-arm64': 1.59.0 - '@oxlint/binding-darwin-x64': 1.59.0 - '@oxlint/binding-freebsd-x64': 1.59.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.59.0 - '@oxlint/binding-linux-arm-musleabihf': 1.59.0 - '@oxlint/binding-linux-arm64-gnu': 1.59.0 - '@oxlint/binding-linux-arm64-musl': 1.59.0 - '@oxlint/binding-linux-ppc64-gnu': 1.59.0 - '@oxlint/binding-linux-riscv64-gnu': 1.59.0 - '@oxlint/binding-linux-riscv64-musl': 1.59.0 - '@oxlint/binding-linux-s390x-gnu': 1.59.0 - '@oxlint/binding-linux-x64-gnu': 1.59.0 - '@oxlint/binding-linux-x64-musl': 1.59.0 - '@oxlint/binding-openharmony-arm64': 1.59.0 - '@oxlint/binding-win32-arm64-msvc': 1.59.0 - '@oxlint/binding-win32-ia32-msvc': 1.59.0 - '@oxlint/binding-win32-x64-msvc': 1.59.0 - oxlint-tsgolint: 0.20.0 + '@oxlint/binding-android-arm-eabi': 1.62.0 + '@oxlint/binding-android-arm64': 1.62.0 + '@oxlint/binding-darwin-arm64': 1.62.0 + '@oxlint/binding-darwin-x64': 1.62.0 + '@oxlint/binding-freebsd-x64': 1.62.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.62.0 + '@oxlint/binding-linux-arm-musleabihf': 1.62.0 + '@oxlint/binding-linux-arm64-gnu': 1.62.0 + '@oxlint/binding-linux-arm64-musl': 1.62.0 + '@oxlint/binding-linux-ppc64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-musl': 1.62.0 + '@oxlint/binding-linux-s390x-gnu': 1.62.0 + '@oxlint/binding-linux-x64-gnu': 1.62.0 + '@oxlint/binding-linux-x64-musl': 1.62.0 + '@oxlint/binding-openharmony-arm64': 1.62.0 + '@oxlint/binding-win32-arm64-msvc': 1.62.0 + '@oxlint/binding-win32-ia32-msvc': 1.62.0 + '@oxlint/binding-win32-x64-msvc': 1.62.0 + oxlint-tsgolint: 0.22.1 p-cancelable@4.0.1: {} @@ -11006,19 +11079,6 @@ snapshots: p-map@7.0.4: {} - pac-proxy-agent@7.2.0: - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3 - get-uri: 6.0.5 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - pac-proxy-agent@9.0.1: dependencies: agent-base: 9.0.0 @@ -11032,11 +11092,6 @@ snapshots: transitivePeerDependencies: - supports-color - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 - pac-resolver@9.0.1(quickjs-wasi@2.2.0): dependencies: degenerator: 7.0.1(quickjs-wasi@2.2.0) @@ -11047,17 +11102,12 @@ snapshots: pako@2.1.0: {} + pangu@4.0.7: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse-srcset@1.0.2: {} parse5-htmlparser2-tree-adapter@7.1.0: @@ -11073,15 +11123,20 @@ snapshots: dependencies: entities: 6.0.1 - parse5@8.0.0: + parse5@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.0.0 parseley@0.12.1: dependencies: leac: 0.6.0 peberminta: 0.9.0 + parseley@0.13.1: + dependencies: + leac: 0.7.0 + peberminta: 0.10.0 + path-browserify@1.0.1: {} path-exists@4.0.0: {} @@ -11100,16 +11155,16 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.3.3 + lru-cache: 11.3.6 minipass: 7.1.3 path-to-regexp@6.3.0: {} pathe@2.0.3: {} - peberminta@0.9.0: {} + peberminta@0.10.0: {} - pend@1.2.0: {} + peberminta@0.9.0: {} performance-now@2.1.0: {} @@ -11149,14 +11204,28 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 4.0.0 + playwright-core@1.59.1: {} + + playwright@1.59.1: + dependencies: + playwright-core: 1.59.1 + optionalDependencies: + fsevents: 2.3.2 + pluralize@8.0.0: {} - postcss@8.5.8: + postcss@8.5.10: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgres-array@2.0.0: {} postgres-bytea@1.0.1: {} @@ -11196,11 +11265,9 @@ snapshots: process-warning@5.0.0: {} - progress@2.0.3: {} - proto-list@1.2.4: {} - protobufjs@7.5.4: + protobufjs@7.5.5: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -11212,72 +11279,32 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 25.5.2 + '@types/node': 25.6.2 long: 5.3.2 - proxy-agent@6.5.0: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - proxy-chain@2.7.1: + protobufjs@8.0.1: dependencies: - socks: 2.8.7 - socks-proxy-agent: 8.0.5 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.1 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.1 + '@types/node': 25.6.2 + long: 5.3.2 psl@1.15.0: dependencies: punycode: 2.3.1 - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode.js@2.3.1: {} punycode@2.3.1: {} - puppeteer-extra@3.3.6: - dependencies: - '@types/debug': 4.1.13 - debug: 4.4.3 - deepmerge: 4.3.1 - transitivePeerDependencies: - - supports-color - - puppeteer-real-browser@1.4.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): - dependencies: - chrome-launcher: 1.2.1 - ghost-cursor: 1.4.2 - puppeteer-extra: 3.3.6 - rebrowser-puppeteer-core: 23.10.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - tree-kill: 1.2.2 - xvfb: 0.4.0 - transitivePeerDependencies: - - '@types/puppeteer' - - bare-abort-controller - - bare-buffer - - bufferutil - - puppeteer - - puppeteer-core - - react-native-b4a - - supports-color - - utf-8-validate - qs@6.14.2: dependencies: side-channel: '@nolyfill/side-channel@1.0.44' @@ -11308,9 +11335,9 @@ snapshots: ramda@0.32.0: {} - rate-limiter-flexible@11.0.0: {} + rate-limiter-flexible@11.1.0: {} - re2js@2.0.1: {} + re2js@2.3.2: {} readable-stream@3.6.2: dependencies: @@ -11322,55 +11349,6 @@ snapshots: real-require@0.2.0: {} - rebrowser-puppeteer-core@23.10.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): - dependencies: - '@puppeteer/browsers': 2.6.1 - chromium-bidi: 0.8.0(devtools-protocol@0.0.1367902) - debug: 4.4.3 - devtools-protocol: 0.0.1367902 - typed-query-selector: 2.12.1 - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - utf-8-validate - - rebrowser-puppeteer-core@24.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): - dependencies: - '@puppeteer/browsers': 2.10.3 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1439962) - debug: 4.4.3 - devtools-protocol: 0.0.1439962 - typed-query-selector: 2.12.1 - ws: 8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - utf-8-validate - - rebrowser-puppeteer@24.8.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10): - dependencies: - '@puppeteer/browsers': 2.10.3 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1439962) - cosmiconfig: 9.0.1(typescript@5.9.3) - devtools-protocol: 0.0.1439962 - puppeteer-core: rebrowser-puppeteer-core@24.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - typed-query-selector: 2.12.1 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - bufferutil - - react-native-b4a - - supports-color - - typescript - - utf-8-validate - redis-errors@1.2.0: {} redis-parser@3.0.0: @@ -11385,6 +11363,23 @@ snapshots: dependencies: jsesc: 3.1.0 + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-pangu@2.2.0: + dependencies: + pangu: 4.0.7 + unist-util-is: 4.1.0 + unist-util-visit: 2.0.3 + remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -11394,11 +11389,26 @@ snapshots: transitivePeerDependencies: - supports-color + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remark@15.0.1: + dependencies: + '@types/mdast': 4.0.4 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + repeat-string@1.6.1: {} request-promise-core@1.1.4(request@2.88.2): dependencies: - lodash: 4.17.23 + lodash: 4.18.1 request: 2.88.2 request-promise@4.2.6(request@2.88.2): @@ -11453,8 +11463,9 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: + es-errors: 1.3.0 is-core-module: '@nolyfill/is-core-module@1.0.39' path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -11469,83 +11480,100 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - rettime@0.10.1: {} + rettime@0.11.7: {} rfc4648@1.5.4: {} rfdc@1.4.1: {} - rolldown-plugin-dts@0.23.2(rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2))(typescript@5.9.3): + rolldown-plugin-dts@0.25.0(rolldown@1.0.0)(typescript@5.9.3): dependencies: - '@babel/generator': 8.0.0-rc.3 - '@babel/helper-validator-identifier': 8.0.0-rc.3 - '@babel/parser': 8.0.0-rc.3 - '@babel/types': 8.0.0-rc.3 + '@babel/generator': 8.0.0-rc.4 + '@babel/helper-validator-identifier': 8.0.0-rc.4 + '@babel/parser': 8.0.0-rc.4 ast-kit: 3.0.0-beta.1 birpc: 4.0.0 - dts-resolver: 2.1.3 - get-tsconfig: 4.13.7 + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 obug: 2.1.1 - picomatch: 4.0.4 - rolldown: 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2) + rolldown: 1.0.0 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - oxc-resolver - rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2): + rolldown@1.0.0: dependencies: - '@oxc-project/types': 0.122.0 - '@rolldown/pluginutils': 1.0.0-rc.12 + '@oxc-project/types': 0.129.0 + '@rolldown/pluginutils': 1.0.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.12 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.12 - '@rolldown/binding-darwin-x64': 1.0.0-rc.12 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.12 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.12 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.12 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.12 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.12 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.12 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.12 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.12 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.12 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.12 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.12 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - rollup@4.60.1: + '@rolldown/binding-android-arm64': 1.0.0 + '@rolldown/binding-darwin-arm64': 1.0.0 + '@rolldown/binding-darwin-x64': 1.0.0 + '@rolldown/binding-freebsd-x64': 1.0.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0 + '@rolldown/binding-linux-arm64-gnu': 1.0.0 + '@rolldown/binding-linux-arm64-musl': 1.0.0 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0 + '@rolldown/binding-linux-s390x-gnu': 1.0.0 + '@rolldown/binding-linux-x64-gnu': 1.0.0 + '@rolldown/binding-linux-x64-musl': 1.0.0 + '@rolldown/binding-openharmony-arm64': 1.0.0 + '@rolldown/binding-wasm32-wasi': 1.0.0 + '@rolldown/binding-win32-arm64-msvc': 1.0.0 + '@rolldown/binding-win32-x64-msvc': 1.0.0 + + 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 + + rollup@4.60.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.1 - '@rollup/rollup-android-arm64': 4.60.1 - '@rollup/rollup-darwin-arm64': 4.60.1 - '@rollup/rollup-darwin-x64': 4.60.1 - '@rollup/rollup-freebsd-arm64': 4.60.1 - '@rollup/rollup-freebsd-x64': 4.60.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 - '@rollup/rollup-linux-arm-musleabihf': 4.60.1 - '@rollup/rollup-linux-arm64-gnu': 4.60.1 - '@rollup/rollup-linux-arm64-musl': 4.60.1 - '@rollup/rollup-linux-loong64-gnu': 4.60.1 - '@rollup/rollup-linux-loong64-musl': 4.60.1 - '@rollup/rollup-linux-ppc64-gnu': 4.60.1 - '@rollup/rollup-linux-ppc64-musl': 4.60.1 - '@rollup/rollup-linux-riscv64-gnu': 4.60.1 - '@rollup/rollup-linux-riscv64-musl': 4.60.1 - '@rollup/rollup-linux-s390x-gnu': 4.60.1 - '@rollup/rollup-linux-x64-gnu': 4.60.1 - '@rollup/rollup-linux-x64-musl': 4.60.1 - '@rollup/rollup-openbsd-x64': 4.60.1 - '@rollup/rollup-openharmony-arm64': 4.60.1 - '@rollup/rollup-win32-arm64-msvc': 4.60.1 - '@rollup/rollup-win32-ia32-msvc': 4.60.1 - '@rollup/rollup-win32-x64-gnu': 4.60.1 - '@rollup/rollup-win32-x64-msvc': 4.60.1 + '@rollup/rollup-android-arm-eabi': 4.60.3 + '@rollup/rollup-android-arm64': 4.60.3 + '@rollup/rollup-darwin-arm64': 4.60.3 + '@rollup/rollup-darwin-x64': 4.60.3 + '@rollup/rollup-freebsd-arm64': 4.60.3 + '@rollup/rollup-freebsd-x64': 4.60.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 + '@rollup/rollup-linux-arm-musleabihf': 4.60.3 + '@rollup/rollup-linux-arm64-gnu': 4.60.3 + '@rollup/rollup-linux-arm64-musl': 4.60.3 + '@rollup/rollup-linux-loong64-gnu': 4.60.3 + '@rollup/rollup-linux-loong64-musl': 4.60.3 + '@rollup/rollup-linux-ppc64-gnu': 4.60.3 + '@rollup/rollup-linux-ppc64-musl': 4.60.3 + '@rollup/rollup-linux-riscv64-gnu': 4.60.3 + '@rollup/rollup-linux-riscv64-musl': 4.60.3 + '@rollup/rollup-linux-s390x-gnu': 4.60.3 + '@rollup/rollup-linux-x64-gnu': 4.60.3 + '@rollup/rollup-linux-x64-musl': 4.60.3 + '@rollup/rollup-openbsd-x64': 4.60.3 + '@rollup/rollup-openharmony-arm64': 4.60.3 + '@rollup/rollup-win32-arm64-msvc': 4.60.3 + '@rollup/rollup-win32-ia32-msvc': 4.60.3 + '@rollup/rollup-win32-x64-gnu': 4.60.3 + '@rollup/rollup-win32-x64-msvc': 4.60.3 fsevents: 2.3.3 rss-parser@3.13.0(patch_hash=afac79a31a3db94c953d49680bc5528468f051957d461e913d2e2dbf5cd22a8d): @@ -11559,14 +11587,14 @@ snapshots: safe-stable-stringify@2.5.0: {} - sanitize-html@2.17.2: + sanitize-html@2.17.3: dependencies: deepmerge: 4.3.1 escape-string-regexp: 4.0.0 htmlparser2: 10.1.0 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.5.8 + postcss: 8.5.10 sax@1.6.0: {} @@ -11578,8 +11606,14 @@ snapshots: dependencies: parseley: 0.12.1 + selderee@0.12.0: + dependencies: + parseley: 0.13.1 + semver@7.7.4: {} + set-cookie-parser@3.1.0: {} + sharp@0.34.5: dependencies: '@img/colour': 1.1.0 @@ -11623,11 +11657,6 @@ snapshots: simplecc-wasm@1.1.1: {} - sleep@6.1.0: - dependencies: - nan: 2.26.2 - optional: true - slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 @@ -11659,13 +11688,18 @@ snapshots: dependencies: agent-base: 7.1.4 debug: 4.4.3 - socks: 2.8.7 + socks: 2.8.8 transitivePeerDependencies: - supports-color socks@2.8.7: dependencies: - ip-address: 10.1.0 + ip-address: 10.2.0 + smart-buffer: 4.2.0 + + socks@2.8.8: + dependencies: + ip-address: 10.2.0 smart-buffer: 4.2.0 sonic-boom@4.2.1: @@ -11709,7 +11743,7 @@ snapshots: statuses@2.0.2: {} - std-env@3.10.0: {} + std-env@4.1.0: {} stealthy-require@1.1.1: {} @@ -11719,15 +11753,6 @@ snapshots: dependencies: bluebird: 2.11.0 - streamx@2.25.0: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - strict-event-emitter@0.5.1: {} string-argv@0.3.2: {} @@ -11752,7 +11777,7 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 - string-width@8.2.0: + string-width@8.2.1: dependencies: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 @@ -11797,30 +11822,7 @@ snapshots: tagged-tag@1.0.0: {} - tapable@2.3.2: {} - - tar-fs@3.1.2: - dependencies: - pump: 3.0.4 - tar-stream: 3.1.8 - optionalDependencies: - bare-fs: 4.5.6 - bare-path: 3.0.0 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - tar-stream@3.1.8: - dependencies: - b4a: 1.8.0 - bare-fs: 4.5.6 - fast-fifo: 1.3.2 - streamx: 2.25.0 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a + tapable@2.3.3: {} tar@7.5.13: dependencies: @@ -11830,13 +11832,6 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - teex@1.0.1: - dependencies: - streamx: 2.25.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - telegram@2.26.22: dependencies: '@cryptography/aes': 0.1.1 @@ -11859,12 +11854,6 @@ snapshots: transitivePeerDependencies: - supports-color - text-decoder@1.2.7: - dependencies: - b4a: 1.8.0 - transitivePeerDependencies: - - react-native-b4a - text-hex@1.0.0: {} text-table@0.2.0: {} @@ -11873,8 +11862,6 @@ snapshots: dependencies: real-require: 0.2.0 - through@2.3.8: {} - timers-ext@0.1.8: dependencies: es5-ext: 0.10.64 @@ -11882,14 +11869,9 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} - - tinyexec@1.0.4: {} + tinyexec@1.1.1: {} - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + tinyexec@1.1.2: {} tinyglobby@0.2.16: dependencies: @@ -11908,11 +11890,11 @@ snapshots: tlds@1.261.0: {} - tldts-core@7.0.28: {} + tldts-core@7.0.30: {} - tldts@7.0.28: + tldts@7.0.30: dependencies: - tldts-core: 7.0.28 + tldts-core: 7.0.30 to-no-case@1.0.2: {} @@ -11933,7 +11915,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.0.28 + tldts: 7.0.30 tr46@0.0.3: {} @@ -11961,6 +11943,7 @@ snapshots: dependencies: picomatch: 4.0.4 typescript: 5.9.3 + optional: true ts-xor@1.3.0: {} @@ -11968,33 +11951,31 @@ snapshots: optionalDependencies: typescript: 5.9.3 - tsdown@0.21.7(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)(synckit@0.11.12)(typescript@5.9.3): + tsdown@0.22.0(tsx@4.21.0)(typescript@5.9.3)(unrun@0.2.37(synckit@0.11.12)): dependencies: ansis: 4.2.0 cac: 7.0.0 - defu: 6.1.4 - empathic: 2.0.0 - hookable: 6.1.0 - import-without-cache: 0.2.5 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 obug: 2.1.1 picomatch: 4.0.4 - rolldown: 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2) - rolldown-plugin-dts: 0.23.2(rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2))(typescript@5.9.3) + rolldown: 1.0.0 + rolldown-plugin-dts: 0.25.0(rolldown@1.0.0)(typescript@5.9.3) semver: 7.7.4 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 tree-kill: 1.2.2 unconfig-core: 7.5.0 - unrun: 0.2.34(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)(synckit@0.11.12) optionalDependencies: + tsx: 4.21.0 typescript: 5.9.3 + unrun: 0.2.37(synckit@0.11.12) transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@ts-macro/tsc' - '@typescript/native-preview' - oxc-resolver - - synckit - vue-tsc tslib@1.14.1: {} @@ -12014,7 +11995,7 @@ snapshots: tunnel@0.0.6: {} - turndown@7.2.2: + turndown@7.2.4: dependencies: '@mixmark-io/domino': 2.2.0 @@ -12036,9 +12017,11 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type@2.7.3: {} + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 - typed-query-selector@2.12.1: {} + type@2.7.3: {} typedarray-to-buffer@3.1.5: dependencies: @@ -12048,29 +12031,26 @@ snapshots: uc.micro@2.1.0: {} - ufo@1.6.3: {} + ufo@1.6.4: {} - ultron@1.0.2: {} + uint8array-extras@1.5.0: {} - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 + ultron@1.0.2: {} unconfig-core@7.5.0: dependencies: '@quansync/fs': 1.0.0 quansync: 1.0.0 - undici-types@7.18.2: {} + undici-types@7.19.2: {} undici-types@7.24.6: {} - undici@6.24.1: {} + undici@6.25.0: {} - undici@7.24.4: {} + undici@7.24.8: {} - undici@7.24.7: {} + undici@7.25.0: {} unenv@2.0.0-rc.24: dependencies: @@ -12086,10 +12066,38 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unist-util-is@4.1.0: {} + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 + unist-util-visit-parents@3.1.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@2.0.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.1 + universal-user-agent@7.0.3: {} universalify@0.2.0: {} @@ -12120,14 +12128,12 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - unrun@0.2.34(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2)(synckit@0.11.12): + unrun@0.2.37(synckit@0.11.12): dependencies: - rolldown: 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.2) + rolldown: 1.0.0-rc.17 optionalDependencies: synckit: 0.11.12 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + optional: true until-async@3.0.2: {} @@ -12153,8 +12159,6 @@ snapshots: url-template@2.0.8: {} - urlpattern-polyfill@10.0.0: {} - utf-8-validate@1.1.0: dependencies: bindings: 1.2.1 @@ -12169,7 +12173,7 @@ snapshots: utility-types@3.11.0: {} - uuid@13.0.0: {} + uuid@14.0.0: {} uuid@3.4.0: {} @@ -12193,71 +12197,61 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)): + vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4) transitivePeerDependencies: - supports-color - typescript - vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3): + vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 - rollup: 4.60.1 - tinyglobby: 0.2.15 + postcss: 8.5.14 + rollup: 4.60.3 + tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.2 fsevents: 2.3.3 jiti: 2.6.1 tsx: 4.21.0 - yaml: 2.8.3 - - vitest@4.0.9(@edge-runtime/vm@3.2.0)(@types/debug@4.1.13)(@types/node@25.5.2)(jiti@2.6.1)(jsdom@29.0.2(@noble/hashes@2.0.1))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(tsx@4.21.0)(yaml@2.8.3): - dependencies: - '@vitest/expect': 4.0.9 - '@vitest/mocker': 4.0.9(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.0.9 - '@vitest/runner': 4.0.9 - '@vitest/snapshot': 4.0.9 - '@vitest/spy': 4.0.9 - '@vitest/utils': 4.0.9 - debug: 4.4.3 - es-module-lexer: 1.7.0 + yaml: 2.8.4 + + vitest@4.1.5(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.1.1(@noble/hashes@2.0.1))(msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)): + dependencies: + '@vitest/expect': 4.1.5 + '@vitest/mocker': 4.1.5(msw@2.13.4(@types/node@25.6.2)(typescript@5.9.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/pretty-format': 4.1.5 + '@vitest/runner': 4.1.5 + '@vitest/snapshot': 4.1.5 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.4 - std-env: 3.10.0 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 7.3.1(@types/node@25.6.2)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: '@edge-runtime/vm': 3.2.0 - '@types/debug': 4.1.13 - '@types/node': 25.5.2 - jsdom: 29.0.2(@noble/hashes@2.0.1) + '@opentelemetry/api': 1.9.1 + '@types/node': 25.6.2 + '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) + jsdom: 29.1.1(@noble/hashes@2.0.1) transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml w3c-xmlserializer@5.0.0: dependencies: @@ -12332,31 +12326,37 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260405.1: + workerd@1.20260507.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260405.1 - '@cloudflare/workerd-darwin-arm64': 1.20260405.1 - '@cloudflare/workerd-linux-64': 1.20260405.1 - '@cloudflare/workerd-linux-arm64': 1.20260405.1 - '@cloudflare/workerd-windows-64': 1.20260405.1 + '@cloudflare/workerd-darwin-64': 1.20260507.1 + '@cloudflare/workerd-darwin-arm64': 1.20260507.1 + '@cloudflare/workerd-linux-64': 1.20260507.1 + '@cloudflare/workerd-linux-arm64': 1.20260507.1 + '@cloudflare/workerd-windows-64': 1.20260507.1 - wrangler@4.81.0(@cloudflare/workers-types@4.20260409.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): + wrangler@4.90.0(@cloudflare/workers-types@4.20260508.1)(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: - '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260405.1) + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260507.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260405.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + miniflare: 4.20260507.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.20260405.1 + workerd: 1.20260507.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260409.1 + '@cloudflare/workers-types': 4.20260508.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil - utf-8-validate + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -12381,8 +12381,6 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 - wrappy@1.0.2: {} - write-file-atomic@1.3.4: dependencies: graceful-fs: 4.2.11 @@ -12409,7 +12407,7 @@ snapshots: wuzzy@0.1.8: dependencies: - lodash: 4.17.23 + lodash: 4.18.1 xml-name-validator@5.0.0: {} @@ -12424,10 +12422,6 @@ snapshots: xtend@4.0.2: {} - xvfb@0.4.0: - optionalDependencies: - sleep: 6.1.0 - xxhash-wasm@1.1.0: {} y18n@5.0.8: {} @@ -12443,6 +12437,8 @@ snapshots: yaml@2.8.3: {} + yaml@2.8.4: {} + yargs-parser@21.1.1: {} yargs-parser@22.0.0: {} @@ -12457,11 +12453,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.3: {} @@ -12491,8 +12482,6 @@ snapshots: '@bufbuild/protobuf': 2.11.0 meriyah: 6.1.4 - zod@3.23.8: {} - - zod@3.25.76: {} + zod@4.4.3: {} - zod@4.3.6: {} + zwitch@2.0.4: {} diff --git a/scripts/ansible/rsshub.env b/scripts/ansible/rsshub.env index d4a283725c66..e8bf0d7ffb60 100644 --- a/scripts/ansible/rsshub.env +++ b/scripts/ansible/rsshub.env @@ -1,3 +1,3 @@ NODE_ENV=production CACHE_TYPE=redis -PUPPETEER_WS_ENDPOINT=ws://localhost:3000 +PLAYWRIGHT_WS_ENDPOINT=ws://localhost:3000 diff --git a/scripts/ansible/rsshub.yaml b/scripts/ansible/rsshub.yaml index bfe6944df12a..a20900d27970 100644 --- a/scripts/ansible/rsshub.yaml +++ b/scripts/ansible/rsshub.yaml @@ -87,7 +87,7 @@ args: chdir: /home/rsshub/app environment: - PUPPETEER_SKIP_DOWNLOAD: true + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true - name: Copy configuration copy: src: rsshub.env diff --git a/scripts/workflow/build-routes.ts b/scripts/workflow/build-routes.ts index 97411284fb65..284a39c2d5b8 100644 --- a/scripts/workflow/build-routes.ts +++ b/scripts/workflow/build-routes.ts @@ -108,12 +108,12 @@ fs.mkdirSync(buildDir, { recursive: true }); // For Worker build, only output routes-worker.js with filtered namespaces // For regular build, output all files if (isWorkerBuild) { - fs.writeFileSync(path.join(__dirname, '../../assets/build/routes-worker.js'), `export default ${JSON.stringify(namespacesToProcess, null, 2)}`.replaceAll(/"module": "(.*)"\n/g, `"module": $1\n`)); + fs.writeFileSync(path.join(__dirname, '../../assets/build/routes-worker.js'), `export default ${JSON.stringify(namespacesToProcess, null, 2)}`.replaceAll(/"module": "(.*)"\n/g, '"module": $1\n')); } else { fs.writeFileSync(path.join(__dirname, '../../assets/build/radar-rules.json'), JSON.stringify(radar, null, 2)); fs.writeFileSync(path.join(__dirname, '../../assets/build/radar-rules.js'), `(${toSource(radar)})`); fs.writeFileSync(path.join(__dirname, '../../assets/build/maintainers.json'), JSON.stringify(maintainers, null, 2)); fs.writeFileSync(path.join(__dirname, '../../assets/build/routes.json'), JSON.stringify(namespaces, null, 2)); - fs.writeFileSync(path.join(__dirname, '../../assets/build/routes.js'), `export default ${JSON.stringify(namespaces, null, 2)}`.replaceAll(/"module": "(.*)"\n/g, `"module": $1\n`)); + fs.writeFileSync(path.join(__dirname, '../../assets/build/routes.js'), `export default ${JSON.stringify(namespaces, null, 2)}`.replaceAll(/"module": "(.*)"\n/g, '"module": $1\n')); fs.writeFileSync(path.join(__dirname, '../../assets/build/route-paths.ts'), routePathsType); } diff --git a/scripts/workflow/data.ts b/scripts/workflow/data.ts index 24726828f798..14b7abd72d46 100644 --- a/scripts/workflow/data.ts +++ b/scripts/workflow/data.ts @@ -137,6 +137,12 @@ export const categories = [ en: 'Finance', zh: '金融', }, + { + icon: '⚽', + link: '/routes/sport', + en: 'Sport', + zh: '体育', + }, { icon: '🔍', link: '/routes/other', diff --git a/scripts/workflow/format-description.ts b/scripts/workflow/format-description.ts new file mode 100644 index 000000000000..a382aa186d60 --- /dev/null +++ b/scripts/workflow/format-description.ts @@ -0,0 +1,294 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import stringWidth from 'fast-string-width'; +import { remark } from 'remark'; +import remarkGfm from 'remark-gfm'; +import remarkPangu from 'remark-pangu'; +import typescript from 'typescript'; + +const __dirname = import.meta.dirname; +const routesDir = path.resolve(__dirname, '../../lib/routes'); + +function remarkDirectiveSpace() { + return (tree: any) => { + walkDirectiveAst(tree); + }; +} + +function walkDirectiveAst(node: any): void { + if (node.type === 'text' && typeof node.value === 'string') { + node.value = node.value.replaceAll(/^:::([A-Za-z][\w-]*)/gm, '::: $1'); + } + if (Array.isArray(node.children)) { + for (const child of node.children) { + walkDirectiveAst(child); + } + } +} + +// Without remark-directive, a `:::` line right after a list/table gets absorbed: +// lists swallow it as lazy continuation, GFM tables consume it as another row. +// Lift trailing `:::` out so it stringifies flush left. +function shouldLiftFromList(list: any): boolean { + const lastItem = list.children?.at(-1); + if (lastItem?.type !== 'listItem') { + return false; + } + const lastPara = lastItem.children?.at(-1); + if (lastPara?.type !== 'paragraph') { + return false; + } + const lastText = lastPara.children?.at(-1); + if (lastText?.type !== 'text') { + return false; + } + return (lastText.value as string).trimEnd().endsWith('\n:::'); +} + +function liftFromList(list: any): void { + const lastItem = list.children.at(-1); + const lastPara = lastItem.children.at(-1); + const lastText = lastPara.children.at(-1); + lastText.value = (lastText.value as string).trimEnd().slice(0, -4); + if (lastText.value === '') { + lastPara.children.pop(); + } + if (lastPara.children.length === 0) { + lastItem.children.pop(); + } +} + +function shouldLiftFromTable(table: any): boolean { + const lastRow = table.children?.at(-1); + if (lastRow?.type !== 'tableRow') { + return false; + } + const cells = lastRow.children; + if (!Array.isArray(cells) || cells.length === 0) { + return false; + } + const firstCell = cells[0]; + if (firstCell?.type !== 'tableCell' || firstCell.children?.length !== 1) { + return false; + } + const text = firstCell.children[0]; + if (text?.type !== 'text' || text.value !== ':::') { + return false; + } + for (let j = 1; j < cells.length; j++) { + if (cells[j]?.type !== 'tableCell' || cells[j].children?.length !== 0) { + return false; + } + } + return true; +} + +function visitForLift(parent: any): void { + if (!Array.isArray(parent.children)) { + return; + } + for (const child of parent.children) { + visitForLift(child); + } + for (let i = parent.children.length - 1; i >= 0; i--) { + const node = parent.children[i]; + let lifted = false; + if (node.type === 'list' && shouldLiftFromList(node)) { + liftFromList(node); + lifted = true; + } else if (node.type === 'table' && shouldLiftFromTable(node)) { + node.children.pop(); + lifted = true; + } + if (lifted) { + parent.children.splice(i + 1, 0, { + type: 'paragraph', + children: [{ type: 'text', value: ':::' }], + }); + } + } +} + +function remarkLiftClosingDirective() { + return (tree: any) => visitForLift(tree); +} + +const processor = remark() + .data('settings', { + bullet: '-', + }) + .use(remarkDirectiveSpace) + .use(remarkLiftClosingDirective) + .use(remarkPangu) + .use(remarkGfm, { + stringLength: stringWidth, + }); + +interface DescriptionEdit { + start: number; + end: number; + raw: string; +} + +function getPropertyName(name: typescript.PropertyName): string { + if (typescript.isIdentifier(name) || typescript.isPrivateIdentifier(name) || typescript.isStringLiteral(name) || typescript.isNoSubstitutionTemplateLiteral(name)) { + return name.text; + } + return ''; +} + +const TARGETS: Record = { route: 'Route', namespace: 'Namespace' }; + +function isTargetTypedDeclaration(decl: typescript.VariableDeclaration): boolean { + if (!typescript.isIdentifier(decl.name)) { + return false; + } + const expectedType = TARGETS[decl.name.text]; + if (!expectedType) { + return false; + } + if (decl.type && typescript.isTypeReferenceNode(decl.type)) { + const typeName = decl.type.typeName; + if (typescript.isIdentifier(typeName) && typeName.text === expectedType) { + return true; + } + } + return false; +} + +function collectDescriptionEdits(sourceFile: typescript.SourceFile): DescriptionEdit[] { + const edits: DescriptionEdit[] = []; + + const visitObject = (obj: typescript.ObjectLiteralExpression) => { + for (const prop of obj.properties) { + if (!typescript.isPropertyAssignment(prop)) { + continue; + } + const name = getPropertyName(prop.name); + if (name === 'description') { + const init = prop.initializer; + if (typescript.isStringLiteral(init) || typescript.isNoSubstitutionTemplateLiteral(init)) { + edits.push({ + start: init.getStart(sourceFile), + end: init.getEnd(), + raw: init.text, + }); + } + } else if ((name === 'ja' || name === 'zh' || name === 'zh-TW') && typescript.isObjectLiteralExpression(prop.initializer)) { + visitObject(prop.initializer); + } + } + }; + + for (const stmt of sourceFile.statements) { + if (!typescript.isVariableStatement(stmt)) { + continue; + } + const isExported = stmt.modifiers?.some((m) => m.kind === typescript.SyntaxKind.ExportKeyword); + if (!isExported) { + continue; + } + for (const decl of stmt.declarationList.declarations) { + if (!isTargetTypedDeclaration(decl)) { + continue; + } + if (decl.initializer && typescript.isObjectLiteralExpression(decl.initializer)) { + visitObject(decl.initializer); + } + } + } + + return edits; +} + +function escapeTemplateLiteral(s: string): string { + return s + .replaceAll('\\', String.raw`\\`) + .replaceAll('`', '\\`') + .replaceAll('${', '\\${'); +} + +async function* walk(dir: string): AsyncGenerator { + const entries = await fs.readdir(dir, { withFileTypes: true }); + for (const entry of entries) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + yield* walk(full); + } else if (entry.isFile() && /\.tsx?$/.test(entry.name) && !entry.name.endsWith('.d.ts')) { + yield full; + } + } +} + +async function processFile(filePath: string): Promise { + const sourceText = await fs.readFile(filePath, 'utf8'); + if (!sourceText.includes('description')) { + return; + } + + const sourceFile = typescript.createSourceFile(filePath, sourceText, typescript.ScriptTarget.Latest, false, filePath.endsWith('.tsx') ? typescript.ScriptKind.TSX : typescript.ScriptKind.TS); + + const edits = collectDescriptionEdits(sourceFile); + if (edits.length === 0) { + return; + } + + edits.sort((a, b) => b.start - a.start); + + let result = sourceText; + let changed = false; + + const formattedResults = await Promise.all( + edits.map(async (edit) => { + if (!edit.raw.trim()) { + return { edit, formatted: null as string | null }; + } + const file = await processor.process(edit.raw); + return { + edit, + formatted: String(file).replace(/\n+$/, ''), + }; + }) + ); + + for (const { edit, formatted } of formattedResults) { + if (!formatted || formatted === edit.raw) { + continue; + } + + const replacement = '`' + escapeTemplateLiteral(formatted) + '`'; // oxlint handles unncessary single line template literals. + result = result.slice(0, edit.start) + replacement + result.slice(edit.end); + changed = true; + } + + if (changed) { + await fs.writeFile(filePath, result, 'utf8'); + } +} + +function isRouteFile(filePath: string): boolean { + const abs = path.resolve(filePath); + if (!abs.startsWith(routesDir + path.sep)) { + return false; + } + const base = path.basename(abs); + return /\.tsx?$/.test(base) && !base.endsWith('.d.ts'); +} + +async function main() { + const started = performance.now(); + const args = process.argv.slice(2); + const files: string[] = + args.length > 0 + ? args.map((f) => path.resolve(f)).filter((f) => isRouteFile(f)) + : // @ts-ignore ts(2550) + await Array.fromAsync(walk(routesDir)); + + await Promise.all(files.map((f) => processFile(f))); + + // oxlint-disable-next-line no-console + console.log(`Finished in ${Math.round(performance.now() - started)}ms on ${files.length} files.`); +} + +await main(); diff --git a/scripts/workflow/vouch/check-pr.mjs b/scripts/workflow/vouch/check-pr.mjs new file mode 100644 index 000000000000..254e8b200bb4 --- /dev/null +++ b/scripts/workflow/vouch/check-pr.mjs @@ -0,0 +1,115 @@ +/** + * @param {{ github: ReturnType, context: typeof import('@actions/github').context, core: typeof import('@actions/core') }} githubScript + * @returns {Promise} + */ +export default async function checkPr({ github, context, core }) { + const author = context.payload.pull_request.user.login; + const prNumber = context.payload.pull_request.number; + + // Skip bots + if (author.endsWith('[bot]')) { + core.info(`Skipping bot: ${author}`); + return; + } + + // Read the VOUCHED.td file via API (no checkout needed) + let content; + try { + const response = await github.rest.repos.getContent({ + owner: context.repo.owner, + repo: context.repo.repo, + path: '.github/VOUCHED.td', + }); + content = Buffer.from(response.data.content, 'base64').toString('utf-8'); + } catch (error) { + if (error.status === 404) { + core.info('No .github/VOUCHED.td file found, skipping check.'); + return; + } + throw error; + } + + // Parse the .td file for vouched and denounced users + const vouched = new Set(); + const denounced = new Map(); + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) { + continue; + } + + const isDenounced = trimmed.startsWith('-'); + const rest = isDenounced ? trimmed.slice(1).trim() : trimmed; + if (!rest) { + continue; + } + + const spaceIdx = rest.indexOf(' '); + const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx); + const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim(); + + // Handle platform:username or bare username + // Only match bare usernames or github: prefix (skip other platforms) + const colonIdx = handle.indexOf(':'); + if (colonIdx !== -1) { + const platform = handle.slice(0, colonIdx).toLowerCase(); + if (platform !== 'github') { + continue; + } + } + const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1); + if (!username) { + continue; + } + + if (isDenounced) { + denounced.set(username.toLowerCase(), reason); + continue; + } + + vouched.add(username.toLowerCase()); + } + + // Check if the author is denounced + const reason = denounced.get(author.toLowerCase()); + if (reason !== undefined) { + // Author is denounced — close the PR + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: 'This pull request has been automatically closed.', + }); + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + state: 'closed', + }); + + core.info(`Closed PR #${prNumber} from denounced user ${author}`); + return; + } + + // Author is positively vouched — add label + if (!vouched.has(author.toLowerCase())) { + core.info(`User ${author} is not denounced or vouched. Allowing PR.`); + return; + } + + const association = context.payload.pull_request.author_association; + if (association === 'OWNER' || association === 'MEMBER' || association === 'COLLABORATOR') { + core.info(`Skipping vouched label for collaborator ${author} (${association}).`); + return; + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: ['vouched'], + }); + + core.info(`Added vouched label to PR #${prNumber} from ${author}`); +} diff --git a/tsdown-container.config.ts b/tsdown-container.config.ts index 362ba1291d80..30ecd0734fd2 100644 --- a/tsdown-container.config.ts +++ b/tsdown-container.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ entry: ['./lib/container.ts'], outDir: 'dist-container', format: 'esm', + dts: false, minify: true, clean: true, platform: 'node', @@ -12,8 +13,8 @@ export default defineConfig({ define: { 'process.env.NODE_ENV': JSON.stringify('production'), }, - external: ['@cloudflare/containers'], deps: { onlyBundle: false, + neverBundle: ['@cloudflare/containers'], }, }); diff --git a/tsdown-worker.config.ts b/tsdown-worker.config.ts index 979a5850b2dc..274d352b949a 100644 --- a/tsdown-worker.config.ts +++ b/tsdown-worker.config.ts @@ -62,6 +62,7 @@ export default defineConfig({ entry: ['./lib/worker.ts'], outDir: 'dist-worker', format: 'esm', + dts: false, minify: true, clean: true, platform: 'node', @@ -76,12 +77,6 @@ export default defineConfig({ __dirname: JSON.stringify('/worker'), __filename: JSON.stringify('/worker/index.mjs'), }, - external: [ - // Exclude non-code files that might be accidentally imported - /\/_README$/, - /\.node$/, - ], - noExternal: [/.*/], plugins: [workerAliasPlugin()], alias: { // External dependencies that need Worker-compatible replacements @@ -98,5 +93,11 @@ export default defineConfig({ }, deps: { onlyBundle: false, + neverBundle: [ + // Exclude non-code files that might be accidentally imported + /\/_README$/, + /\.node$/, + ], + alwaysBundle: [/.*/], }, }); diff --git a/wrangler-container.toml b/wrangler-container.toml index 3eaee9f8b297..8d3b445c2e2b 100644 --- a/wrangler-container.toml +++ b/wrangler-container.toml @@ -15,7 +15,7 @@ class_name = "RSSHubContainer" image = "./Dockerfile" max_instances = 20 instance_type = "standard-1" -image_vars = { PUPPETEER_SKIP_DOWNLOAD = "0" } +image_vars = { PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "0" } # Durable Object binding for the container [[durable_objects.bindings]] diff --git a/wrangler.toml b/wrangler.toml index c4b55db515a4..7412daf39238 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -16,7 +16,7 @@ command = "pnpm run worker-build" [observability] enabled = true -# Browser Rendering API for puppeteer support +# Browser Run API for Playwright support # Requires Workers Paid plan [browser] binding = "BROWSER"