use microformats-parser
This commit is contained in:
parent
4f35af5590
commit
c1d2f72e61
|
@ -61,7 +61,7 @@
|
||||||
"@fastify/accepts": "4.2.0",
|
"@fastify/accepts": "4.2.0",
|
||||||
"@fastify/cookie": "8.3.0",
|
"@fastify/cookie": "8.3.0",
|
||||||
"@fastify/cors": "8.3.0",
|
"@fastify/cors": "8.3.0",
|
||||||
"@fastify/express": "^2.3.0",
|
"@fastify/express": "2.3.0",
|
||||||
"@fastify/http-proxy": "9.2.1",
|
"@fastify/http-proxy": "9.2.1",
|
||||||
"@fastify/multipart": "7.7.0",
|
"@fastify/multipart": "7.7.0",
|
||||||
"@fastify/static": "6.10.2",
|
"@fastify/static": "6.10.2",
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "4.1.0",
|
"bullmq": "4.1.0",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.0",
|
"cbor": "9.0.0",
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
"got": "13.0.0",
|
"got": "13.0.0",
|
||||||
"happy-dom": "9.20.3",
|
"happy-dom": "9.20.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"http-link-header": "^1.1.0",
|
"http-link-header": "1.1.0",
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"ip-cidr": "3.1.0",
|
"ip-cidr": "3.1.0",
|
||||||
"ipaddr.js": "2.1.0",
|
"ipaddr.js": "2.1.0",
|
||||||
|
@ -112,6 +112,7 @@
|
||||||
"jsrsasign": "10.8.6",
|
"jsrsasign": "10.8.6",
|
||||||
"meilisearch": "0.33.0",
|
"meilisearch": "0.33.0",
|
||||||
"mfm-js": "0.23.3",
|
"mfm-js": "0.23.3",
|
||||||
|
"microformats-parser": "1.4.1",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
|
@ -120,13 +121,13 @@
|
||||||
"nodemailer": "6.9.3",
|
"nodemailer": "6.9.3",
|
||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "2.4.2",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"oauth2orize": "^1.11.1",
|
"oauth2orize": "1.11.1",
|
||||||
"oauth2orize-pkce": "^0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.1.2",
|
"otpauth": "9.1.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.0",
|
"pg": "8.11.0",
|
||||||
"pkce-challenge": "^4.0.1",
|
"pkce-challenge": "4.0.1",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
"pug": "3.0.2",
|
"pug": "3.0.2",
|
||||||
|
@ -172,25 +173,25 @@
|
||||||
"@types/accepts": "1.3.5",
|
"@types/accepts": "1.3.5",
|
||||||
"@types/archiver": "5.3.2",
|
"@types/archiver": "5.3.2",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/body-parser": "^1.19.2",
|
"@types/body-parser": "1.19.2",
|
||||||
"@types/cbor": "6.0.0",
|
"@types/cbor": "6.0.0",
|
||||||
"@types/color-convert": "2.0.0",
|
"@types/color-convert": "2.0.0",
|
||||||
"@types/content-disposition": "0.5.5",
|
"@types/content-disposition": "0.5.5",
|
||||||
"@types/escape-regexp": "0.0.1",
|
"@types/escape-regexp": "0.0.1",
|
||||||
"@types/fluent-ffmpeg": "2.1.21",
|
"@types/fluent-ffmpeg": "2.1.21",
|
||||||
"@types/http-link-header": "^1.0.3",
|
"@types/http-link-header": "1.0.3",
|
||||||
"@types/jest": "29.5.2",
|
"@types/jest": "29.5.2",
|
||||||
"@types/js-yaml": "4.0.5",
|
"@types/js-yaml": "4.0.5",
|
||||||
"@types/jsdom": "21.1.1",
|
"@types/jsdom": "21.1.1",
|
||||||
"@types/jsonld": "1.5.9",
|
"@types/jsonld": "1.5.9",
|
||||||
"@types/jsrsasign": "10.5.8",
|
"@types/jsrsasign": "10.5.8",
|
||||||
"@types/mime-types": "2.1.1",
|
"@types/mime-types": "2.1.1",
|
||||||
"@types/ms": "^0.7.31",
|
"@types/ms": "0.7.31",
|
||||||
"@types/node": "20.3.1",
|
"@types/node": "20.3.1",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.8",
|
"@types/nodemailer": "6.4.8",
|
||||||
"@types/oauth": "0.9.1",
|
"@types/oauth": "0.9.1",
|
||||||
"@types/oauth2orize": "^1.11.0",
|
"@types/oauth2orize": "1.11.0",
|
||||||
"@types/pg": "8.10.2",
|
"@types/pg": "8.10.2",
|
||||||
"@types/pug": "2.0.6",
|
"@types/pug": "2.0.6",
|
||||||
"@types/punycode": "2.1.0",
|
"@types/punycode": "2.1.0",
|
||||||
|
@ -202,7 +203,7 @@
|
||||||
"@types/sanitize-html": "2.9.0",
|
"@types/sanitize-html": "2.9.0",
|
||||||
"@types/semver": "7.5.0",
|
"@types/semver": "7.5.0",
|
||||||
"@types/sharp": "0.32.0",
|
"@types/sharp": "0.32.0",
|
||||||
"@types/simple-oauth2": "^5.0.4",
|
"@types/simple-oauth2": "5.0.4",
|
||||||
"@types/sinonjs__fake-timers": "8.1.2",
|
"@types/sinonjs__fake-timers": "8.1.2",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@types/tmp": "0.2.3",
|
"@types/tmp": "0.2.3",
|
||||||
|
@ -221,6 +222,6 @@
|
||||||
"execa": "6.1.0",
|
"execa": "6.1.0",
|
||||||
"jest": "29.5.0",
|
"jest": "29.5.0",
|
||||||
"jest-mock": "29.5.0",
|
"jest-mock": "29.5.0",
|
||||||
"simple-oauth2": "^5.0.0"
|
"simple-oauth2": "5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import pug from 'pug';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
import fastifyExpress from '@fastify/express';
|
import fastifyExpress from '@fastify/express';
|
||||||
import { verifyChallenge } from 'pkce-challenge';
|
import { verifyChallenge } from 'pkce-challenge';
|
||||||
|
import { mf2 } from 'microformats-parser';
|
||||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { kinds } from '@/misc/api-permissions.js';
|
import { kinds } from '@/misc/api-permissions.js';
|
||||||
|
@ -24,6 +25,7 @@ import type { LocalUser } from '@/models/entities/User.js';
|
||||||
import { MemoryKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache } from '@/misc/cache.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import Logger from '@/logger.js';
|
import Logger from '@/logger.js';
|
||||||
|
import type { StatusError } from '@/misc/status-error.js';
|
||||||
import type { ServerResponse } from 'node:http';
|
import type { ServerResponse } from 'node:http';
|
||||||
import type { FastifyInstance } from 'fastify';
|
import type { FastifyInstance } from 'fastify';
|
||||||
|
|
||||||
|
@ -111,20 +113,33 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
|
||||||
redirectUris.push(...httpLinkHeader.parse(linkHeader).get('rel', 'redirect_uri').map(r => r.uri));
|
redirectUris.push(...httpLinkHeader.parse(linkHeader).get('rel', 'redirect_uri').map(r => r.uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
const fragment = JSDOM.fragment(await res.text());
|
const text = await res.text();
|
||||||
|
const fragment = JSDOM.fragment(text);
|
||||||
|
|
||||||
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
|
redirectUris.push(...[...fragment.querySelectorAll<HTMLLinkElement>('link[rel=redirect_uri][href]')].map(el => el.href));
|
||||||
|
|
||||||
const name = fragment.querySelector<HTMLElement>('.h-app .p-name')?.textContent?.trim() ?? id;
|
let name = id;
|
||||||
|
if (text) {
|
||||||
|
const microformats = mf2(text, { baseUrl: res.url });
|
||||||
|
const nameProperty = microformats.items.find(item => item.type?.includes('h-app') && item.properties.url?.includes(id))?.properties.name[0];
|
||||||
|
if (typeof nameProperty === 'string') {
|
||||||
|
name = nameProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()),
|
redirectUris: redirectUris.map(uri => new URL(uri, res.url).toString()),
|
||||||
name,
|
name: typeof name === 'string' ? name : id,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to fetch client information', { err });
|
console.error(err);
|
||||||
throw new AuthorizationError('Failed to fetch client information', 'server_error');
|
logger.error('Error while fetching client information', { err });
|
||||||
|
if (err instanceof StatusError) {
|
||||||
|
throw new AuthorizationError('Failed to fetch client information', 'invalid_request');
|
||||||
|
} else {
|
||||||
|
throw new AuthorizationError('Failed to parse client information', 'server_error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ describe('OAuth', () => {
|
||||||
reply.send(`
|
reply.send(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<link rel="redirect_uri" href="/redirect" />
|
<link rel="redirect_uri" href="/redirect" />
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -807,7 +807,7 @@ describe('OAuth', () => {
|
||||||
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||||
reply.send(`
|
reply.send(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
'Mixed links': reply => {
|
'Mixed links': reply => {
|
||||||
|
@ -815,14 +815,14 @@ describe('OAuth', () => {
|
||||||
reply.send(`
|
reply.send(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<link rel="redirect_uri" href="/redirect2" />
|
<link rel="redirect_uri" href="/redirect2" />
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
'Multiple items in Link header': reply => {
|
'Multiple items in Link header': reply => {
|
||||||
reply.header('Link', '</redirect2>; rel="redirect_uri",</redirect>; rel="redirect_uri"');
|
reply.header('Link', '</redirect2>; rel="redirect_uri",</redirect>; rel="redirect_uri"');
|
||||||
reply.send(`
|
reply.send(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
'Multiple items in HTML': reply => {
|
'Multiple items in HTML': reply => {
|
||||||
|
@ -830,7 +830,7 @@ describe('OAuth', () => {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<link rel="redirect_uri" href="/redirect2" />
|
<link rel="redirect_uri" href="/redirect2" />
|
||||||
<link rel="redirect_uri" href="/redirect" />
|
<link rel="redirect_uri" href="/redirect" />
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -856,7 +856,7 @@ describe('OAuth', () => {
|
||||||
sender = (reply): void => {
|
sender = (reply): void => {
|
||||||
reply.send(`
|
reply.send(`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div class="h-app"><div class="p-name">Misklient
|
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||||
`);
|
`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -907,6 +907,29 @@ describe('OAuth', () => {
|
||||||
assert.strictEqual(response.status, 200);
|
assert.strictEqual(response.status, 200);
|
||||||
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Mismatching URL in h-app', async () => {
|
||||||
|
sender = (reply): void => {
|
||||||
|
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||||
|
reply.send(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<div class="h-app"><a href="/foo" class="u-url p-name">Misklient
|
||||||
|
`);
|
||||||
|
reply.send();
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = new AuthorizationCode(clientConfig);
|
||||||
|
|
||||||
|
const response = await fetch(client.authorizeURL({
|
||||||
|
redirect_uri,
|
||||||
|
scope: 'write:notes',
|
||||||
|
state: 'state',
|
||||||
|
code_challenge: 'code',
|
||||||
|
code_challenge_method: 'S256',
|
||||||
|
} as AuthorizationParamsExtended));
|
||||||
|
assert.strictEqual(response.status, 200);
|
||||||
|
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Unknown OAuth endpoint', async () => {
|
test('Unknown OAuth endpoint', async () => {
|
||||||
|
|
|
@ -99,7 +99,7 @@ importers:
|
||||||
specifier: 8.3.0
|
specifier: 8.3.0
|
||||||
version: 8.3.0
|
version: 8.3.0
|
||||||
'@fastify/express':
|
'@fastify/express':
|
||||||
specifier: ^2.3.0
|
specifier: 2.3.0
|
||||||
version: 2.3.0
|
version: 2.3.0
|
||||||
'@fastify/http-proxy':
|
'@fastify/http-proxy':
|
||||||
specifier: 9.2.1
|
specifier: 9.2.1
|
||||||
|
@ -153,7 +153,7 @@ importers:
|
||||||
specifier: 2.0.5
|
specifier: 2.0.5
|
||||||
version: 2.0.5
|
version: 2.0.5
|
||||||
body-parser:
|
body-parser:
|
||||||
specifier: ^1.20.2
|
specifier: 1.20.2
|
||||||
version: 1.20.2
|
version: 1.20.2
|
||||||
bullmq:
|
bullmq:
|
||||||
specifier: 4.1.0
|
specifier: 4.1.0
|
||||||
|
@ -216,7 +216,7 @@ importers:
|
||||||
specifier: 1.2.0
|
specifier: 1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
http-link-header:
|
http-link-header:
|
||||||
specifier: ^1.1.0
|
specifier: 1.1.0
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
ioredis:
|
ioredis:
|
||||||
specifier: 5.3.2
|
specifier: 5.3.2
|
||||||
|
@ -251,6 +251,9 @@ importers:
|
||||||
mfm-js:
|
mfm-js:
|
||||||
specifier: 0.23.3
|
specifier: 0.23.3
|
||||||
version: 0.23.3
|
version: 0.23.3
|
||||||
|
microformats-parser:
|
||||||
|
specifier: 1.4.1
|
||||||
|
version: 1.4.1
|
||||||
mime-types:
|
mime-types:
|
||||||
specifier: 2.1.35
|
specifier: 2.1.35
|
||||||
version: 2.1.35
|
version: 2.1.35
|
||||||
|
@ -276,10 +279,10 @@ importers:
|
||||||
specifier: 0.10.0
|
specifier: 0.10.0
|
||||||
version: 0.10.0
|
version: 0.10.0
|
||||||
oauth2orize:
|
oauth2orize:
|
||||||
specifier: ^1.11.1
|
specifier: 1.11.1
|
||||||
version: 1.11.1
|
version: 1.11.1
|
||||||
oauth2orize-pkce:
|
oauth2orize-pkce:
|
||||||
specifier: ^0.1.2
|
specifier: 0.1.2
|
||||||
version: 0.1.2
|
version: 0.1.2
|
||||||
os-utils:
|
os-utils:
|
||||||
specifier: 0.0.14
|
specifier: 0.0.14
|
||||||
|
@ -294,7 +297,7 @@ importers:
|
||||||
specifier: 8.11.0
|
specifier: 8.11.0
|
||||||
version: 8.11.0
|
version: 8.11.0
|
||||||
pkce-challenge:
|
pkce-challenge:
|
||||||
specifier: ^4.0.1
|
specifier: 4.0.1
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
probe-image-size:
|
probe-image-size:
|
||||||
specifier: 7.2.3
|
specifier: 7.2.3
|
||||||
|
@ -509,7 +512,7 @@ importers:
|
||||||
specifier: 2.4.2
|
specifier: 2.4.2
|
||||||
version: 2.4.2
|
version: 2.4.2
|
||||||
'@types/body-parser':
|
'@types/body-parser':
|
||||||
specifier: ^1.19.2
|
specifier: 1.19.2
|
||||||
version: 1.19.2
|
version: 1.19.2
|
||||||
'@types/cbor':
|
'@types/cbor':
|
||||||
specifier: 6.0.0
|
specifier: 6.0.0
|
||||||
|
@ -527,7 +530,7 @@ importers:
|
||||||
specifier: 2.1.21
|
specifier: 2.1.21
|
||||||
version: 2.1.21
|
version: 2.1.21
|
||||||
'@types/http-link-header':
|
'@types/http-link-header':
|
||||||
specifier: ^1.0.3
|
specifier: 1.0.3
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
'@types/jest':
|
'@types/jest':
|
||||||
specifier: 29.5.2
|
specifier: 29.5.2
|
||||||
|
@ -548,7 +551,7 @@ importers:
|
||||||
specifier: 2.1.1
|
specifier: 2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
'@types/ms':
|
'@types/ms':
|
||||||
specifier: ^0.7.31
|
specifier: 0.7.31
|
||||||
version: 0.7.31
|
version: 0.7.31
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 20.3.1
|
specifier: 20.3.1
|
||||||
|
@ -563,7 +566,7 @@ importers:
|
||||||
specifier: 0.9.1
|
specifier: 0.9.1
|
||||||
version: 0.9.1
|
version: 0.9.1
|
||||||
'@types/oauth2orize':
|
'@types/oauth2orize':
|
||||||
specifier: ^1.11.0
|
specifier: 1.11.0
|
||||||
version: 1.11.0
|
version: 1.11.0
|
||||||
'@types/pg':
|
'@types/pg':
|
||||||
specifier: 8.10.2
|
specifier: 8.10.2
|
||||||
|
@ -599,7 +602,7 @@ importers:
|
||||||
specifier: 0.32.0
|
specifier: 0.32.0
|
||||||
version: 0.32.0
|
version: 0.32.0
|
||||||
'@types/simple-oauth2':
|
'@types/simple-oauth2':
|
||||||
specifier: ^5.0.4
|
specifier: 5.0.4
|
||||||
version: 5.0.4
|
version: 5.0.4
|
||||||
'@types/sinonjs__fake-timers':
|
'@types/sinonjs__fake-timers':
|
||||||
specifier: 8.1.2
|
specifier: 8.1.2
|
||||||
|
@ -656,7 +659,7 @@ importers:
|
||||||
specifier: 29.5.0
|
specifier: 29.5.0
|
||||||
version: 29.5.0
|
version: 29.5.0
|
||||||
simple-oauth2:
|
simple-oauth2:
|
||||||
specifier: ^5.0.0
|
specifier: 5.0.0
|
||||||
version: 5.0.0
|
version: 5.0.0
|
||||||
|
|
||||||
packages/frontend:
|
packages/frontend:
|
||||||
|
@ -15528,6 +15531,13 @@ packages:
|
||||||
twemoji-parser: 14.0.0
|
twemoji-parser: 14.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/microformats-parser@1.4.1:
|
||||||
|
resolution: {integrity: sha512-BSg9Y/Aik8hvvme/fkxnXMRvTKuVwOeTapeZdaPQ+92DEubyM31iMtwbgFZ1383om643UvfYY5G23E9s1FY2KQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
parse5: 6.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/micromatch@3.1.10:
|
/micromatch@3.1.10:
|
||||||
resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
|
resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
Loading…
Reference in New Issue