diff --git a/.eslintignore b/.eslintignore index 5b176d430..c0bb49c95 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,7 @@ coverage .vscode docker-compose.yml !/.github -!/docs/.vuepress +lib/routes-deprecated +lib/router.js +babel.config.js +scripts/docker/minify-docker.js diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9aa571cd5..845d7d382 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,8 +11,6 @@ updates: ignore: - dependency-name: jsrsasign versions: ['>=11.0.0'] # no longer includes KJUR.crypto.Cipher for RSA - - dependency-name: unified - versions: ['>=10.0.0'] - package-ecosystem: 'github-actions' directory: '/' diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index bd725fea9..7ef73eafe 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -15,7 +15,7 @@ jobs: build: runs-on: ubuntu-latest name: Build assets - timeout-minutes: 5 + timeout-minutes: 60 permissions: contents: write steps: @@ -33,13 +33,9 @@ jobs: - name: Install dependencies (yarn) run: pnpm i - name: Build assets - run: npm run build - - name: Commit files - run: | - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git status - git diff-index --quiet HEAD || (git commit -m "chore: auto build" -a --no-verify && git push "https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git" HEAD:master) + run: pnpm build + - name: Build docs + run: pnpm build:docs - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: @@ -57,6 +53,8 @@ jobs: run: | cp -r ./assets/build/docs/en/* ./rsshub-docs/src/routes cp -r ./assets/build/docs/zh/* ./rsshub-docs/src/zh/routes + cp ./lib/types.ts ./rsshub-docs/.vitepress/theme/types.ts + cp ./scripts/workflow/data.ts ./rsshub-docs/.vitepress/config/data.ts - name: Commit docs run: | cd rsshub-docs diff --git a/.github/workflows/comment-on-issue.yml b/.github/workflows/comment-on-issue.yml index 3fea22cb6..9ef4dd561 100644 --- a/.github/workflows/comment-on-issue.yml +++ b/.github/workflows/comment-on-issue.yml @@ -15,16 +15,16 @@ jobs: - uses: pnpm/action-setup@v3 with: version: 8 - - uses: actions/setup-node@v4 # just need its cache + - uses: actions/setup-node@v4 with: node-version: lts/* cache: 'pnpm' - - name: Install dependencies (pnpm) # needed since we need to parse markdown, so we also use got instead + - name: Install dependencies (pnpm) # import remark-parse and unified run: pnpm i - name: Generate feedback uses: actions/github-script@v7 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-issue/call-maintainer.js`) - return script({ github, context, core }) + const { default: callMaintainer } = await import('${{ github.workspace }}/scripts/workflow/test-issue/call-maintainer.mjs') + await callMaintainer({ github, context, core }) diff --git a/.github/workflows/docker-test-cont.yml b/.github/workflows/docker-test-cont.yml index 05f9f6f00..4ab535a2d 100644 --- a/.github/workflows/docker-test-cont.yml +++ b/.github/workflows/docker-test-cont.yml @@ -2,14 +2,14 @@ name: PR - route test on: workflow_run: - workflows: [ PR - Docker build test ] # open, reopen, synchronized, edited included - types: [ completed ] + workflows: [PR - Docker build test] # open, reopen, synchronized, edited included + types: [completed] jobs: testRoute: name: Route test runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful + if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful steps: - uses: actions/checkout@v4 @@ -44,8 +44,8 @@ jobs: const body = PR.body const number = PR.number const sender = PR.user.login - const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/identify.js`) - return script({ github, context, core }, body, number, sender) + const { default: identify } = await import('${{ github.workspace }}/scripts/workflow/test-route/identify.mjs') + return identify({ github, context, core }, body, number, sender) - name: Fetch Docker image if: (env.TEST_CONTINUE) @@ -72,13 +72,13 @@ jobs: with: version: 8 - - uses: actions/setup-node@v4 # just need its cache + - uses: actions/setup-node@v4 if: (env.TEST_CONTINUE) with: node-version: lts/* cache: 'pnpm' - - name: Install dependencies (pnpm) # `got` needed since `github.request` disallows HTTP requests + - name: Install dependencies (pnpm) # require js-beautify if: (env.TEST_CONTINUE) run: pnpm i @@ -99,9 +99,8 @@ jobs: const routes = JSON.parse(process.env.TEST_ROUTES) const number = PR.number core.info(`${link}, ${routes}, ${number}`) - const got = require("got") - const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/test.js`) - return script({ github, context, core, got }, link, routes, number) + const { default: test } = await import('${{ github.workspace }}/scripts/workflow/test-route/test.mjs') + await test({ github, context, core }, link, routes, number) - name: Pull Request Labeler if: ${{ failure() }} @@ -114,4 +113,4 @@ jobs: - name: Print Docker container logs if: (env.TEST_CONTINUE) - run: docker logs rsshub # logs/combined.log? Not so readable... + run: docker logs rsshub # logs/combined.log? Not so readable... diff --git a/.github/workflows/issue-command.yml b/.github/workflows/issue-command.yml index 1cc371618..18d84c591 100644 --- a/.github/workflows/issue-command.yml +++ b/.github/workflows/issue-command.yml @@ -77,8 +77,8 @@ jobs: const body = event.comment.body const number = event.issue.number const sender = event.comment.user.login - const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/identify.js`) - return script({ github, context, core }, body, number, sender) + const { default: identify } = await import('${{ github.workspace }}/scripts/workflow/test-route/identify.mjs') + return identify({ github, context, core }, body, number, sender) - name: Build RSSHub if: env.TEST_CONTINUE @@ -107,9 +107,8 @@ jobs: const routes = JSON.parse(process.env.TEST_ROUTES) const number = event.issue.number core.info(`${link}, ${routes}, ${number}`) - const got = require("got") - const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/test.js`) - return script({ github, context, core, got }, link, routes, number) + const { default: test } = await import('${{ github.workspace }}/scripts/workflow/test-route/test.mjs') + await test({ github, context, core }, link, routes, number) - name: Print logs if: (env.TEST_CONTINUE) diff --git a/.github/workflows/test-full-routes.yml b/.github/workflows/test-full-routes.yml new file mode 100644 index 000000000..9e342975d --- /dev/null +++ b/.github/workflows/test-full-routes.yml @@ -0,0 +1,44 @@ +name: Build assets + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + name: Build assets + timeout-minutes: 60 + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v3 + with: + version: 8 + - name: Use Node.js Active LTS + uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: 'pnpm' + - name: Install dependencies (yarn) + run: pnpm i + - name: Build assets + run: pnpm build + - name: Build full routes test result + continue-on-error: true + run: pnpm vitest:fullroutes + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./assets + user_name: 'github-actions[bot]' + user_email: '41898282+github-actions[bot]@users.noreply.github.com' + keep_files: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 523d0c80f..30a978ed7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,6 @@ on: branches-ignore: - 'dependabot/**' paths: - - 'test/**' - 'lib/**' - 'package.json' - 'pnpm-lock.yaml' @@ -43,6 +42,8 @@ jobs: run: pnpm i - name: Run postinstall script for dependencies run: pnpm rb + - name: Build routes + run: pnpm build - name: Test all and generate coverage run: pnpm run vitest:coverage env: @@ -84,6 +85,8 @@ jobs: run: pnpm i - name: Run postinstall script for dependencies run: pnpm rb + - name: Build routes + run: pnpm build - name: Install Chromium if: ${{ matrix.chromium.dependency != '' }} # 'chromium-browser' from Ubuntu APT repo is a dummy package. Its version (85.0.4183.83) means diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..b6b684c5a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +lib/routes-deprecated +lib/router.js +babel.config.js +scripts/docker/minify-docker.js diff --git a/.puppeteerrc.js b/.puppeteerrc.cjs similarity index 100% rename from .puppeteerrc.js rename to .puppeteerrc.cjs diff --git a/Dockerfile b/Dockerfile index 22a31a8bf..228cf0c49 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,7 +84,7 @@ FROM node:21-bookworm-slim AS chromium-downloader # Yeah, downloading Chromium never needs those dependencies below. WORKDIR /app -COPY ./.puppeteerrc.js /app/ +COPY ./.puppeteerrc.cjs /app/ COPY --from=dep-version-parser /ver/.puppeteer_version /app/.puppeteer_version ARG TARGETPLATFORM diff --git a/lib/middleware/cache.test.ts b/lib/middleware/cache.test.ts index bb526daab..7edce9fc5 100644 --- a/lib/middleware/cache.test.ts +++ b/lib/middleware/cache.test.ts @@ -1,23 +1,16 @@ -import { describe, expect, it, vi, afterEach, afterAll, beforeAll } from 'vitest'; +import { describe, expect, it, vi, afterEach } from 'vitest'; import Parser from 'rss-parser'; import wait from '@/utils/wait'; +process.env.CACHE_EXPIRE = '1'; +process.env.CACHE_CONTENT_EXPIRE = '3'; + const parser = new Parser(); -beforeAll(() => { - process.env.CACHE_EXPIRE = '1'; - process.env.CACHE_CONTENT_EXPIRE = '3'; -}); - afterEach(() => { - delete process.env.CACHE_TYPE; vi.resetModules(); }); -afterAll(() => { - delete process.env.CACHE_EXPIRE; -}); - describe('cache', () => { it('memory', async () => { process.env.CACHE_TYPE = 'memory'; diff --git a/lib/registry.test.ts b/lib/registry.test.ts index ebc5c7526..d168eb599 100644 --- a/lib/registry.test.ts +++ b/lib/registry.test.ts @@ -1,55 +1,8 @@ -import { describe, expect, it, afterAll } from 'vitest'; -process.env.SOCKET = 'socket'; - +import { describe, expect, it } from 'vitest'; import app from '@/app'; -import Parser from 'rss-parser'; -const parser = new Parser(); import { config } from '@/config'; -afterAll(() => { - delete process.env.SOCKET; -}); - -async function checkRSS(response) { - const checkDate = (date) => { - expect(date).toEqual(expect.any(String)); - expect(Date.parse(date)).toEqual(expect.any(Number)); - expect(Date.now() - +new Date(date)).toBeGreaterThan(-1000 * 60 * 60 * 24 * 5); - expect(Date.now() - +new Date(date)).toBeLessThan(1000 * 60 * 60 * 24 * 30 * 12 * 10); - }; - - const parsed = await parser.parseString(await response.text()); - - expect(parsed).toEqual(expect.any(Object)); - expect(parsed.title).toEqual(expect.any(String)); - expect(parsed.title).not.toBe('RSSHub'); - expect(parsed.description).toEqual(expect.any(String)); - expect(parsed.link).toEqual(expect.any(String)); - expect(parsed.lastBuildDate).toEqual(expect.any(String)); - expect(parsed.ttl).toEqual(Math.trunc(config.cache.routeExpire / 60) + ''); - expect(parsed.items).toEqual(expect.any(Array)); - checkDate(parsed.lastBuildDate); - - // check items - const guids: (string | undefined)[] = []; - for (const item of parsed.items) { - expect(item).toEqual(expect.any(Object)); - expect(item.title).toEqual(expect.any(String)); - expect(item.link).toEqual(expect.any(String)); - expect(item.content).toEqual(expect.any(String)); - expect(item.guid).toEqual(expect.any(String)); - if (item.pubDate) { - expect(item.pubDate).toEqual(expect.any(String)); - checkDate(item.pubDate); - } - - // guid must be unique - expect(guids).not.toContain(item.guid); - guids.push(item.guid); - } -} - -describe('router', () => { +describe('registry', () => { // root it(`/`, async () => { const response = await app.request('/'); @@ -58,14 +11,6 @@ describe('router', () => { expect(response.headers.get('cache-control')).toBe('no-cache'); }); - // route - it(`/test/1`, async () => { - const response = await app.request('/test/1'); - expect(response.status).toBe(200); - - await checkRSS(response); - }); - // robots.txt it('/robots.txt', async () => { config.disallowRobot = false; diff --git a/lib/registry.ts b/lib/registry.ts index 0fef532ae..74101502d 100644 --- a/lib/registry.ts +++ b/lib/registry.ts @@ -7,8 +7,6 @@ import { serveStatic } from '@hono/node-server/serve-static'; import index from '@/routes/index'; import robotstxt from '@/routes/robots.txt'; -import { namespace as testNamespace } from './routes/test/namespace'; -import { route as testRoute } from '@/routes/test/index'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -27,18 +25,9 @@ let namespaces: Record< switch (process.env.NODE_ENV) { case 'test': - modules = { - '/test/namespace.ts': { - namespace: testNamespace, - }, - '/test/index.ts': { - route: testRoute, - }, - }; - break; case 'production': - // eslint-disable-next-line n/no-unpublished-require - namespaces = require('../assets/build/routes.json'); + // @ts-expect-error + namespaces = await import('../assets/build/routes.json'); break; default: modules = directoryImport({ diff --git a/lib/routes.test.ts b/lib/routes.test.ts new file mode 100644 index 000000000..20fedf119 --- /dev/null +++ b/lib/routes.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from 'vitest'; +import app from '@/app'; +import Parser from 'rss-parser'; +const parser = new Parser(); +import { config } from '@/config'; + +process.env.ALLOW_USER_SUPPLY_UNSAFE_DOMAIN = 'true'; + +const routes = { + '/test/:id': '/test/1', +}; +if (process.env.FULL_ROUTES_TEST) { + const { namespaces } = await import('@/registry'); + for (const namespace in namespaces) { + for (const route in namespaces[namespace].routes) { + const requireConfig = namespaces[namespace].routes[route].features?.requireConfig; + let configs; + if (typeof requireConfig !== 'boolean') { + configs = requireConfig + ?.filter((config) => !config.optional) + .map((config) => config.name) + .filter((name) => name !== 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN'); + } + if (namespaces[namespace].routes[route].example && !configs?.length) { + routes[`/${namespace}${route}`] = namespaces[namespace].routes[route].example; + } + } + } +} + +async function checkRSS(response) { + const checkDate = (date) => { + expect(date).toEqual(expect.any(String)); + expect(Date.parse(date)).toEqual(expect.any(Number)); + expect(Date.now() - +new Date(date)).toBeGreaterThan(-1000 * 60 * 60 * 24 * 5); + expect(Date.now() - +new Date(date)).toBeLessThan(1000 * 60 * 60 * 24 * 30 * 12 * 10); + }; + + const parsed = await parser.parseString(await response.text()); + + expect(parsed).toEqual(expect.any(Object)); + expect(parsed.title).toEqual(expect.any(String)); + expect(parsed.title).not.toBe('RSSHub'); + expect(parsed.description).toEqual(expect.any(String)); + expect(parsed.link).toEqual(expect.any(String)); + expect(parsed.lastBuildDate).toEqual(expect.any(String)); + expect(parsed.ttl).toEqual(Math.trunc(config.cache.routeExpire / 60) + ''); + expect(parsed.items).toEqual(expect.any(Array)); + checkDate(parsed.lastBuildDate); + + // check items + const guids: (string | undefined)[] = []; + for (const item of parsed.items) { + expect(item).toEqual(expect.any(Object)); + expect(item.title).toEqual(expect.any(String)); + expect(item.link).toEqual(expect.any(String)); + expect(item.content).toEqual(expect.any(String)); + expect(item.guid).toEqual(expect.any(String)); + if (item.pubDate) { + expect(item.pubDate).toEqual(expect.any(String)); + checkDate(item.pubDate); + } + + // guid must be unique + expect(guids).not.toContain(item.guid); + guids.push(item.guid); + } +} + +describe('routes', () => { + for (const route in routes) { + it.concurrent(route, async () => { + const response = await app.request(routes[route]); + expect(response.status).toBe(200); + await checkRSS(response); + }); + } +}); diff --git a/lib/routes/2023game/index.ts b/lib/routes/2023game/index.ts index ab94ff406..55d900fd0 100644 --- a/lib/routes/2023game/index.ts +++ b/lib/routes/2023game/index.ts @@ -31,9 +31,9 @@ export const route: Route = { | 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 { - const category = (ctx!.req.param('category') ?? 'sgame').replaceAll('.', '/'); - const tab = ctx?.req.param('tab') ?? 'all'; +async function handler(ctx: Context): Promise { + const category = (ctx.req.param('category') ?? 'sgame').replaceAll('.', '/'); + const tab = ctx.req.param('tab') ?? 'all'; const currentUrl = `https://www.2023game.com/${category}/`; diff --git a/lib/routes/afdian/dynamic.ts b/lib/routes/afdian/dynamic.ts index f69f6edc1..2819a48a9 100644 --- a/lib/routes/afdian/dynamic.ts +++ b/lib/routes/afdian/dynamic.ts @@ -1,4 +1,5 @@ import got from '@/utils/got'; +import type { Route } from '@/types'; export const route: Route = { path: '/dynamic/:uid?', diff --git a/lib/routes/appstorrent/namespace.ts b/lib/routes/appstorrent/namespace.ts new file mode 100644 index 000000000..3b5b12a00 --- /dev/null +++ b/lib/routes/appstorrent/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'AppsTorrent', + url: 'appstorrent.ru', +}; diff --git a/lib/routes/appstorrent/programs.ts b/lib/routes/appstorrent/programs.ts new file mode 100644 index 000000000..ad9d40b86 --- /dev/null +++ b/lib/routes/appstorrent/programs.ts @@ -0,0 +1,91 @@ +import { Data, DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import got, { Options } from '@/utils/got'; +import { getCurrentPath } from '@/utils/helpers'; +import { parseDate } from '@/utils/parse-date'; +import { art } from '@/utils/render'; +import { load } from 'cheerio'; +import dayjs from 'dayjs'; +import { Context } from 'hono'; +import * as path from 'node:path'; +const __dirname = getCurrentPath(import.meta.url); + +export const route: Route = { + path: '/programs', + categories: ['program-update'], + example: '/appstorrent/programs', + name: 'Programs', + maintainers: ['xzzpig'], + handler, + url: 'appstorrent.ru/programs/', +}; + +async function handler(ctx?: Context): Promise { + const limit = ctx?.req.query('limit') ? Number.parseInt(ctx?.req.query('limit') ?? '20') : 20; + const baseUrl = 'https://appstorrent.ru'; + const currentUrl = `${baseUrl}/programs/`; + const gotOptions: Options = { + http2: true, + }; + + const response = await got(currentUrl, gotOptions); + const $ = load(response.data as any); + + const selector = 'article.soft-item:not(.locked)'; + const list = $(selector) + .slice(0, limit) + .toArray() + .map((item) => { + const $item = $(item); + return { + title: $item.find('.subtitle').text().trim(), + link: $item.find('.subtitle a').attr('href')!, + category: [$item.find('.info .category').text().trim()], + version: $item.find('.version').text(), + architecture: $item.find('.architecture').text().trim(), + size: $item.find('.size').text().trim(), + }; + }); + + const items: DataItem[] = await Promise.all( + list.map( + (item) => + cache.tryGet(item.link, async () => { + const response = await got(item.link, gotOptions); + const $ = load(response.data as any); + + const pubDate = parseDate($('.tech-info .date-news a').attr('href')?.replace('https://appstorrent.ru/', '') ?? ''); + + return { + title: item.title, + link: item.link, + category: item.category, + pubDate, + description: art(path.join(__dirname, 'templates/description.art'), { + cover: baseUrl + $('.main-title img').attr('src')?.trim(), + title: item.title, + pubDate: dayjs(pubDate).format('YYYY-MM-DD'), + version: item.version, + architecture: item.architecture, + compatibility: $('div.right > div.info > div.right-container > div:nth-child(5) > div > span:nth-child(2) > a').text(), + size: item.size, + activation: $('div.right > div.info > div.right-container > div:nth-child(4) > div > span:nth-child(2) > a').text(), + description: $('.content .body-content').first().text(), + changelog: $('.content .body-content').last().text(), + screenshots: $('.screenshots img') + .toArray() + .map((img) => $(img).attr('src')) + .map((src) => baseUrl + src), + }), + } as DataItem; + }) as Promise + ) + ); + + return { + title: $('title').text(), + link: currentUrl.toString(), + allowEmpty: true, + item: items, + }; +} diff --git a/lib/routes/appstorrent/templates/description.art b/lib/routes/appstorrent/templates/description.art new file mode 100644 index 000000000..2e86e1682 --- /dev/null +++ b/lib/routes/appstorrent/templates/description.art @@ -0,0 +1,22 @@ +

