merge: Replace JSDOM with cheerio (!973)

View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/973

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
Marie 2025-05-08 16:09:36 +00:00
commit c5f5c6fef0
8 changed files with 98 additions and 188 deletions

View file

@ -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",
@ -108,6 +106,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",

View file

@ -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 } from 'cheerio';
import type { MiInstance } from '@/models/Instance.js';
import type Logger from '@/logger.js';
import { DI } from '@/di-symbols.js';
@ -15,7 +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 { DOMWindow } from 'jsdom';
import type { CheerioAPI } from 'cheerio';
type NodeInfo = {
openRegistrations?: unknown;
@ -181,17 +181,14 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchDom(instance: MiInstance): Promise<Document> {
private async fetchDom(instance: MiInstance): Promise<CheerioAPI> {
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 +203,15 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchFaviconUrl(instance: MiInstance, doc: Document | null): Promise<string | null> {
private async fetchFaviconUrl(instance: MiInstance, doc: CheerioAPI | null): Promise<string | null> {
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 +232,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async fetchIconUrl(instance: MiInstance, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async fetchIconUrl(instance: MiInstance, doc: CheerioAPI | null, manifest: Record<string, any> | null): Promise<string | null> {
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 +242,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 +264,8 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getThemeColor(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
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<string, any> | null): Promise<string | null> {
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 +276,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getSiteName(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async getSiteName(info: NodeInfo | null, doc: CheerioAPI | null, manifest: Record<string, any> | null): Promise<string | null> {
if (info && info.metadata) {
if (typeof info.metadata.nodeName === 'string') {
return info.metadata.nodeName;
@ -283,7 +286,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 +301,7 @@ export class FetchInstanceMetadataService {
}
@bindThis
private async getDescription(info: NodeInfo | null, doc: Document | null, manifest: Record<string, any> | null): Promise<string | null> {
private async getDescription(info: NodeInfo | null, doc: CheerioAPI | null, manifest: Record<string, any> | null): Promise<string | null> {
if (info && info.metadata) {
if (typeof info.metadata.nodeDescription === 'string') {
return info.metadata.nodeDescription;
@ -308,12 +311,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;
}

View file

@ -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<string[]> {
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;
}

View file

@ -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<typeof meta, typeof paramDef> { // 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<typeof meta, typeof paramDef> { // eslint-
// なにもしない
}
}
*/
// these two methods need to be kept in sync with
// `ApRendererService.renderPerson`

View file

@ -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',
},

View file

@ -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<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]')?.content,
clientName: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content,
clientLogo: fragment.querySelector<HTMLMetaElement>('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'),
};
}

View file

@ -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 { JSDOM } from 'jsdom';
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';
@ -464,7 +465,7 @@ export function makeStreamCatcher<T>(
export type SimpleGetResponse = {
status: number,
body: any | JSDOM | null,
body: any | CheerioAPI | null,
type: string | null,
location: string | null
};
@ -495,7 +496,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 {

187
pnpm-lock.yaml generated
View file

@ -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
@ -212,6 +206,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))
@ -4216,9 +4213,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==}
@ -4396,9 +4390,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==}
@ -10300,10 +10291,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'}
@ -10891,6 +10878,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:
@ -11824,12 +11812,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:
@ -11837,12 +11827,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:
@ -11878,7 +11871,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
@ -14423,12 +14416,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': {}
@ -14603,8 +14590,6 @@ snapshots:
'@types/tmp@0.2.6': {}
'@types/tough-cookie@4.0.2': {}
'@types/tough-cookie@4.0.5': {}
'@types/unist@3.0.2': {}
@ -14718,24 +14703,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
@ -16143,6 +16110,7 @@ snapshots:
dependencies:
'@asamuzakjp/css-color': 3.1.1
rrweb-cssom: 0.8.0
optional: true
csstype@3.1.3: {}
@ -16203,6 +16171,7 @@ snapshots:
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
optional: true
data-view-buffer@1.0.1:
dependencies:
@ -16273,7 +16242,8 @@ snapshots:
decamelize@1.2.0: {}
decimal.js@10.5.0: {}
decimal.js@10.5.0:
optional: true
decode-bmp@0.2.1:
dependencies:
@ -17625,6 +17595,7 @@ snapshots:
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
optional: true
html-entities@2.5.2: {}
@ -17928,7 +17899,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: {}
@ -18441,7 +18413,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
@ -18469,35 +18441,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: {}
@ -19506,7 +19449,8 @@ snapshots:
dependencies:
boolbase: 1.0.0
nwsapi@2.2.19: {}
nwsapi@2.2.19:
optional: true
oauth2orize-pkce@0.1.2: {}
@ -20595,7 +20539,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
rrweb-cssom@0.8.0: {}
rrweb-cssom@0.8.0:
optional: true
rss-parser@3.13.0:
dependencies:
@ -20657,6 +20602,7 @@ snapshots:
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
optional: true
scheduler@0.26.0: {}
@ -21311,7 +21257,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: {}
@ -21427,11 +21374,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: {}
@ -21475,12 +21424,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
@ -21686,10 +21637,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
@ -21884,49 +21831,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:
@ -21955,7 +21860,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
@ -22075,6 +21980,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:
@ -22135,6 +22041,7 @@ snapshots:
dependencies:
tr46: 5.1.0
webidl-conversions: 7.0.0
optional: true
whatwg-url@5.0.0:
dependencies:
@ -22238,7 +22145,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:
@ -22247,7 +22155,8 @@ snapshots:
xmlbuilder@11.0.1: {}
xmlchars@2.2.0: {}
xmlchars@2.2.0:
optional: true
xtend@4.0.2: {}