From 231ef297b5b650048b1daaaafa9fd78ca917eeec Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Wed, 30 Apr 2025 11:12:54 -0400 Subject: [PATCH 1/3] replace JSDOM with cheerio --- packages/backend/package.json | 7 +- .../src/core/FetchInstanceMetadataService.ts | 44 +++-- .../backend/src/misc/verify-field-link.ts | 23 +-- .../src/server/api/endpoints/i/update.ts | 3 +- packages/backend/test/e2e/oauth.ts | 10 +- packages/backend/test/utils.ts | 6 +- pnpm-lock.yaml | 187 +++++------------- 7 files changed, 94 insertions(+), 186 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 9aa26033d0..4bd940c957 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -91,8 +91,6 @@ "@swc/core": "1.11.24", "@transfem-org/sfm-js": "0.24.6", "@twemoji/parser": "15.1.1", - "@types/redis-info": "3.0.3", - "@types/psl": "^1.1.3", "accepts": "1.3.8", "ajv": "8.17.1", "archiver": "7.0.1", @@ -107,6 +105,7 @@ "cbor": "9.0.2", "chalk": "5.4.1", "chalk-template": "1.1.0", + "cheerio": "1.0.0", "chokidar": "3.6.0", "cli-highlight": "2.1.11", "color-convert": "2.0.1", @@ -132,7 +131,6 @@ "ipaddr.js": "2.2.0", "is-svg": "5.1.0", "js-yaml": "4.1.0", - "jsdom": "26.1.0", "json5": "2.2.3", "jsonld": "8.3.3", "jsrsasign": "11.1.0", @@ -209,7 +207,6 @@ "@types/http-link-header": "1.0.7", "@types/jest": "29.5.14", "@types/js-yaml": "4.0.9", - "@types/jsdom": "21.1.7", "@types/jsonld": "1.5.15", "@types/jsrsasign": "10.5.15", "@types/mime-types": "2.1.4", @@ -221,10 +218,12 @@ "@types/oauth2orize-pkce": "0.1.2", "@types/pg": "8.11.14", "@types/proxy-addr": "^2.0.3", + "@types/psl": "^1.1.3", "@types/pug": "2.0.10", "@types/qrcode": "1.5.5", "@types/random-seed": "0.3.5", "@types/ratelimiter": "3.4.6", + "@types/redis-info": "3.0.3", "@types/rename": "1.0.7", "@types/sanitize-html": "2.15.0", "@types/semver": "7.7.0", diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index ce3af7c774..5bfcfc5c98 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -5,9 +5,9 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; -import { JSDOM } from 'jsdom'; import tinycolor from 'tinycolor2'; import * as Redis from 'ioredis'; +import { load as cheerio, CheerioAPI } from 'cheerio'; import type { MiInstance } from '@/models/Instance.js'; import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; @@ -15,7 +15,6 @@ import { LoggerService } from '@/core/LoggerService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; -import type { DOMWindow } from 'jsdom'; type NodeInfo = { openRegistrations?: unknown; @@ -181,17 +180,14 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchDom(instance: MiInstance): Promise { + private async fetchDom(instance: MiInstance): Promise { this.logger.info(`Fetching HTML of ${instance.host} ...`); const url = 'https://' + instance.host; const html = await this.httpRequestService.getHtml(url); - const { window } = new JSDOM(html); - const doc = window.document; - - return doc; + return cheerio(html); } @bindThis @@ -206,12 +202,15 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchFaviconUrl(instance: MiInstance, doc: Document | null): Promise { + private async fetchFaviconUrl(instance: MiInstance, doc: CheerioAPI | null): Promise { const url = 'https://' + instance.host; if (doc) { // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 - const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.relList.contains('icon'))?.href; + const href = doc('link[rel][href]') + .filter((_, link) => link.attribs.rel.split(' ').includes('icon')) + .last() + .attr('href'); if (href) { return (new URL(href, url)).href; @@ -232,7 +231,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async fetchIconUrl(instance: MiInstance, doc: Document | null, manifest: Record | null): Promise { + private async fetchIconUrl(instance: MiInstance, doc: CheerioAPI | null, manifest: Record | null): Promise { if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { const url = 'https://' + instance.host; return (new URL(manifest.icons[0].src, url)).href; @@ -242,13 +241,16 @@ export class FetchInstanceMetadataService { const url = 'https://' + instance.host; // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 - const links = Array.from(doc.getElementsByTagName('link')).reverse(); + const links = Array.from(doc('link[rel][href]')).reverse().map(link => ({ + rel: link.attribs.rel.split(' '), + href: link.attribs.href, + })); // https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559 const href = [ - links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href, - links.find(link => link.relList.contains('apple-touch-icon'))?.href, - links.find(link => link.relList.contains('icon'))?.href, + links.find(link => link.rel.includes('apple-touch-icon-precomposed'))?.href, + links.find(link => link.rel.includes('apple-touch-icon'))?.href, + links.find(link => link.rel.includes('icon'))?.href, ] .find(href => href); @@ -261,8 +263,8 @@ export class FetchInstanceMetadataService { } @bindThis - private async getThemeColor(info: NodeInfo | null, doc: Document | null, manifest: Record | null): Promise { - const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; + private async getThemeColor(info: NodeInfo | null, doc: CheerioAPI | null, manifest: Record | null): Promise { + const themeColor = info?.metadata?.themeColor ?? doc?.('meta[name="theme-color"][content]').attr('content') ?? manifest?.theme_color; if (themeColor) { const color = new tinycolor(themeColor); @@ -273,7 +275,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async getSiteName(info: NodeInfo | null, doc: Document | null, manifest: Record | null): Promise { + private async getSiteName(info: NodeInfo | null, doc: CheerioAPI | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeName === 'string') { return info.metadata.nodeName; @@ -283,7 +285,7 @@ export class FetchInstanceMetadataService { } if (doc) { - const og = doc.querySelector('meta[property="og:title"]')?.getAttribute('content'); + const og = doc('meta[property="og:title"][content]').attr('content'); if (og) { return og; @@ -298,7 +300,7 @@ export class FetchInstanceMetadataService { } @bindThis - private async getDescription(info: NodeInfo | null, doc: Document | null, manifest: Record | null): Promise { + private async getDescription(info: NodeInfo | null, doc: CheerioAPI | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeDescription === 'string') { return info.metadata.nodeDescription; @@ -308,12 +310,12 @@ export class FetchInstanceMetadataService { } if (doc) { - const meta = doc.querySelector('meta[name="description"]')?.getAttribute('content'); + const meta = doc('meta[name="description"][content]').attr('content'); if (meta) { return meta; } - const og = doc.querySelector('meta[property="og:description"]')?.getAttribute('content'); + const og = doc('meta[property="og:description"][content]').attr('content'); if (og) { return og; } diff --git a/packages/backend/src/misc/verify-field-link.ts b/packages/backend/src/misc/verify-field-link.ts index f519acfba0..62542eaaa0 100644 --- a/packages/backend/src/misc/verify-field-link.ts +++ b/packages/backend/src/misc/verify-field-link.ts @@ -3,32 +3,29 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { JSDOM } from 'jsdom'; +import { load as cheerio } from 'cheerio'; import type { HttpRequestService } from '@/core/HttpRequestService.js'; type Field = { name: string, value: string }; export async function verifyFieldLinks(fields: Field[], profile_url: string, httpRequestService: HttpRequestService): Promise { const verified_links = []; - for (const field_url of fields - .filter(x => URL.canParse(x.value) && ['http:', 'https:'].includes((new URL(x.value).protocol)))) { + for (const field_url of fields.filter(x => URL.canParse(x.value) && ['http:', 'https:'].includes((new URL(x.value).protocol)))) { try { const html = await httpRequestService.getHtml(field_url.value); - const { window } = new JSDOM(html); - const doc: Document = window.document; + const doc = cheerio(html); - const aEls = Array.from(doc.getElementsByTagName('a')); - const linkEls = Array.from(doc.getElementsByTagName('link')); + const links = doc('a[rel~="me"][href], link[rel~="me"][href]').toArray(); - const includesProfileLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === profile_url); - if (includesProfileLinks) { verified_links.push(field_url.value); } - - window.close(); - } catch (err) { + const includesProfileLinks = links.some(link => link.attribs.href === profile_url); + if (includesProfileLinks) { + verified_links.push(field_url.value); + } + } catch { // don't do anything. - continue; } } + return verified_links; } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 5f93597fd7..ad8f38703b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -6,7 +6,6 @@ import * as mfm from '@transfem-org/sfm-js'; import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; -import { JSDOM } from 'jsdom'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; import * as Acct from '@/misc/acct.js'; @@ -622,6 +621,7 @@ export default class extends Endpoint { // eslint- } // this function is superseded by '@/misc/verify-field-link.ts' + /* private async verifyLink(url: string, user: MiLocalUser) { if (!safeForSql(url)) return; @@ -653,6 +653,7 @@ export default class extends Endpoint { // eslint- // なにもしない } } + */ // these two methods need to be kept in sync with // `ApRendererService.renderPerson` diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index 7434701e67..47851e9474 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -19,7 +19,7 @@ import { ResourceOwnerPassword, } from 'simple-oauth2'; import pkceChallenge from 'pkce-challenge'; -import { JSDOM } from 'jsdom'; +import { load as cheerio } from 'cheerio'; import Fastify, { type FastifyInstance, type FastifyReply } from 'fastify'; import { api, port, sendEnvUpdateRequest, signup } from '../utils.js'; import type * as misskey from 'misskey-js'; @@ -73,11 +73,11 @@ const clientConfig: ModuleOptions<'client_id'> = { }; function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined, clientLogo: string | undefined } { - const fragment = JSDOM.fragment(html); + const fragment = cheerio(html); return { - transactionId: fragment.querySelector('meta[name="misskey:oauth:transaction-id"]')?.content, - clientName: fragment.querySelector('meta[name="misskey:oauth:client-name"]')?.content, - clientLogo: fragment.querySelector('meta[name="misskey:oauth:client-logo"]')?.content, + transactionId: fragment('meta[name="misskey:oauth:transaction-id"][content]').attr('content'), + clientName: fragment('meta[name="misskey:oauth:client-name"][content]').attr('content'), + clientLogo: fragment('meta[name="misskey:oauth:client-logo"][content]').attr('content'), }; } diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 7b69cb04f4..70deff2e2d 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -11,7 +11,7 @@ import { inspect } from 'node:util'; import WebSocket, { ClientOptions } from 'ws'; import fetch, { File, RequestInit, type Headers } from 'node-fetch'; import { DataSource } from 'typeorm'; -import { JSDOM } from 'jsdom'; +import { load as cheerio, CheerioAPI } from 'cheerio'; import { type Response } from 'node-fetch'; import Fastify from 'fastify'; import { entities } from '../src/postgres.js'; @@ -464,7 +464,7 @@ export function makeStreamCatcher( export type SimpleGetResponse = { status: number, - body: any | JSDOM | null, + body: any | CheerioAPI | null, type: string | null, location: string | null }; @@ -495,7 +495,7 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde const body = jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() : - htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) : + htmlTypes.includes(res.headers.get('content-type') ?? '') ? cheerio(await res.text()) : await bodyExtractor(res); return { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 691069f563..69c37b12fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,12 +161,6 @@ importers: '@twemoji/parser': specifier: 15.1.1 version: 15.1.1 - '@types/psl': - specifier: ^1.1.3 - version: 1.1.3 - '@types/redis-info': - specifier: 3.0.3 - version: 3.0.3 accepts: specifier: 1.3.8 version: 1.3.8 @@ -209,6 +203,9 @@ importers: chalk-template: specifier: 1.1.0 version: 1.1.0 + cheerio: + specifier: 1.0.0 + version: 1.0.0 chokidar: specifier: 4.0.3 version: 4.0.3 @@ -284,9 +281,6 @@ importers: js-yaml: specifier: 4.1.0 version: 4.1.0 - jsdom: - specifier: 26.1.0 - version: 26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) json5: specifier: 2.2.3 version: 2.2.3 @@ -592,9 +586,6 @@ importers: '@types/js-yaml': specifier: 4.0.9 version: 4.0.9 - '@types/jsdom': - specifier: 21.1.7 - version: 21.1.7 '@types/jsonld': specifier: 1.5.15 version: 1.5.15 @@ -628,6 +619,9 @@ importers: '@types/proxy-addr': specifier: ^2.0.3 version: 2.0.3 + '@types/psl': + specifier: ^1.1.3 + version: 1.1.3 '@types/pug': specifier: 2.0.10 version: 2.0.10 @@ -640,6 +634,9 @@ importers: '@types/ratelimiter': specifier: 3.4.6 version: 3.4.6 + '@types/redis-info': + specifier: 3.0.3 + version: 3.0.3 '@types/rename': specifier: 1.0.7 version: 1.0.7 @@ -1016,7 +1013,7 @@ importers: version: 8.31.0(eslint@9.25.1)(typescript@5.8.3) '@vitest/coverage-v8': specifier: 3.1.2 - version: 3.1.2(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)) + version: 3.1.2(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0)(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)) '@vue/compiler-core': specifier: 3.5.13 version: 3.5.13 @@ -1085,7 +1082,7 @@ importers: version: 1.0.3 vitest: specifier: 3.1.2 - version: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) + version: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0)(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) vitest-fetch-mock: specifier: 0.4.5 version: 0.4.5(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)) @@ -4220,9 +4217,6 @@ packages: '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} - '@types/jsdom@21.1.7': - resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -4400,9 +4394,6 @@ packages: '@types/tmp@0.2.6': resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} - '@types/tough-cookie@4.0.2': - resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} - '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -10327,10 +10318,6 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici@5.28.2: - resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==} - engines: {node: '>=14.0'} - undici@5.29.0: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} @@ -10918,6 +10905,7 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 lru-cache: 10.4.3 + optional: true '@aws-crypto/crc32@5.2.0': dependencies: @@ -11851,12 +11839,14 @@ snapshots: '@cropper/utils@2.0.0': {} - '@csstools/color-helpers@5.0.2': {} + '@csstools/color-helpers@5.0.2': + optional: true '@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 + optional: true '@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: @@ -11864,12 +11854,15 @@ snapshots: '@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 + optional: true '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': dependencies: '@csstools/css-tokenizer': 3.0.3 + optional: true - '@csstools/css-tokenizer@3.0.3': {} + '@csstools/css-tokenizer@3.0.3': + optional: true '@cypress/request@3.0.8': dependencies: @@ -11905,7 +11898,7 @@ snapshots: dependencies: ky: 0.33.3 ky-universal: 0.11.0(ky@0.33.3)(web-streams-polyfill@4.0.0) - undici: 5.28.2 + undici: 5.29.0 transitivePeerDependencies: - web-streams-polyfill @@ -14454,12 +14447,6 @@ snapshots: '@types/js-yaml@4.0.9': {} - '@types/jsdom@21.1.7': - dependencies: - '@types/node': 22.15.2 - '@types/tough-cookie': 4.0.2 - parse5: 7.3.0 - '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -14634,8 +14621,6 @@ snapshots: '@types/tmp@0.2.6': {} - '@types/tough-cookie@4.0.2': {} - '@types/tough-cookie@4.0.5': {} '@types/unist@3.0.2': {} @@ -14749,24 +14734,6 @@ snapshots: vite: 6.3.3(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) vue: 3.5.13(typescript@5.8.3) - '@vitest/coverage-v8@3.1.2(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3))': - dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0(supports-color@8.1.1) - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.9.0 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) - transitivePeerDependencies: - - supports-color - '@vitest/coverage-v8@3.1.2(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0)(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3))': dependencies: '@ampproject/remapping': 2.3.0 @@ -16181,6 +16148,7 @@ snapshots: dependencies: '@asamuzakjp/css-color': 3.1.1 rrweb-cssom: 0.8.0 + optional: true csstype@3.1.3: {} @@ -16241,6 +16209,7 @@ snapshots: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 + optional: true data-view-buffer@1.0.1: dependencies: @@ -16311,7 +16280,8 @@ snapshots: decamelize@1.2.0: {} - decimal.js@10.5.0: {} + decimal.js@10.5.0: + optional: true decode-bmp@0.2.1: dependencies: @@ -17680,6 +17650,7 @@ snapshots: html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 + optional: true html-entities@2.5.2: {} @@ -17983,7 +17954,8 @@ snapshots: is-plain-object@5.0.0: {} - is-potential-custom-element-name@1.0.1: {} + is-potential-custom-element-name@1.0.1: + optional: true is-promise@2.2.2: {} @@ -18496,7 +18468,7 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} - jsdom@26.1.0: + jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: cssstyle: 4.3.0 data-urls: 5.0.0 @@ -18524,35 +18496,6 @@ snapshots: - utf-8-validate optional: true - jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5): - dependencies: - cssstyle: 4.3.0 - data-urls: 5.0.0 - decimal.js: 10.5.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.19 - parse5: 7.3.0 - rrweb-cssom: 0.8.0 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 5.1.2 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) - xml-name-validator: 5.0.0 - optionalDependencies: - canvas: 3.1.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - jsesc@2.5.2: {} json-buffer@3.0.1: {} @@ -19561,7 +19504,8 @@ snapshots: dependencies: boolbase: 1.0.0 - nwsapi@2.2.19: {} + nwsapi@2.2.19: + optional: true oauth2orize-pkce@0.1.2: {} @@ -20650,7 +20594,8 @@ snapshots: transitivePeerDependencies: - supports-color - rrweb-cssom@0.8.0: {} + rrweb-cssom@0.8.0: + optional: true rss-parser@3.13.0: dependencies: @@ -20712,6 +20657,7 @@ snapshots: saxes@6.0.0: dependencies: xmlchars: 2.2.0 + optional: true scheduler@0.26.0: {} @@ -21366,7 +21312,8 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 - symbol-tree@3.2.4: {} + symbol-tree@3.2.4: + optional: true systeminformation@5.25.11: {} @@ -21484,11 +21431,13 @@ snapshots: tinyspy@3.0.2: {} - tldts-core@6.1.63: {} + tldts-core@6.1.63: + optional: true tldts@6.1.63: dependencies: tldts-core: 6.1.63 + optional: true tmp@0.2.3: {} @@ -21532,12 +21481,14 @@ snapshots: tough-cookie@5.1.2: dependencies: tldts: 6.1.63 + optional: true tr46@0.0.3: {} tr46@5.1.0: dependencies: punycode: 2.3.1 + optional: true tree-kill@1.2.2: optional: true @@ -21743,10 +21694,6 @@ snapshots: undici-types@6.21.0: {} - undici@5.28.2: - dependencies: - '@fastify/busboy': 2.1.0 - undici@5.29.0: dependencies: '@fastify/busboy': 2.1.0 @@ -21941,49 +21888,7 @@ snapshots: vitest-fetch-mock@0.4.5(vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)): dependencies: - vitest: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) - - vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3): - dependencies: - '@vitest/expect': 3.1.2 - '@vitest/mocker': 3.1.2(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(vite@6.3.3(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3)) - '@vitest/pretty-format': 3.1.2 - '@vitest/runner': 3.1.2 - '@vitest/snapshot': 3.1.2 - '@vitest/spy': 3.1.2 - '@vitest/utils': 3.1.2 - chai: 5.2.0 - debug: 4.4.0(supports-color@8.1.1) - expect-type: 1.2.1 - magic-string: 0.30.17 - pathe: 2.0.3 - std-env: 3.9.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.13 - tinypool: 1.0.2 - tinyrainbow: 2.0.0 - vite: 6.3.3(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) - vite-node: 3.1.2(@types/node@22.15.2)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/debug': 4.1.12 - '@types/node': 22.15.2 - happy-dom: 17.4.4 - jsdom: 26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml + vitest: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0)(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3) vitest@3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(happy-dom@17.4.4)(jsdom@26.1.0)(msw@2.7.5(@types/node@22.15.2)(typescript@5.8.3))(sass@1.87.0)(terser@5.39.0)(tsx@4.19.3): dependencies: @@ -22012,7 +21917,7 @@ snapshots: '@types/debug': 4.1.12 '@types/node': 22.15.2 happy-dom: 17.4.4 - jsdom: 26.1.0 + jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti - less @@ -22132,6 +22037,7 @@ snapshots: w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 + optional: true wait-on@8.0.3(debug@4.4.0): dependencies: @@ -22192,6 +22098,7 @@ snapshots: dependencies: tr46: 5.1.0 webidl-conversions: 7.0.0 + optional: true whatwg-url@5.0.0: dependencies: @@ -22295,7 +22202,8 @@ snapshots: xml-name-validator@4.0.0: {} - xml-name-validator@5.0.0: {} + xml-name-validator@5.0.0: + optional: true xml2js@0.5.0: dependencies: @@ -22304,7 +22212,8 @@ snapshots: xmlbuilder@11.0.1: {} - xmlchars@2.2.0: {} + xmlchars@2.2.0: + optional: true xtend@4.0.2: {} From 261a7e3ab3063ec0955a9ea4242dce81eaad4544 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Sat, 3 May 2025 22:25:33 -0400 Subject: [PATCH 2/3] fix type errors --- packages/backend/src/server/api/mastodon/endpoints/search.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts index 33bfa87e5f..78672639e5 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/search.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts @@ -112,7 +112,7 @@ export class ApiSearchMastodon { { method: 'POST', headers: { - ...request.headers as HeadersInit, + ...request.headers, 'Accept': 'application/json', 'Content-Type': 'application/json', }, @@ -135,7 +135,7 @@ export class ApiSearchMastodon { { method: 'POST', headers: { - ...request.headers as HeadersInit, + ...request.headers, 'Accept': 'application/json', 'Content-Type': 'application/json', }, From c43ac87df27e0b88194b315b077ccb43df11ef9c Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 8 May 2025 11:31:36 -0400 Subject: [PATCH 3/3] separate type imports for Cheerio --- packages/backend/src/core/FetchInstanceMetadataService.ts | 3 ++- packages/backend/test/utils.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 5bfcfc5c98..980f1fcacf 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -7,7 +7,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import tinycolor from 'tinycolor2'; import * as Redis from 'ioredis'; -import { load as cheerio, CheerioAPI } from 'cheerio'; +import { load as cheerio } from 'cheerio'; import type { MiInstance } from '@/models/Instance.js'; import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; @@ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import type { CheerioAPI } from 'cheerio'; type NodeInfo = { openRegistrations?: unknown; diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 70deff2e2d..7f2768488f 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -11,11 +11,12 @@ import { inspect } from 'node:util'; import WebSocket, { ClientOptions } from 'ws'; import fetch, { File, RequestInit, type Headers } from 'node-fetch'; import { DataSource } from 'typeorm'; -import { load as cheerio, CheerioAPI } from 'cheerio'; +import { load as cheerio } from 'cheerio'; import { type Response } from 'node-fetch'; import Fastify from 'fastify'; import { entities } from '../src/postgres.js'; import { loadConfig } from '../src/config.js'; +import type { CheerioAPI } from 'cheerio'; import type * as misskey from 'misskey-js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';