+ +

{{ title }}


+Public Date: {{pubDate}}
+Version: {{version}}
+Architecture: {{architecture}}
+Compactibility: {{compatibility}}
+Size: {{size}}
+Activation: {{activation}}
+

+Description: +

+{{ description }} +

+Change Log: +

+{{ changelog }} +

+Screenshots +{{each screenshots}} + +{{/each}} \ No newline at end of file diff --git a/lib/routes/bilibili/dynamic.ts b/lib/routes/bilibili/dynamic.ts index 0420620a6..8e127506e 100644 --- a/lib/routes/bilibili/dynamic.ts +++ b/lib/routes/bilibili/dynamic.ts @@ -121,7 +121,6 @@ async function handler(ctx) { headers: { Referer: `https://space.bilibili.com/${uid}/`, }, - transformResponse: [(data) => data], }); const cards = JSONbig.parse(response.body).data.cards; diff --git a/lib/routes/bilibili/user-collection.ts b/lib/routes/bilibili/user-collection.ts index 56c2d961b..0d46ade10 100644 --- a/lib/routes/bilibili/user-collection.ts +++ b/lib/routes/bilibili/user-collection.ts @@ -3,6 +3,7 @@ import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; +import { queryToBoolean } from '@/utils/readable-social'; const notFoundData = { title: '此 bilibili 频道不存在', @@ -15,7 +16,7 @@ export const route: Route = { parameters: { uid: '用户 id, 可在 UP 主主页中找到', sid: '合集 id, 可在合集页面的 URL 中找到', - disableEmbed: '默认为开启内嵌视频, 任意值为关闭', + disableEmbed: '空,0与false为开启内嵌视频, 其他任意值为关闭', sortReverse: '默认:默认排序 1:升序排序', page: '页码, 默认1', }, @@ -35,7 +36,7 @@ export const route: Route = { async function handler(ctx) { const uid = Number.parseInt(ctx.req.param('uid')); const sid = Number.parseInt(ctx.req.param('sid')); - const disableEmbed = ctx.req.param('disableEmbed'); + const disableEmbed = queryToBoolean(ctx.req.param('disableEmbed')); const sortReverse = Number.parseInt(ctx.req.param('sortReverse')) === 1; const page = ctx.req.param('page') ? Number.parseInt(ctx.req.param('page')) : 1; const limit = ctx.req.query('limit') ?? 25; diff --git a/lib/routes/bluestacks/release.ts b/lib/routes/bluestacks/release.ts index 5b3ddeec1..5f1289379 100644 --- a/lib/routes/bluestacks/release.ts +++ b/lib/routes/bluestacks/release.ts @@ -1,6 +1,6 @@ import { Route } from '@/types'; import cache from '@/utils/cache'; -import cherrio from 'cheerio'; +import * as cheerio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import puppeteer from '@/utils/puppeteer'; @@ -43,7 +43,7 @@ async function handler() { const res = await page.evaluate(() => document.documentElement.innerHTML); await page.close(); - const $ = cherrio.load(res); + const $ = cheerio.load(res); const items = $('div h3 a') .toArray() @@ -67,7 +67,7 @@ async function handler() { waitUntil: 'domcontentloaded', }); const res = await page.evaluate(() => document.documentElement.innerHTML); - const $ = cherrio.load(res); + const $ = cheerio.load(res); await page.close(); item.description = $('div.article__body').html(); diff --git a/lib/routes/booru/mmda.ts b/lib/routes/booru/mmda.ts index a58b021bd..664cc1bb2 100644 --- a/lib/routes/booru/mmda.ts +++ b/lib/routes/booru/mmda.ts @@ -21,9 +21,11 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, - radar: { - source: ['mmda.booru.org/index.php'], - }, + radar: [ + { + source: ['mmda.booru.org/index.php'], + }, + ], name: 'MMDArchive 标签查询', maintainers: ['N78Wy'], handler, diff --git a/lib/routes/cas/mesalab/kb.ts b/lib/routes/cas/mesalab/kb.ts index 9cf63ca5d..346aacc2a 100644 --- a/lib/routes/cas/mesalab/kb.ts +++ b/lib/routes/cas/mesalab/kb.ts @@ -1,6 +1,6 @@ import { Route } from '@/types'; import cache from '@/utils/cache'; -import cherrio from 'cheerio'; +import * as cheerio from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -34,7 +34,7 @@ async function handler() { const url = `${homepage}/f/article/articleList?pageNo=1&pageSize=15&createTimeSort=DESC`; const response = await got(url); - const $ = cherrio.load(response.data); + const $ = cheerio.load(response.data); const articles = $('.aw-item').toArray(); const items = await Promise.all( @@ -45,7 +45,7 @@ async function handler() { return cache.tryGet(link, async () => { const result = await got(link); - const $ = cherrio.load(result.data); + const $ = cheerio.load(result.data); return { title, author: $('.user_name').text(), diff --git a/lib/routes/dbaplus/namespace.ts b/lib/routes/dbaplus/namespace.ts new file mode 100644 index 000000000..5de237b67 --- /dev/null +++ b/lib/routes/dbaplus/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'dbaplus社群', + url: 'dbaplus.cn', +}; diff --git a/lib/routes/dbaplus/rss.ts b/lib/routes/dbaplus/rss.ts new file mode 100644 index 000000000..f94576c35 --- /dev/null +++ b/lib/routes/dbaplus/rss.ts @@ -0,0 +1,48 @@ +import { Route } from '@/types'; +import { parseDate } from '@/utils/parse-date'; +import got from '@/utils/got'; +import { load } from 'cheerio'; + +export const route: Route = { + path: '/', + categories: ['programming'], + example: '/dbaplus', + radar: [ + { + source: ['dbaplus.cn/'], + }, + ], + name: '最新文章', + maintainers: ['cnkmmk'], + handler, + url: 'dbaplus.cn/', +}; + +async function handler() { + const url = 'https://dbaplus.cn'; + const response = await got(`${url}/news-9-1.html`); + const $ = load(response.data); + + const list = $('div.col-xs-12.col-md-8.pd30 > div.panel.panel-default.categeay > div.panel-body > ul.media-list.clearfix > li.media') + .map((i, e) => { + const element = $(e); + const title = element.find('h3 > a').text(); + const link = element.find('h3 > a').attr('href'); + const description = element.find('div.mt10.geay').text(); + const dateraw = element.find('span.time').text(); + + return { + title, + description, + link, + pubDate: parseDate(dateraw, 'YYYY年MM月DD日'), + }; + }) + .get(); + + return { + title: 'dbaplus社群', + link: url, + item: list, + }; +} diff --git a/lib/routes/modrinth/versions.ts b/lib/routes/modrinth/versions.ts index f5023665d..e7ba723c1 100644 --- a/lib/routes/modrinth/versions.ts +++ b/lib/routes/modrinth/versions.ts @@ -37,23 +37,25 @@ export const route: Route = { supportPodcast: false, supportScihub: false, }, - radar: { - source: [ - 'modrinth.com/mod/:id/*', - 'modrinth.com/plugin/:id/*', - 'modrinth.com/datapack/:id/*', - 'modrinth.com/shader/:id/*', - 'modrinth.com/resourcepack/:id/*', - 'modrinth.com/modpack/:id/*', - 'modrinth.com/mod/:id', - 'modrinth.com/plugin/:id', - 'modrinth.com/datapack/:id', - 'modrinth.com/shader/:id', - 'modrinth.com/resourcepack/:id', - 'modrinth.com/modpack/:id', - ], - target: '/project/:id/versions', - }, + radar: [ + { + source: [ + 'modrinth.com/mod/:id/*', + 'modrinth.com/plugin/:id/*', + 'modrinth.com/datapack/:id/*', + 'modrinth.com/shader/:id/*', + 'modrinth.com/resourcepack/:id/*', + 'modrinth.com/modpack/:id/*', + 'modrinth.com/mod/:id', + 'modrinth.com/plugin/:id', + 'modrinth.com/datapack/:id', + 'modrinth.com/shader/:id', + 'modrinth.com/resourcepack/:id', + 'modrinth.com/modpack/:id', + ], + target: '/project/:id/versions', + }, + ], name: 'Project versions', maintainers: ['SettingDust'], handler, diff --git a/lib/routes/ouc/hqsz.ts b/lib/routes/ouc/hqsz.ts index 33dd96832..9c7fc635c 100644 --- a/lib/routes/ouc/hqsz.ts +++ b/lib/routes/ouc/hqsz.ts @@ -2,6 +2,7 @@ import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import cache from '@/utils/cache'; import CryptoJS from 'crypto-js/crypto-js'; +import { Route } from '@/types'; export const route: Route = { path: '/hqsz', diff --git a/lib/routes/telegram/blog.ts b/lib/routes/telegram/blog.ts index 28b0253ea..661caed28 100644 --- a/lib/routes/telegram/blog.ts +++ b/lib/routes/telegram/blog.ts @@ -1,6 +1,6 @@ import { Route } from '@/types'; import cache from '@/utils/cache'; -import cherrio from 'cheerio'; +import * as cheerio from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -32,7 +32,7 @@ async function handler() { const link = 'https://telegram.org/blog'; const res = await got(link); - const $$ = cherrio.load(res.body); + const $$ = cheerio.load(res.body); const items = await Promise.all( $$('.dev_blog_card_link_wrap') @@ -42,7 +42,7 @@ async function handler() { const link = 'https://telegram.org' + $.attr('href'); return cache.tryGet(link, async () => { const result = await got(link); - const $ = cherrio.load(result.body); + const $ = cheerio.load(result.body); return { title: $('#dev_page_title').text(), link, diff --git a/lib/routes/telegram/tglib/client.ts b/lib/routes/telegram/tglib/client.ts index d135afb45..6d01710f9 100644 --- a/lib/routes/telegram/tglib/client.ts +++ b/lib/routes/telegram/tglib/client.ts @@ -1,4 +1,3 @@ -import readline from 'node:readline/promises'; import { Api, TelegramClient } from 'telegram'; import { UserAuthParams } from 'telegram/client/auth'; import { StringSession } from 'telegram/sessions'; @@ -187,20 +186,3 @@ function streamDocument(obj, thumbSize = '', offset, limit) { } export { client, getMediaLink, decodeMedia, getFilename, streamDocument, streamThumbnail }; - -if (require.main === module) { - Promise.resolve().then(async () => { - const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); - const client = await getClient( - { - phoneNumber: () => rl.question('Please enter your phone number: '), - password: () => rl.question('Please enter your password: '), - phoneCode: () => rl.question('Please enter the code you received: '), - onError: (err) => process.stderr.write(err.toString()), - }, - '' - ); - process.stdout.write(`TELEGRAM_SESSION=${client.session.save()}\n`); - process.exit(0); - }); -} diff --git a/lib/routes/test/index.ts b/lib/routes/test/index.ts index 52c884ea0..752fad595 100644 --- a/lib/routes/test/index.ts +++ b/lib/routes/test/index.ts @@ -79,13 +79,9 @@ async function handler(ctx) { break; case 'cache': { - const description = await cache.tryGet( - 'test', - () => ({ - text: `Cache${++cacheIndex}`, - }), - config.cache.routeExpire * 2 - ); + const description = await cache.tryGet('test', () => ({ + text: `Cache${++cacheIndex}`, + })); item.push({ title: 'Cache Title', description: description.text, diff --git a/lib/routes/youtube/channel.ts b/lib/routes/youtube/channel.ts index 426958bc4..dbfb5d1bb 100644 --- a/lib/routes/youtube/channel.ts +++ b/lib/routes/youtube/channel.ts @@ -10,7 +10,12 @@ export const route: Route = { example: '/youtube/channel/UCDwDMPOZfxVV0x_dz0eQ8KQ', parameters: { id: 'YouTube channel id', embed: 'Default to embed the video, set to any value to disable embedding' }, features: { - requireConfig: false, + requireConfig: [ + { + name: 'YOUTUBE_KEY', + description: ' YouTube API Key, support multiple keys, split them with `,`, [API Key application](https://console.developers.google.com/)', + }, + ], requirePuppeteer: false, antiCrawler: false, supportBT: false, diff --git a/lib/types.ts b/lib/types.ts index c5472e36c..0c7c3541c 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,5 +1,31 @@ import type { Context } from 'hono'; +// Make sure it's synchronise with scripts/workflow/data.ts +type Category = + | 'social-media' + | 'new-media' + | 'traditional-media' + | 'bbs' + | 'blog' + | 'programming' + | 'design' + | 'live' + | 'multimedia' + | 'picture' + | 'anime' + | 'program-update' + | 'university' + | 'forecast' + | 'travel' + | 'shopping' + | 'game' + | 'reading' + | 'government' + | 'study' + | 'journal' + | 'finance' + | 'other'; + // rss export type DataItem = { title: string; @@ -72,7 +98,7 @@ interface NamespaceItem { /** * The classification of the namespace, which will be written into the corresponding classification document */ - categories?: string[]; + categories?: Category[]; /** * Hints and additional explanations for users using this namespace, it will be inserted into the documentation @@ -117,7 +143,7 @@ interface RouteItem { /** * The handler function of the route */ - handler: (ctx?: Context) => Promise | Data; + handler: (ctx: Context) => Promise | Data; /** * An example URL of the route @@ -137,7 +163,7 @@ interface RouteItem { /** * The classification of the route, which will be written into the corresponding classification documentation */ - categories?: string[]; + categories?: Category[]; /** * Special features of the route, such as what configuration items it depends on, whether it is strict anti-crawl, whether it supports a certain function and so on diff --git a/lib/utils/proxy/index.ts b/lib/utils/proxy/index.ts index b307f84cd..b49730693 100644 --- a/lib/utils/proxy/index.ts +++ b/lib/utils/proxy/index.ts @@ -1,4 +1,7 @@ import { config } from '@/config'; +import { PacProxyAgent } from 'pac-proxy-agent'; +import { HttpsProxyAgent } from 'https-proxy-agent'; +import { SocksProxyAgent } from 'socks-proxy-agent'; const proxyIsPAC = config.pacUri || config.pacScript; @@ -20,16 +23,13 @@ if (proxyIsPAC) { proxyUrlHandler = proxy.proxyUrlHandler; } -let agent = null; +let agent: PacProxyAgent | HttpsProxyAgent | SocksProxyAgent | null = null; if (proxyIsPAC) { - const { PacProxyAgent } = require('pac-proxy-agent'); agent = new PacProxyAgent(`pac+${proxyUri}`); } else if (proxyUri) { if (proxyUri.startsWith('http')) { - const { HttpsProxyAgent } = require('https-proxy-agent'); agent = new HttpsProxyAgent(proxyUri); } else if (proxyUri.startsWith('socks')) { - const { SocksProxyAgent } = require('socks-proxy-agent'); agent = new SocksProxyAgent(proxyUri); } } diff --git a/lib/utils/puppeteer.ts b/lib/utils/puppeteer.ts index 9ad470f91..24c361632 100644 --- a/lib/utils/puppeteer.ts +++ b/lib/utils/puppeteer.ts @@ -2,8 +2,7 @@ import { config } from '@/config'; import puppeteer from 'puppeteer'; import logger from './logger'; import proxy from './proxy'; - -const proxyChain = require('proxy-chain'); +import proxyChain from 'proxy-chain'; import { type PuppeteerExtra, addExtra } from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; @@ -27,28 +26,6 @@ const outPuppeteer = async ( let insidePuppeteer: PuppeteerExtra | typeof puppeteer = puppeteer; if (extraOptions.stealth) { insidePuppeteer = addExtra(puppeteer); - - // workaround for vercel/nft #54, #283, #304 - require('puppeteer-extra-plugin-stealth/evasions/chrome.app'); - require('puppeteer-extra-plugin-stealth/evasions/chrome.csi'); - require('puppeteer-extra-plugin-stealth/evasions/chrome.loadTimes'); - require('puppeteer-extra-plugin-stealth/evasions/chrome.runtime'); - require('puppeteer-extra-plugin-stealth/evasions/defaultArgs'); - require('puppeteer-extra-plugin-stealth/evasions/iframe.contentWindow'); - require('puppeteer-extra-plugin-stealth/evasions/media.codecs'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.hardwareConcurrency'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.languages'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.permissions'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.plugins'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.vendor'); - require('puppeteer-extra-plugin-stealth/evasions/navigator.webdriver'); - require('puppeteer-extra-plugin-stealth/evasions/sourceurl'); - require('puppeteer-extra-plugin-stealth/evasions/user-agent-override'); - require('puppeteer-extra-plugin-stealth/evasions/webgl.vendor'); - require('puppeteer-extra-plugin-stealth/evasions/window.outerdimensions'); - require('puppeteer-extra-plugin-user-preferences'); - require('puppeteer-extra-plugin-user-data-dir'); - insidePuppeteer.use(StealthPlugin()); } diff --git a/lib/views/error.tsx b/lib/views/error.tsx index be2f75b1b..9aedf5864 100644 --- a/lib/views/error.tsx +++ b/lib/views/error.tsx @@ -52,13 +52,12 @@ const Index: FC<{

Helpful Information

-

Error Message:

- {message} -

Route: {errorRoute}

-

Full Route: {requestPath}

-

Node Version: {nodeVersion}

-

Git Hash: {gitHash}

-

Git Date: {gitDate?.toUTCString()}

+

Error Message:
{message}

+

Route: {errorRoute}

+

Full Route: {requestPath}

+

Node Version: {nodeVersion}

+

Git Hash: {gitHash}

+

Git Date: {gitDate?.toUTCString()}

Report

diff --git a/package.json b/package.json index c27f74fc5..e4e3b7496 100644 --- a/package.json +++ b/package.json @@ -19,20 +19,23 @@ "files": [ "lib" ], + "type": "module", "scripts": { "build": "tsx scripts/workflow/build-routes.ts", - "dev": "cross-env NODE_ENV=dev tsx watch --no-cache lib/index.ts", - "dev:cache": "cross-env NODE_ENV=production tsx watch lib/index.ts", + "build:docs": "tsx scripts/workflow/build-docs.ts", + "dev": "NODE_ENV=dev tsx watch --no-cache lib/index.ts", + "dev:cache": "NODE_ENV=production tsx watch lib/index.ts", "format": "eslint --cache --fix \"**/*.{ts,js,yml}\" && prettier \"**/*.{ts,js,json}\" --write", "format:check": "eslint --cache \"**/*.{ts,js,yml}\" && prettier \"**/*.{ts,js,json}\" --check", "format:staged": "lint-staged", - "vitest": "cross-env NODE_ENV=test vitest", - "vitest:coverage": "cross-env NODE_ENV=test vitest --coverage.enabled --reporter=junit", - "vitest:watch": "cross-env NODE_ENV=test vitest --watch", + "vitest": "NODE_ENV=test vitest", + "vitest:fullroutes": "NODE_ENV=test FULL_ROUTES_TEST=true vitest --reporter=json --reporter=default --outputFile=\"./assets/build/test-full-routes.json\" routes", + "vitest:coverage": "NODE_ENV=test vitest --coverage.enabled --reporter=junit", + "vitest:watch": "NODE_ENV=test vitest --watch", "lint": "eslint --cache .", "prepare": "husky || true", "profiling": "NODE_ENV=production tsx --prof lib/index.ts", - "start": "cross-env NODE_ENV=production tsx lib/index.ts", + "start": "NODE_ENV=production tsx lib/index.ts", "test": "npm run format:check && npm run vitest:coverage" }, "lint-staged": { @@ -49,10 +52,10 @@ "dependencies": { "@hono/node-server": "1.8.2", "@hono/swagger-ui": "0.2.1", - "@hono/zod-openapi": "0.9.8", + "@hono/zod-openapi": "0.9.9", "@notionhq/client": "2.2.14", "@postlight/parser": "2.2.3", - "@sentry/node": "7.107.0", + "@sentry/node": "7.108.0", "@tonyrl/rand-user-agent": "2.0.55", "aes-js": "3.1.2", "art-template": "4.13.2", @@ -110,7 +113,7 @@ "telegram": "2.20.2", "tiny-async-pool": "2.1.0", "title": "3.5.3", - "tldts": "6.1.13", + "tldts": "6.1.14", "tough-cookie": "4.1.3", "tsx": "4.7.1", "twitter-api-v2": "1.16.1", @@ -149,7 +152,6 @@ "@typescript-eslint/parser": "7.3.1", "@vercel/nft": "0.26.4", "@vitest/coverage-v8": "1.4.0", - "cross-env": "7.0.3", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-nibble": "8.1.0", @@ -159,6 +161,7 @@ "eslint-plugin-yml": "1.13.2", "fs-extra": "11.2.0", "husky": "9.0.11", + "js-beautify": "1.15.1", "lint-staged": "15.2.2", "mockdate": "3.0.5", "nock": "13.5.4", @@ -167,7 +170,7 @@ "supertest": "6.3.4", "tosource": "2.0.0-alpha.3", "typescript": "5.4.3", - "unified": "9.2.2", + "unified": "11.0.4", "vite-tsconfig-paths": "4.3.2", "vitest": "1.4.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa2ee63b0..004507bce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: 0.2.1 version: 0.2.1(hono@4.1.3) '@hono/zod-openapi': - specifier: 0.9.8 - version: 0.9.8(hono@4.1.3)(zod@3.22.4) + specifier: 0.9.9 + version: 0.9.9(hono@4.1.3)(zod@3.22.4) '@notionhq/client': specifier: 2.2.14 version: 2.2.14 @@ -21,8 +21,8 @@ dependencies: specifier: 2.2.3 version: 2.2.3 '@sentry/node': - specifier: 7.107.0 - version: 7.107.0 + specifier: 7.108.0 + version: 7.108.0 '@tonyrl/rand-user-agent': specifier: 2.0.55 version: 2.0.55 @@ -195,8 +195,8 @@ dependencies: specifier: 3.5.3 version: 3.5.3 tldts: - specifier: 6.1.13 - version: 6.1.13 + specifier: 6.1.14 + version: 6.1.14 tough-cookie: specifier: 4.1.3 version: 4.1.3 @@ -307,9 +307,6 @@ devDependencies: '@vitest/coverage-v8': specifier: 1.4.0 version: 1.4.0(vitest@1.4.0) - cross-env: - specifier: 7.0.3 - version: 7.0.3 eslint: specifier: 8.57.0 version: 8.57.0 @@ -337,6 +334,9 @@ devDependencies: husky: specifier: 9.0.11 version: 9.0.11 + js-beautify: + specifier: 1.15.1 + version: 1.15.1 lint-staged: specifier: 15.2.2 version: 15.2.2 @@ -362,8 +362,8 @@ devDependencies: specifier: 5.4.3 version: 5.4.3 unified: - specifier: 9.2.2 - version: 9.2.2 + specifier: 11.0.4 + version: 11.0.4 vite-tsconfig-paths: specifier: 4.3.2 version: 4.3.2(typescript@5.4.3) @@ -1915,8 +1915,8 @@ packages: hono: 4.1.3 dev: false - /@hono/zod-openapi@0.9.8(hono@4.1.3)(zod@3.22.4): - resolution: {integrity: sha512-NS6lvQEGnsjyQkp+aQjMARREz3WGf19y0+RiiJMVrihWlmKbGaJkPuOWpfFQG6FU9q+FVUyUCryXK+3J07GPAw==} + /@hono/zod-openapi@0.9.9(hono@4.1.3)(zod@3.22.4): + resolution: {integrity: sha512-Icak3c8WKNS1gFDWmYs2zJ7ra3js9lDeAXNPf5h3fa0SnJQcjcCrLaG6EZPPAbW92HRwCDeQt8yA/ZVp17HPFg==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=3.11.3' @@ -1970,6 +1970,18 @@ packages: resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} dev: false + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -2098,6 +2110,10 @@ packages: - encoding dev: false + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: true + /@otplib/core@12.0.1: resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==} dev: false @@ -2131,6 +2147,13 @@ packages: '@otplib/plugin-thirty-two': 12.0.1 dev: false + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + /@pkgr/core@0.1.1: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -2334,43 +2357,43 @@ packages: selderee: 0.11.0 dev: false - /@sentry-internal/tracing@7.107.0: - resolution: {integrity: sha512-le9wM8+OHBbq7m/8P7JUJ1UhSPIty+Z/HmRXc5Z64ODZcOwFV6TmDpYx729IXDdz36XUKmeI+BeM7yQdTTZPfQ==} + /@sentry-internal/tracing@7.108.0: + resolution: {integrity: sha512-zuK5XsTsb+U+hgn3SPetYDAogrXsM16U/LLoMW7+TlC6UjlHGYQvmX3o+M2vntejoU1QZS8m1bCAZSMWEypAEw==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.107.0 - '@sentry/types': 7.107.0 - '@sentry/utils': 7.107.0 + '@sentry/core': 7.108.0 + '@sentry/types': 7.108.0 + '@sentry/utils': 7.108.0 dev: false - /@sentry/core@7.107.0: - resolution: {integrity: sha512-C7ogye6+KPyBi8NVL0P8Rxx3Ur7Td8ufnjxosVy678lqY+dcYPk/HONROrzUFYW5fMKWL4/KYnwP+x9uHnkDmw==} + /@sentry/core@7.108.0: + resolution: {integrity: sha512-I/VNZCFgLASxHZaD0EtxZRM34WG9w2gozqgrKGNMzAymwmQ3K9g/1qmBy4e6iS3YRptb7J5UhQkZQHrcwBbjWQ==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.107.0 - '@sentry/utils': 7.107.0 + '@sentry/types': 7.108.0 + '@sentry/utils': 7.108.0 dev: false - /@sentry/node@7.107.0: - resolution: {integrity: sha512-UZXkG7uThT2YyPW8AOSKRXp1LbVcBHufa4r1XAwBukA2FKO6HHJPjMUgY6DYVQ6k+BmA56CNfVjYrdLbyjBYYA==} + /@sentry/node@7.108.0: + resolution: {integrity: sha512-pMxc9txnDDkU4Z8k2Uw/DPSLPehNtWV3mjJ3+my0AMORGYrXLkJI93tddlE5z/7k+GEJdj1HsOLgxUN0OU+HGA==} engines: {node: '>=8'} dependencies: - '@sentry-internal/tracing': 7.107.0 - '@sentry/core': 7.107.0 - '@sentry/types': 7.107.0 - '@sentry/utils': 7.107.0 + '@sentry-internal/tracing': 7.108.0 + '@sentry/core': 7.108.0 + '@sentry/types': 7.108.0 + '@sentry/utils': 7.108.0 dev: false - /@sentry/types@7.107.0: - resolution: {integrity: sha512-H7qcPjPSUWHE/Zf5bR1EE24G0pGVuJgrSx8Tvvl5nKEepswMYlbXHRVSDN0gTk/E5Z7cqf+hUBOpkQgZyps77w==} + /@sentry/types@7.108.0: + resolution: {integrity: sha512-bKtHITmBN3kqtqE5eVvL8mY8znM05vEodENwRpcm6TSrrBjC2RnwNWVwGstYDdHpNfFuKwC8mLY9bgMJcENo8g==} engines: {node: '>=8'} dev: false - /@sentry/utils@7.107.0: - resolution: {integrity: sha512-C6PbN5gHh73MRHohnReeQ60N8rrLYa9LciHue3Ru2290eSThg4CzsPnx4SzkGpkSeVlhhptKtKZ+hp/ha3iVuw==} + /@sentry/utils@7.108.0: + resolution: {integrity: sha512-a45yEFD5qtgZaIFRAcFkG8C8lnDzn6t4LfLXuV4OafGAy/3ZAN3XN8wDnrruHkiUezSSANGsLg3bXaLW/JLvJw==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.107.0 + '@sentry/types': 7.108.0 dev: false /@sinclair/typebox@0.27.8: @@ -2691,10 +2714,6 @@ packages: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} dev: false - /@types/unist@2.0.10: - resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} - dev: true - /@types/unist@3.0.2: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} dev: true @@ -2994,6 +3013,11 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: true + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -3266,10 +3290,6 @@ packages: - supports-color dev: true - /bail@1.0.5: - resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} - dev: true - /bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} dev: true @@ -3456,7 +3476,7 @@ packages: http-cache-semantics: 4.1.1 keyv: 4.5.4 mimic-response: 4.0.0 - normalize-url: 8.0.1 + normalize-url: 8.0.0 responselike: 3.0.0 dev: false @@ -3802,6 +3822,11 @@ packages: dependencies: delayed-stream: 1.0.0 + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -3822,6 +3847,13 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} dev: true @@ -3872,14 +3904,6 @@ packages: typescript: 5.4.3 dev: false - /cross-env@7.0.3: - resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} - engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} - hasBin: true - dependencies: - cross-spawn: 7.0.3 - dev: true - /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -4232,6 +4256,10 @@ packages: engines: {node: '>=6'} dev: false + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true + /ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} dependencies: @@ -4245,6 +4273,17 @@ packages: safe-buffer: 5.2.1 dev: false + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.6.0 + dev: true + /electron-to-chromium@1.4.669: resolution: {integrity: sha512-E2SmpffFPrZhBSgf8ibqanRS2mpuk3FIRDzLDwt7WFpfgJMKDHJs0hmacyP0PS1cWsq0dVkwIIzlscNaterkPg==} dev: true @@ -4260,6 +4299,10 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true + /enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} dev: false @@ -4936,6 +4979,14 @@ packages: for-in: 1.0.2 dev: false + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} dev: false @@ -5155,6 +5206,18 @@ packages: is-glob: 4.0.3 dev: true + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 5.0.0 + path-scurry: 1.10.1 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -5585,6 +5648,10 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + /inquirer@8.2.6: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} @@ -5691,11 +5758,6 @@ packages: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} dev: false - /is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - dev: true - /is-builtin-module@3.2.1: resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} engines: {node: '>=6'} @@ -5761,11 +5823,6 @@ packages: engines: {node: '>=8'} dev: true - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: true - /is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -5855,6 +5912,32 @@ packages: istanbul-lib-report: 3.0.1 dev: true + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /js-beautify@1.15.1: + resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} + engines: {node: '>=14'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.3.10 + js-cookie: 3.0.5 + nopt: 7.2.0 + dev: true + + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + dev: true + /js-tokens@3.0.2: resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} dev: false @@ -6280,7 +6363,6 @@ packages: /lru-cache@10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} - dev: false /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -6688,6 +6770,13 @@ packages: dependencies: brace-expansion: 1.1.11 + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -6856,6 +6945,14 @@ packages: abbrev: 1.1.1 dev: true + /nopt@7.2.0: + resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + abbrev: 2.0.0 + dev: true + /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -6865,8 +6962,8 @@ packages: validate-npm-package-license: 3.0.4 dev: true - /normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + /normalize-url@8.0.0: + resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} engines: {node: '>=14.16'} dev: false @@ -7188,6 +7285,14 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 5.0.0 + dev: true + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -7360,6 +7465,10 @@ packages: engines: {node: '>= 8'} dev: true + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /proxy-agent@6.4.0: resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} engines: {node: '>= 14'} @@ -8285,6 +8394,15 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + /string-width@7.1.0: resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} @@ -8545,15 +8663,15 @@ packages: hasBin: true dev: false - /tldts-core@6.1.13: - resolution: {integrity: sha512-M1XP4D13YtXARKroULnLsKKuI1NCRAbJmUGGoXqWinajIDOhTeJf/trYUyBoLVx1/Nx1KBKxCrlW57ZW9cMHAA==} + /tldts-core@6.1.14: + resolution: {integrity: sha512-McyMQkkIUFYhfs3FPTxTn+5mewxERhfwy2x7TWHkBPb1poKaTBJhXehtuMg0FrhXp53J5eXRfvSD/oH/3mk/2A==} dev: false - /tldts@6.1.13: - resolution: {integrity: sha512-+GxHFKVHvUTg2ieNPTx3b/NpZbgJSTZEDdI4cJzTjVYDuxijeHi1tt7CHHsMjLqyc+T50VVgWs3LIb2LrXOzxw==} + /tldts@6.1.14: + resolution: {integrity: sha512-zGbimRt9fHP68Gbj5fGWmR90xiuRDK/iYukHevT9mcG6Job+HfR119D9DSr6voFWjpStJwOtVfWGpbhNYVwt3A==} hasBin: true dependencies: - tldts-core: 6.1.13 + tldts-core: 6.1.14 dev: false /tmp@0.0.33: @@ -8627,10 +8745,6 @@ packages: engines: {node: '>= 14.0.0'} dev: false - /trough@1.0.5: - resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} - dev: true - /trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} dev: true @@ -8844,24 +8958,6 @@ packages: vfile: 6.0.1 dev: true - /unified@9.2.2: - resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==} - dependencies: - '@types/unist': 2.0.10 - bail: 1.0.5 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 2.1.0 - trough: 1.0.5 - vfile: 4.2.1 - dev: true - - /unist-util-stringify-position@2.0.3: - resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} - dependencies: - '@types/unist': 2.0.10 - dev: true - /unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} dependencies: @@ -8988,13 +9084,6 @@ packages: extsprintf: 1.3.0 dev: false - /vfile-message@2.0.4: - resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} - dependencies: - '@types/unist': 2.0.10 - unist-util-stringify-position: 2.0.3 - dev: true - /vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} dependencies: @@ -9002,15 +9091,6 @@ packages: unist-util-stringify-position: 4.0.0 dev: true - /vfile@4.2.1: - resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} - dependencies: - '@types/unist': 2.0.10 - is-buffer: 2.0.5 - unist-util-stringify-position: 2.0.3 - vfile-message: 2.0.4 - dev: true - /vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} dependencies: @@ -9282,7 +9362,15 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: false + + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true /wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} diff --git a/scripts/workflow/build-docs.ts b/scripts/workflow/build-docs.ts new file mode 100644 index 000000000..43384e152 --- /dev/null +++ b/scripts/workflow/build-docs.ts @@ -0,0 +1,122 @@ +import { namespaces } from '../../lib/registry'; +import fs from 'node:fs'; +import * as path from 'node:path'; +import { categories } from './data'; +import { getCurrentPath } from '../../lib/utils/helpers'; + +const fullTests = await (await fetch('https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/test-full-routes.json')).json(); +const testResult = fullTests.testResults[0].assertionResults; + +const __dirname = getCurrentPath(import.meta.url); + +const docs = {}; + +for (const namespace in namespaces) { + let defaultCategory = namespaces[namespace].categories?.[0]; + if (!defaultCategory) { + for (const path in namespaces[namespace].routes) { + if (namespaces[namespace].routes[path].categories) { + defaultCategory = namespaces[namespace].routes[path].categories[0]; + break; + } + } + } + if (!defaultCategory) { + defaultCategory = 'other'; + } + for (const path in namespaces[namespace].routes) { + const realPath = `/${namespace}${path}`; + const data = namespaces[namespace].routes[path]; + const categories = data.categories || namespaces[namespace].categories || [defaultCategory]; + // docs.json + for (const category of categories) { + if (!docs[category]) { + docs[category] = {}; + } + if (!docs[category][namespace]) { + docs[category][namespace] = { + routes: {}, + }; + } + docs[category][namespace].name = namespaces[namespace].name; + docs[category][namespace].url = namespaces[namespace].url; + docs[category][namespace].description = namespaces[namespace].description; + docs[category][namespace].routes[realPath] = data; + } + } +} + +// Generate markdown +const pinyinCompare = new Intl.Collator('zh-Hans-CN-u-co-pinyin').compare; +const isASCII = (str) => /^[\u0000-\u007F]*$/.test(str); + +function generateMd(lang) { + const md = {}; + for (const category in docs) { + const nameObj = categories.find((c) => c.link.includes(category)); + if (!nameObj) { + throw new Error(`Category not found: ${category}, please double check your spelling.`); + } + + md[category] = `# ${`${nameObj.icon} ${nameObj[lang]}`}\n\n`; + + const namespaces = Object.keys(docs[category]).sort((a, b) => { + const aname = docs[category][a].name[0]; + const bname = docs[category][b].name[0]; + const ia = isASCII(aname); + const ib = isASCII(bname); + if (ia && ib) { + return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1; + } else if (ia || ib) { + return ia > ib ? -1 : 1; + } else { + return pinyinCompare(aname, bname); + } + }); + for (const namespace of namespaces) { + if (docs[category][namespace].name === 'Unknown') { + docs[category][namespace].name = namespace; + } + md[category] += `## ${docs[category][namespace].name || namespace} ${docs[category][namespace].url ? `` : ''}\n\n`; + if (docs[category][namespace].description) { + md[category] += `${docs[category][namespace].description}\n\n`; + } + + const realPaths = Object.keys(docs[category][namespace].routes).sort((a, b) => { + const aname = docs[category][namespace].routes[a].name[0]; + const bname = docs[category][namespace].routes[b].name[0]; + const ia = isASCII(aname); + const ib = isASCII(bname); + if (ia && ib) { + return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1; + } else if (ia || ib) { + return ia > ib ? -1 : 1; + } else { + return pinyinCompare(aname, bname); + } + }); + + for (const realPath of realPaths) { + const data = docs[category][namespace].routes[realPath]; + const test = testResult.find((t) => t.title === realPath); + const parsedTest = test + ? { + code: test.status === 'passed' ? 0 : 1, + message: test.failureMessages?.[0], + } + : undefined; + md[category] += `### ${data.name} ${data.url || docs[category][namespace].url ? `` : ''}\n\n`; + md[category] += `\n\n`; + if (data.description) { + md[category] += `${data.description}\n\n`; + } + } + } + } + fs.mkdirSync(path.join(__dirname, `../../assets/build/docs/${lang}`), { recursive: true }); + for (const category in md) { + fs.writeFileSync(path.join(__dirname, `../../assets/build/docs/${lang}/${category}.md`), md[category]); + } +} +generateMd('en'); +generateMd('zh'); diff --git a/scripts/workflow/build-routes.ts b/scripts/workflow/build-routes.ts index 02928a556..3a4634cc1 100644 --- a/scripts/workflow/build-routes.ts +++ b/scripts/workflow/build-routes.ts @@ -4,7 +4,9 @@ import { parse } from 'tldts'; import fs from 'node:fs'; import * as path from 'node:path'; import toSource from 'tosource'; -import { categories } from './data'; + +import { getCurrentPath } from '../../lib/utils/helpers'; +const __dirname = getCurrentPath(import.meta.url); const maintainers: Record = {}; const radar: { @@ -13,7 +15,6 @@ const radar: { [subdomain: string]: RadarItem[] | string; }; } = {}; -const docs = {}; for (const namespace in namespaces) { let defaultCategory = namespaces[namespace].categories?.[0]; @@ -63,21 +64,6 @@ for (const namespace in namespaces) { } } } - // docs.json - for (const category of categories) { - if (!docs[category]) { - docs[category] = {}; - } - if (!docs[category][namespace]) { - docs[category][namespace] = { - routes: {}, - }; - } - docs[category][namespace].name = namespaces[namespace].name; - docs[category][namespace].url = namespaces[namespace].url; - docs[category][namespace].description = namespaces[namespace].description; - docs[category][namespace].routes[realPath] = data; - } } } @@ -85,67 +71,3 @@ fs.writeFileSync(path.join(__dirname, '../../assets/build/radar-rules.json'), JS 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)); - -// Generate markdown -const pinyinCompare = new Intl.Collator('zh-Hans-CN-u-co-pinyin').compare; -const isASCII = (str) => /^[\u0000-\u007F]*$/.test(str); - -function generateMd(lang) { - const md = {}; - for (const category in docs) { - const nameObj = categories.find((c) => c.link.includes(category)); - md[category] = `# ${`${nameObj!.icon} ${nameObj![lang]}`}\n\n`; - - const namespaces = Object.keys(docs[category]).sort((a, b) => { - const aname = docs[category][a].name[0]; - const bname = docs[category][b].name[0]; - const ia = isASCII(aname); - const ib = isASCII(bname); - if (ia && ib) { - return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1; - } else if (ia || ib) { - return ia > ib ? -1 : 1; - } else { - return pinyinCompare(aname, bname); - } - }); - for (const namespace of namespaces) { - if (docs[category][namespace].name === 'Unknown') { - docs[category][namespace].name = namespace; - } - md[category] += `## ${docs[category][namespace].name || namespace} ${docs[category][namespace].url ? `` : ''}\n\n`; - if (docs[category][namespace].description) { - md[category] += `${docs[category][namespace].description}\n\n`; - } - - const realPaths = Object.keys(docs[category][namespace].routes).sort((a, b) => { - const aname = docs[category][namespace].routes[a].name[0]; - const bname = docs[category][namespace].routes[b].name[0]; - const ia = isASCII(aname); - const ib = isASCII(bname); - if (ia && ib) { - return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1; - } else if (ia || ib) { - return ia > ib ? -1 : 1; - } else { - return pinyinCompare(aname, bname); - } - }); - - for (const realPath of realPaths) { - const data = docs[category][namespace].routes[realPath]; - md[category] += `### ${data.name} ${data.url || docs[category][namespace].url ? `` : ''}\n\n`; - md[category] += `\n\n`; - if (data.description) { - md[category] += `${data.description}\n\n`; - } - } - } - } - fs.mkdirSync(path.join(__dirname, `../../assets/build/docs/${lang}`), { recursive: true }); - for (const category in md) { - fs.writeFileSync(path.join(__dirname, `../../assets/build/docs/${lang}/${category}.md`), md[category]); - } -} -generateMd('en'); -generateMd('zh'); diff --git a/scripts/workflow/test-issue/call-maintainer.js b/scripts/workflow/test-issue/call-maintainer.mjs similarity index 87% rename from scripts/workflow/test-issue/call-maintainer.js rename to scripts/workflow/test-issue/call-maintainer.mjs index 406ba9f25..76f8101c5 100644 --- a/scripts/workflow/test-issue/call-maintainer.js +++ b/scripts/workflow/test-issue/call-maintainer.mjs @@ -1,10 +1,9 @@ -const unified = require('unified'); -const parse = require('remark-parse'); -const got = require('got'); +import { unified } from 'unified'; +import remarkParse from 'remark-parse'; // @TODO maybe we could use label or some other better ways to distinguish bug/feature issues const matchTitle = ['路由地址', 'Routes']; -const maintainerURL = 'https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/maintainer.json'; +const maintainerURL = 'https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/maintainers.json'; const successTag = 'Bug Ping: Pinged'; const parseFailTag = 'Bug Ping: Parsing Failed'; const failTag = 'Bug Ping: Not Found'; @@ -15,7 +14,7 @@ const route = 'Route'; const dndUsernames = new Set([]); async function parseBodyRoutes(body, core) { - const ast = await unified().use(parse).parse(body); + const ast = await unified().use(remarkParse).parse(body); // Is this a bug report? const title = ast.children[0].children[0].value.trim(); @@ -44,10 +43,8 @@ async function parseBodyRoutes(body, core) { } async function getMaintainersByRoutes(routes, core) { - // TODO: change me when https://github.com/actions/github-script is run on node20 - // const response = await fetch(maintainerURL); - // const maintainers = await response.json(); - const maintainers = await got(maintainerURL).json(); + const response = await fetch(maintainerURL); + const maintainers = await response.json(); return routes.map((e) => { const m = maintainers[e]; @@ -59,9 +56,9 @@ async function getMaintainersByRoutes(routes, core) { }); } -module.exports = async ({ github, context, core }) => { +export default async function callMaintainer({ github, context, core }) { const body = context.payload.issue.body; - const issue_facts = { + const issueFacts = { issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, @@ -70,7 +67,7 @@ module.exports = async ({ github, context, core }) => { const addLabels = (labels) => github.rest.issues .addLabels({ - ...issue_facts, + ...issueFacts, labels, }) .catch((error) => { @@ -79,7 +76,7 @@ module.exports = async ({ github, context, core }) => { const updateIssueState = (state) => github.rest.issues .update({ - ...issue_facts, + ...issueFacts, state, }) .catch((error) => { @@ -138,7 +135,7 @@ module.exports = async ({ github, context, core }) => { } } - const labels = [`Count: ${successCount}/${routes.length}`]; + const labels = ['']; if (failedCount > 0) { labels.push(failTag); @@ -160,14 +157,14 @@ module.exports = async ({ github, context, core }) => { // Reply to the issue and notify the maintainers (if any) await github.rest.issues .createComment({ - ...issue_facts, + ...issueFacts, body: `${comments} > To maintainers: if you are not willing to be disturbed, list your username in \`scripts/workflow/test-issue/call-maintainer.js\`. In this way, your username will be wrapped in an inline code block when tagged so you will not be notified. -如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 \`NOROUTE\` 关键词,或者留下评论。我们会重新审核。 If all routes can not be found, the issue will be closed automatically. Please use \`NOROUTE\` for a route-irrelevant issue or leave a comment if it is a mistake. +如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 \`NOROUTE\` 关键词,或者留下评论。我们会重新审核。 `, }) .catch((error) => { @@ -177,4 +174,4 @@ If all routes can not be found, the issue will be closed automatically. Please u if (failedCount && emptyCount === 0 && successCount === 0) { await updateIssueState('closed'); } -}; +} diff --git a/scripts/workflow/test-route/identify.js b/scripts/workflow/test-route/identify.mjs similarity index 77% rename from scripts/workflow/test-route/identify.js rename to scripts/workflow/test-route/identify.mjs index 4a5199ff2..4aa342d8c 100644 --- a/scripts/workflow/test-route/identify.js +++ b/scripts/workflow/test-route/identify.mjs @@ -1,14 +1,15 @@ const noFound = 'Auto: Route No Found'; +const testFailed = 'Auto: Route Test Failed'; const allowedUser = new Set(['dependabot[bot]', 'pull[bot]']); // dependabot and downstream PR requested by pull[bot] -module.exports = async ({ github, context, core }, body, number, sender) => { +export default async function identify({ github, context, core }, body, number, sender) { core.debug(`sender: ${sender}`); core.debug(`body: ${body}`); // Remove all HTML comments before performing the match const bodyNoCmts = body.replaceAll(//g, ''); const m = bodyNoCmts.match(/```routes\s+([\S\s]*?)```/); core.debug(`match: ${m}`); - let res = null; + let routes = null; const issueFacts = { owner: context.repo.owner, @@ -31,11 +32,11 @@ module.exports = async ({ github, context, core }, body, number, sender) => { core.warning(error); }); - const removeLabel = () => + const removeLabel = (labelName = noFound) => github.rest.issues .removeLabel({ ...issueFacts, - name: noFound, + name: labelName, }) .catch((error) => { core.warning(error); @@ -80,8 +81,13 @@ module.exports = async ({ github, context, core }, body, number, sender) => { .catch((error) => { core.warning(error); }); - if (pr.pull_request && pr.state === 'closed') { - await updatePrState('open'); + if (pr.pull_request) { + if (pr.state === 'closed') { + await updatePrState('open'); + } + if (pr.labels.some((e) => e.name === testFailed)) { + await removeLabel(testFailed); + } } if (allowedUser.has(sender)) { @@ -94,19 +100,25 @@ module.exports = async ({ github, context, core }, body, number, sender) => { } if (m && m[1]) { - res = m[1].trim().split(/\r?\n/); - core.info(`routes detected: ${res}`); + routes = m[1].trim().split(/\r?\n/); + core.info(`routes detected: ${routes}`); - if (res.length && res[0] === 'NOROUTE') { + if (routes.length && routes[0] === 'NOROUTE') { core.info('PR stated no route, passing'); await removeLabel(); await addLabels(['Auto: Route Test Skipped']); return; - } else if (res.length && !res.some((e) => e.includes('/:'))) { + } else if (routes.length) { + if (routes.some((e) => e.includes('/:'))) { + await addLabels([noFound]); + return createComment(`Please use actual values in \`routes\` section instead of path parameters. + 请在 \`routes\` 部分使用实际值而不是路径参数。`); + } + core.exportVariable('TEST_CONTINUE', true); await removeLabel(); - return res; + return routes; } } @@ -119,4 +131,4 @@ module.exports = async ({ github, context, core }, body, number, sender) => { } throw new Error('Please follow the PR rules: failed to detect route'); -}; +} diff --git a/scripts/workflow/test-route/test.js b/scripts/workflow/test-route/test.js deleted file mode 100644 index 88a6f8459..000000000 --- a/scripts/workflow/test-route/test.js +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable no-await-in-loop */ - -module.exports = async ({ github, context, core, got }, baseUrl, routes, number) => { - if (routes[0] === 'NOROUTE') { - return; - } - - const links = routes.map((e) => { - const l = e.startsWith('/') ? e : `/${e}`; - return `${baseUrl}${l}`; - }); - - let com_l = []; - let com = `Successfully [generated](${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) as following:\n`; - - for (const lks of links) { - core.info(`testing route: ${lks}`); - // Intended, one at a time - let success = false; - let detail; - try { - // TODO: change me when https://github.com/actions/github-script is run on node20 - // const res = await fetch(lks); - // if (!res.ok) { - // throw res; - // } - // success = true; - // detail = (await res.text()).replace(/\s+(\n|$)/g, '\n'); - const res = await got(lks); - if (res && res.body) { - success = true; - detail = res.body.replaceAll(/\s+(\n|$)/g, '\n'); - } - } catch (error) { - // TODO: change me when https://github.com/actions/github-script is run on node20 - // detail = `HTTPError: Response code ${err.status} (${err.statusText})`; - // const res = await err.text(); - // const errInfoList = err.body && res.match(/(?<=
)(.+?)(?=<\/pre>)/gs);
-            detail = error.toString();
-            const errInfoList = error.response && error.response.body && error.response.body.match(/(?<=
)(.+?)(?=<\/pre>)/gs);
-            if (errInfoList) {
-                detail += '\n\n';
-                detail += errInfoList
-                    .slice(0, 3)
-                    .map((e) => e.trim())
-                    .join('\n');
-            }
-        }
-
-        let temp_com = `
-
-${lks} - ${success ? 'Success ✔️' : 'Failed ❌'} - -\`\`\`${success ? 'rss' : ''}`; - temp_com += ` -${detail.slice(0, 65300 - temp_com.length)} -\`\`\` -
-`; - if (com.length + temp_com.length >= 65500) { - com += '\n\n...'; - com_l.push(com); - com = temp_com; - } else { - com += temp_com; - } - } - - if (com.length > 0) { - com_l.push(com); - } - - if (com_l.length >= 5) { - com_l = com_l.slice(0, 5); - } - - if (process.env.PULL_REQUEST) { - await github.rest.issues - .addLabels({ - issue_number: number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Auto: Route Test Complete'], - }) - .catch((error) => { - core.warning(error); - }); - } - - for (const com_s of com_l) { - // Intended, one at a time - await github.rest.issues - .createComment({ - issue_number: number, - owner: context.repo.owner, - repo: context.repo.repo, - body: com_s, - }) - .catch((error) => { - core.warning(error); - }); - } -}; diff --git a/scripts/workflow/test-route/test.mjs b/scripts/workflow/test-route/test.mjs new file mode 100644 index 000000000..8fc1966f1 --- /dev/null +++ b/scripts/workflow/test-route/test.mjs @@ -0,0 +1,92 @@ +import jsBeautify from 'js-beautify'; + +export default async function test({ github, context, core }, baseUrl, routes, number) { + if (routes[0] === 'NOROUTE') { + return; + } + + const links = routes.map((e) => { + const l = e.startsWith('/') ? e : `/${e}`; + return `${baseUrl}${l}`; + }); + + let commentList = []; + let comment = `Successfully [generated](${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) as following:\n`; + + for await (const lks of links) { + core.info(`testing route: ${lks}`); + // Intended, one at a time + let success = false; + let detail; + + const res = await fetch(lks); + const body = await res.text(); + if (res.ok) { + success = true; + detail = jsBeautify.html(body.replaceAll(/\s+(\n|$)/g, '\n'), { indent_size: 2 }); + } else { + detail = `HTTPError: Response code ${res.status} (${res.statusText})`; + const errInfoList = body && body.match(/(?<=

)(.+?)(?=<\/p>)/gs); + if (errInfoList) { + detail += '\n\n'; + detail += errInfoList + .slice(0, 5) + .map((e) => (e.length > 1000 ? e.slice(0, 1000) + '...' : e).trim()) + .join('\n'); + } + } + + let routeFeedback = ` +

+${lks} - ${success ? 'Success ✔️' : 'Failed ❌'} + +\`\`\`${success ? 'rss' : ''}`; + routeFeedback += ` +${detail.slice(0, 65300 - routeFeedback.length)} +\`\`\` +
+`; + if (comment.length + routeFeedback.length >= 65500) { + comment += '\n\n...'; + commentList.push(comment); + comment = routeFeedback; + } else { + comment += routeFeedback; + } + } + + if (comment.length > 0) { + commentList.push(comment); + } + + if (commentList.length >= 5) { + commentList = commentList.slice(0, 5); + } + + if (process.env.PULL_REQUEST) { + await github.rest.issues + .addLabels({ + issue_number: number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['Auto: Route Test Complete'], + }) + .catch((error) => { + core.warning(error); + }); + } + + for await (const comment of commentList) { + // Intended, one at a time + await github.rest.issues + .createComment({ + issue_number: number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment, + }) + .catch((error) => { + core.warning(error); + }); + } +} diff --git a/tsconfig.json b/tsconfig.json index 5135a9665..20bc58343 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ESNext", "module": "ESNext", - "moduleResolution": "Bundler", + "moduleResolution": "node", "strict": true, "jsx": "react-jsx", "jsxImportSource": "hono/jsx", @@ -11,7 +11,12 @@ }, "esModuleInterop": true, "noImplicitAny": false, - "outDir": "./dist" + "outDir": "./dist", + "skipLibCheck": true, + "noEmit": true, + "incremental": true, + "resolveJsonModule": true, + "isolatedModules": true }, "include": ["./lib/**/*", "./api/vercel.ts"], "exclude": ["node_modules", "*.test.*"]