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
+
+
{{ title }}
+Public Date: {{pubDate}}
+Version: {{version}}
+Architecture: {{architecture}}
+Compactibility: {{compatibility}}
+Size: {{size}}
+Activation: {{activation}}
+
+{{ description }} +
+Change Log: ++{{ changelog }} +
+Screenshots +{{each screenshots}} +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 ? `