Compare commits

...

23 commits

Author SHA1 Message Date
4censord ea3c0e0bea merge: Draft: Add /metrics endpoint (!613)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/613
2024-09-23 20:12:19 +00:00
Julia c224ed031f merge: Merge stable into develop (!636)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/636

Approved-by: dakkar <dakkar@thenautilus.net>
2024-09-23 20:10:52 +00:00
Julia Johannesen 8eb8d72889
Re-bump develop version 2024-09-23 15:58:14 -04:00
Julia 674fd13807 merge: Bump version (!635)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/635
2024-09-23 19:33:41 +00:00
Julia Johannesen 0672ed921e
Bump version 2024-09-23 15:03:34 -04:00
dakkar 69efba9366 merge: Fetch sponsors from OC (!624)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/624

Closes #653

Approved-by: Julia <julia@insertdomain.name>
Approved-by: Tess K <me@thvxl.se>
2024-09-23 17:57:39 +00:00
Julia c94f6994dd merge: Only accept HTML <link rel="alternate"> on successful HTTP statuses (!633)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/633

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Tess K <me@thvxl.se>
Approved-by: fEmber <acomputerdog@gmail.com>
Approved-by: Marie <github@yuugi.dev>
2024-09-23 17:51:26 +00:00
Julia 30c1c7c24d merge: simpler RateLimitService, might help with the leaks (!627)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/627

Approved-by: Julia <julia@insertdomain.name>
Approved-by: Marie <github@yuugi.dev>
Approved-by: Tess K <me@thvxl.se>
Approved-by: fEmber <acomputerdog@gmail.com>
2024-09-23 16:51:39 +00:00
Julia 7c157408af merge: Add DetachedWindowAPI.close calls to MfmService (!634)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/634

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
2024-09-23 16:39:13 +00:00
Julia Johannesen ceaec33249
Add DetachedWindowAPI.close calls to MfmService 2024-09-22 19:41:12 -04:00
Julia Johannesen e4cbd58821
Remove superfluous DetachedWindowAPI.close call 2024-09-22 18:51:29 -04:00
Julia Johannesen 5b282924ea
Add DetachedWindowAPI.close calls 2024-09-22 18:36:46 -04:00
Julia Johannesen b667a68bd4
Use res.ok instead of 200-299 2024-09-22 18:35:29 -04:00
Julia Johannesen 6ea48be84a
Only accept HTML <link rel="alternate"> on success 2024-09-22 17:13:24 -04:00
dakkar 8a982c61c0 move rate-limit-exceeded error reporting, earlier
a rate-limit-exceeded error has `kind:'client'`, so the branch that
adds the `Retry-After` would never get taken
2024-09-20 09:16:44 +01:00
dakkar 3f6beb97d2 copy RateLimiterService from MisskeyIO
This implementation allocates fewer Promises, might help with the
memory leaks
2024-09-20 08:35:45 +01:00
dakkar 7439230401 bump happy-dom
just because MisskeyIO uses this version
2024-09-20 08:30:24 +01:00
dakkar e9e51fdc01 bump glob
latest version no longer uses `inflight`; other dependencies still use
an older `glob`, though…
2024-09-20 08:29:36 +01:00
Marie bc02b4ae1f merge: fix: use i18n in settings/general.vue (!626)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/626

Closes #541

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
2024-09-19 15:59:59 +00:00
Esurio cc51f7038e fix: use i18n in settings 2024-09-19 12:37:56 +00:00
Marie 62a81bed9b
upd: change sorting of supporters 2024-09-16 19:02:06 +02:00
Marie 2e18359dad chore: lint 2024-09-15 18:17:35 +00:00
Marie b7815df134
upd: fetch sponsors from OC 2024-09-15 20:04:29 +02:00
12 changed files with 88 additions and 102 deletions

View file

@ -776,6 +776,7 @@ noCrawleDescription: "Ask search engines to not index your profile page, notes,
lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes will be visible to anyone, even if you require followers to be manually approved." lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes will be visible to anyone, even if you require followers to be manually approved."
alwaysMarkSensitive: "Mark as sensitive by default" alwaysMarkSensitive: "Mark as sensitive by default"
loadRawImages: "Load original images instead of showing thumbnails" loadRawImages: "Load original images instead of showing thumbnails"
showTickerOnReplies: "Show instance ticker on replies"
searchEngine: "Search Engine For Search MFM" searchEngine: "Search Engine For Search MFM"
searchEngineOther: "Other" searchEngineOther: "Other"
searchEngineCustomURIDescription: "The custom URI must be input in the format like \"https://www.google.com/search?q=\\{query}\" or \"https://www.google.com/search?q=%s\"." searchEngineCustomURIDescription: "The custom URI must be input in the format like \"https://www.google.com/search?q=\\{query}\" or \"https://www.google.com/search?q=%s\"."
@ -1089,6 +1090,8 @@ collapseRenotes: "Collapse boosts you've already seen"
collapseRenotesDescription: "Collapse boosts that you have boosted or reacted to" collapseRenotesDescription: "Collapse boosts that you have boosted or reacted to"
collapseNotesRepliedTo: "Collapse notes replied to" collapseNotesRepliedTo: "Collapse notes replied to"
collapseFiles: "Collapse files" collapseFiles: "Collapse files"
uncollapseCW: "Uncollapse CWs on notes"
expandLongNote: "Always expand long notes"
autoloadConversation: "Load conversation on replies" autoloadConversation: "Load conversation on replies"
internalServerError: "Internal Server Error" internalServerError: "Internal Server Error"
internalServerErrorDescription: "The server has run into an unexpected error." internalServerErrorDescription: "The server has run into an unexpected error."

12
locales/index.d.ts vendored
View file

@ -3120,6 +3120,10 @@ export interface Locale extends ILocale {
* *
*/ */
"loadRawImages": string; "loadRawImages": string;
/**
*
*/
"showTickerOnReplies": string;
/** /**
* MFMの検索エンジン * MFMの検索エンジン
*/ */
@ -4373,6 +4377,14 @@ export interface Locale extends ILocale {
* *
*/ */
"collapseFiles": string; "collapseFiles": string;
/**
* CWを展開する
*/
"uncollapseCW": string;
/**
* 稿
*/
"expandLongNote": string;
/** /**
* *
*/ */

View file

@ -776,6 +776,7 @@ noCrawleDescription: "外部の検索エンジンにあなたのユーザーペ
lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。" lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。"
alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする" alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする"
loadRawImages: "添付画像のサムネイルをオリジナル画質にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
showTickerOnReplies: "返信にサーバー情報を表示する"
searchEngine: "検索MFMの検索エンジン" searchEngine: "検索MFMの検索エンジン"
searchEngineOther: "カスタム" searchEngineOther: "カスタム"
searchEngineCustomURIDescription: "カスタム検索エンジンのURIは、\"https://www.google.com/search?q=\\{query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。" searchEngineCustomURIDescription: "カスタム検索エンジンのURIは、\"https://www.google.com/search?q=\\{query}\" や \"https://www.google.com/search?q=%s\" のような形式で入力する必要があります。"
@ -1089,6 +1090,8 @@ collapseRenotes: "ブーストのスマート省略"
collapseRenotesDescription: "リアクションやブーストをしたことがあるノートをたたんで表示します。" collapseRenotesDescription: "リアクションやブーストをしたことがあるノートをたたんで表示します。"
collapseNotesRepliedTo: "返信元のノートを折りたたむ" collapseNotesRepliedTo: "返信元のノートを折りたたむ"
collapseFiles: "ファイルを折りたたむ" collapseFiles: "ファイルを折りたたむ"
uncollapseCW: "CWを展開する"
expandLongNote: "長い投稿を常に展開する"
autoloadConversation: "会話スレッドを自動で読み込む" autoloadConversation: "会話スレッドを自動で読み込む"
internalServerError: "サーバー内部エラー" internalServerError: "サーバー内部エラー"
internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。" internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。"

View file

@ -120,9 +120,9 @@
"file-type": "19.3.0", "file-type": "19.3.0",
"fluent-ffmpeg": "2.1.3", "fluent-ffmpeg": "2.1.3",
"form-data": "4.0.0", "form-data": "4.0.0",
"glob": "10.3.10", "glob": "11.0.0",
"got": "14.4.2", "got": "14.4.2",
"happy-dom": "15.6.1", "happy-dom": "15.7.4",
"hpagent": "1.2.0", "hpagent": "1.2.0",
"htmlescape": "1.1.1", "htmlescape": "1.1.1",
"http-link-header": "1.1.3", "http-link-header": "1.1.3",

View file

@ -245,7 +245,7 @@ export class MfmService {
return null; return null;
} }
const { window } = new Window(); const { happyDOM, window } = new Window();
const doc = window.document; const doc = window.document;
@ -463,7 +463,11 @@ export class MfmService {
appendChildren(nodes, body); appendChildren(nodes, body);
return new XMLSerializer().serializeToString(body); const serialized = new XMLSerializer().serializeToString(body);
happyDOM.close().catch(e => {});
return serialized;
} }
// the toMastoApiHtml function was taken from Iceshrimp and written by zotan and modified by marie to work with the current MK version // the toMastoApiHtml function was taken from Iceshrimp and written by zotan and modified by marie to work with the current MK version
@ -474,7 +478,7 @@ export class MfmService {
return null; return null;
} }
const { window } = new Window(); const { happyDOM, window } = new Window();
const doc = window.document; const doc = window.document;
@ -681,6 +685,8 @@ export class MfmService {
result = result.replace(/^<p>/, '').replace(/<\/p>$/, ''); result = result.replace(/^<p>/, '').replace(/<\/p>$/, '');
} }
happyDOM.close().catch(e => {});
return result; return result;
} }
} }

View file

@ -207,7 +207,11 @@ export class ApRequestService {
//#region リクエスト先がhtmlかつactivity+jsonへのalternate linkタグがあるとき //#region リクエスト先がhtmlかつactivity+jsonへのalternate linkタグがあるとき
const contentType = res.headers.get('content-type'); const contentType = res.headers.get('content-type');
if ((contentType ?? '').split(';')[0].trimEnd().toLowerCase() === 'text/html' && _followAlternate === true) { if (
res.ok
&& (contentType ?? '').split(';')[0].trimEnd().toLowerCase() === 'text/html'
&& _followAlternate === true
) {
const html = await res.text(); const html = await res.text();
const window = new Window({ const window = new Window({
settings: { settings: {
@ -242,6 +246,8 @@ export class ApRequestService {
} }
} catch (e) { } catch (e) {
// something went wrong parsing the HTML, ignore the whole thing // something went wrong parsing the HTML, ignore the whole thing
} finally {
await window.happyDOM.close();
} }
} }
//#endregion //#endregion

View file

@ -64,15 +64,6 @@ export class ApiCallService implements OnApplicationShutdown {
let statusCode = err.httpStatusCode; let statusCode = err.httpStatusCode;
if (err.httpStatusCode === 401) { if (err.httpStatusCode === 401) {
reply.header('WWW-Authenticate', 'Bearer realm="Misskey"'); reply.header('WWW-Authenticate', 'Bearer realm="Misskey"');
} else if (err.kind === 'client') {
reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="invalid_request", error_description="${err.message}"`);
statusCode = statusCode ?? 400;
} else if (err.kind === 'permission') {
// (ROLE_PERMISSION_DENIEDは関係ない)
if (err.code === 'PERMISSION_DENIED') {
reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="insufficient_scope", error_description="${err.message}"`);
}
statusCode = statusCode ?? 403;
} else if (err.code === 'RATE_LIMIT_EXCEEDED') { } else if (err.code === 'RATE_LIMIT_EXCEEDED') {
const info: unknown = err.info; const info: unknown = err.info;
const unixEpochInSeconds = Date.now(); const unixEpochInSeconds = Date.now();
@ -83,6 +74,15 @@ export class ApiCallService implements OnApplicationShutdown {
} else { } else {
this.logger.warn(`rate limit information has unexpected type ${typeof(err.info?.reset)}`); this.logger.warn(`rate limit information has unexpected type ${typeof(err.info?.reset)}`);
} }
} else if (err.kind === 'client') {
reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="invalid_request", error_description="${err.message}"`);
statusCode = statusCode ?? 400;
} else if (err.kind === 'permission') {
// (ROLE_PERMISSION_DENIEDは関係ない)
if (err.code === 'PERMISSION_DENIED') {
reply.header('WWW-Authenticate', `Bearer realm="Misskey", error="insufficient_scope", error_description="${err.message}"`);
}
statusCode = statusCode ?? 403;
} else if (!statusCode) { } else if (!statusCode) {
statusCode = 500; statusCode = 500;
} }

View file

@ -32,18 +32,11 @@ export class RateLimiterService {
@bindThis @bindThis
public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string, factor = 1) { public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string, factor = 1) {
{ return new Promise<void>((ok, reject) => {
if (this.disabled) { if (this.disabled) ok();
return Promise.resolve();
}
// those lines with the "wrong" brace style / indentation are
// done that way so that the *other* lines stay identical to
// Misskey, simplifying merges
// Short-term limit // Short-term limit
// eslint-disable-next-line brace-style const minP = (): void => {
const minP = () => { return new Promise<void>((ok, reject) => {
const minIntervalLimiter = new Limiter({ const minIntervalLimiter = new Limiter({
id: `${actor}:${limitation.key}:min`, id: `${actor}:${limitation.key}:min`,
duration: limitation.minInterval! * factor, duration: limitation.minInterval! * factor,
@ -62,18 +55,16 @@ export class RateLimiterService {
return reject({ code: 'BRIEF_REQUEST_INTERVAL', info }); return reject({ code: 'BRIEF_REQUEST_INTERVAL', info });
} else { } else {
if (hasLongTermLimit) { if (hasLongTermLimit) {
return maxP().then(ok, reject); return maxP();
} else { } else {
return ok(); return ok();
} }
} }
}); });
// eslint-disable-next-line brace-style };
}); };
// Long term limit // Long term limit
// eslint-disable-next-line brace-style const maxP = (): void => {
const maxP = () => { return new Promise<void>((ok, reject) => {
const limiter = new Limiter({ const limiter = new Limiter({
id: `${actor}:${limitation.key}`, id: `${actor}:${limitation.key}`,
duration: limitation.duration! * factor, duration: limitation.duration! * factor,
@ -94,8 +85,7 @@ export class RateLimiterService {
return ok(); return ok();
} }
}); });
// eslint-disable-next-line brace-style };
}); };
const hasShortTermLimit = typeof limitation.minInterval === 'number'; const hasShortTermLimit = typeof limitation.minInterval === 'number';
@ -104,12 +94,12 @@ export class RateLimiterService {
typeof limitation.max === 'number'; typeof limitation.max === 'number';
if (hasShortTermLimit) { if (hasShortTermLimit) {
return minP(); minP();
} else if (hasLongTermLimit) { } else if (hasLongTermLimit) {
return maxP(); maxP();
} else { } else {
return Promise.resolve(); ok();
} }
} });
} }
} }

View file

@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js';
export const meta = { export const meta = {
tags: ['meta'], tags: ['meta'],
description: 'Get Sharkey GH Sponsors', description: 'Get Sharkey Sponsors',
requireCredential: false, requireCredential: false,
requireCredentialPrivateMode: false, requireCredentialPrivateMode: false,
@ -30,29 +30,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.redis) private redisClient: Redis.Redis, @Inject(DI.redis) private redisClient: Redis.Redis,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
let sponsors; let totalSponsors;
const cachedSponsors = await this.redisClient.get('sponsors'); const cachedSponsors = await this.redisClient.get('sponsors');
if (!ps.forceUpdate && cachedSponsors) { if (!ps.forceUpdate && cachedSponsors) {
sponsors = JSON.parse(cachedSponsors); totalSponsors = JSON.parse(cachedSponsors);
} else { } else {
AbortSignal.timeout ??= function timeout(ms) {
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), ms);
return ctrl.signal;
};
try { try {
sponsors = await fetch('https://kaifa.ch/transfem-sponsors.json', { signal: AbortSignal.timeout(2000) }) const backers = await fetch('https://opencollective.com/sharkey/tiers/backer/all.json').then((response) => response.json());
.then((response) => response.json()); const sponsorsOC = await fetch('https://opencollective.com/sharkey/tiers/sponsor/all.json').then((response) => response.json());
await this.redisClient.set('sponsors', JSON.stringify(sponsors), 'EX', 3600); // Merge both together into one array and make sure it only has Active subscriptions
const allSponsors = [...sponsorsOC, ...backers].filter(sponsor => sponsor.isActive === true);
// Remove possible duplicates
totalSponsors = [...new Map(allSponsors.map(v => [v.profile, v])).values()];
await this.redisClient.set('sponsors', JSON.stringify(totalSponsors), 'EX', 3600);
} catch (error) { } catch (error) {
sponsors = { totalSponsors = [];
sponsors: [],
};
} }
} }
return { sponsor_data: sponsors['sponsors'] }; return { sponsor_data: totalSponsors };
}); });
} }
} }

View file

@ -170,9 +170,9 @@ SPDX-License-Identifier: AGPL-3.0-only
:key="sponsor" :key="sponsor"
style="margin-bottom: 0.5rem;" style="margin-bottom: 0.5rem;"
> >
<a :href="sponsor.profile" target="_blank" :class="$style.contributor"> <a :href="sponsor.website || sponsor.profile" target="_blank" :class="$style.contributor">
<img :src="sponsor.avatar" :class="$style.contributorAvatar"> <img :src="sponsor.image || `https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=${sponsor.name}`" :class="$style.contributorAvatar">
<span :class="$style.contributorUsername">{{ sponsor.details.name }}</span> <span :class="$style.contributorUsername">{{ sponsor.name }}</span>
</a> </a>
</span> </span>
</div> </div>
@ -209,7 +209,7 @@ const easterEggEngine = ref(null);
const sponsors = ref([]); const sponsors = ref([]);
const containerEl = shallowRef<HTMLElement>(); const containerEl = shallowRef<HTMLElement>();
await misskeyApi('sponsors', { forceUpdate: true }).then((res) => sponsors.value.push(res.sponsor_data)); await misskeyApi('sponsors', { forceUpdate: false }).then((res) => sponsors.value.push(res.sponsor_data));
function iconLoaded() { function iconLoaded() {
const emojis = defaultStore.state.reactions; const emojis = defaultStore.state.reactions;

View file

@ -56,8 +56,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
<MkSwitch v-model="collapseNotesRepliedTo">{{ i18n.ts.collapseNotesRepliedTo }}</MkSwitch> <MkSwitch v-model="collapseNotesRepliedTo">{{ i18n.ts.collapseNotesRepliedTo }}</MkSwitch>
<MkSwitch v-model="collapseFiles">{{ i18n.ts.collapseFiles }}</MkSwitch> <MkSwitch v-model="collapseFiles">{{ i18n.ts.collapseFiles }}</MkSwitch>
<MkSwitch v-model="uncollapseCW">Uncollapse CWs on notes</MkSwitch> <MkSwitch v-model="uncollapseCW">{{ i18n.ts.uncollapseCW }}</MkSwitch>
<MkSwitch v-model="expandLongNote">Always expand long notes</MkSwitch> <MkSwitch v-model="expandLongNote">{{ i18n.ts.expandLongNote }}</MkSwitch>
<MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
<MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
<MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch> <MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch>
@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="showReactionsCount">{{ i18n.ts.showReactionsCount }}</MkSwitch> <MkSwitch v-model="showReactionsCount">{{ i18n.ts.showReactionsCount }}</MkSwitch>
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch> <MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
<MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch> <MkSwitch v-model="showTickerOnReplies">{{ i18n.ts.showTickerOnReplies }}</MkSwitch>
<MkSelect v-model="searchEngine" placeholder="Other"> <MkSelect v-model="searchEngine" placeholder="Other">
<template #label>{{ i18n.ts.searchEngine }}</template> <template #label>{{ i18n.ts.searchEngine }}</template>
<option <option

View file

@ -251,14 +251,14 @@ importers:
specifier: 4.0.0 specifier: 4.0.0
version: 4.0.0 version: 4.0.0
glob: glob:
specifier: 10.3.10 specifier: 11.0.0
version: 10.3.10 version: 11.0.0
got: got:
specifier: 14.4.2 specifier: 14.4.2
version: 14.4.2 version: 14.4.2
happy-dom: happy-dom:
specifier: 15.6.1 specifier: 15.7.4
version: 15.6.1 version: 15.7.4
hpagent: hpagent:
specifier: 1.2.0 specifier: 1.2.0
version: 1.2.0 version: 1.2.0
@ -7586,11 +7586,6 @@ packages:
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
hasBin: true hasBin: true
glob@10.4.2:
resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==}
engines: {node: '>=16 || 14 >=14.18'}
hasBin: true
glob@11.0.0: glob@11.0.0:
resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@ -7677,8 +7672,8 @@ packages:
happy-dom@10.0.3: happy-dom@10.0.3:
resolution: {integrity: sha512-WkCP+Z5fX6U5PY+yHP3ElV5D9PoxRAHRWPFq3pG9rg/6Hjf5ak7dozAgSCywsTRUq2qfa8vV8OQvUy5pRXy8EQ==} resolution: {integrity: sha512-WkCP+Z5fX6U5PY+yHP3ElV5D9PoxRAHRWPFq3pG9rg/6Hjf5ak7dozAgSCywsTRUq2qfa8vV8OQvUy5pRXy8EQ==}
happy-dom@15.6.1: happy-dom@15.7.4:
resolution: {integrity: sha512-dsMHLsJHZYhXeExP47B2siAfKNVxptlwFss3/bq/9sG3iBt0P2WYFBq68JgMR5vB5gsN2Ev0feTTPD/+rosUNQ==} resolution: {integrity: sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hard-rejection@2.1.0: hard-rejection@2.1.0:
@ -7908,6 +7903,7 @@ packages:
inflight@1.0.6: inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
inherits@2.0.4: inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@ -8226,10 +8222,6 @@ packages:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'} engines: {node: '>=14'}
jackspeak@3.4.0:
resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==}
engines: {node: '>=14'}
jackspeak@4.0.1: jackspeak@4.0.1:
resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@ -9544,10 +9536,6 @@ packages:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
path-scurry@2.0.0: path-scurry@2.0.0:
resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@ -11134,7 +11122,6 @@ packages:
ts-case-convert@2.0.2: ts-case-convert@2.0.2:
resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==} resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
bundledDependencies: []
ts-dedent@2.2.0: ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@ -14095,7 +14082,7 @@ snapshots:
content-disposition: 0.5.4 content-disposition: 0.5.4
fastify-plugin: 4.5.0 fastify-plugin: 4.5.0
fastq: 1.17.1 fastq: 1.17.1
glob: 10.4.2 glob: 10.3.10
'@fastify/view@8.2.0': '@fastify/view@8.2.0':
dependencies: dependencies:
@ -20049,15 +20036,6 @@ snapshots:
minipass: 7.0.4 minipass: 7.0.4
path-scurry: 1.10.1 path-scurry: 1.10.1
glob@10.4.2:
dependencies:
foreground-child: 3.1.1
jackspeak: 3.4.0
minimatch: 9.0.4
minipass: 7.1.2
package-json-from-dist: 1.0.0
path-scurry: 1.11.1
glob@11.0.0: glob@11.0.0:
dependencies: dependencies:
foreground-child: 3.1.1 foreground-child: 3.1.1
@ -20194,7 +20172,7 @@ snapshots:
whatwg-encoding: 2.0.0 whatwg-encoding: 2.0.0
whatwg-mimetype: 3.0.0 whatwg-mimetype: 3.0.0
happy-dom@15.6.1: happy-dom@15.7.4:
dependencies: dependencies:
entities: 4.5.0 entities: 4.5.0
webidl-conversions: 7.0.0 webidl-conversions: 7.0.0
@ -20709,12 +20687,6 @@ snapshots:
optionalDependencies: optionalDependencies:
'@pkgjs/parseargs': 0.11.0 '@pkgjs/parseargs': 0.11.0
jackspeak@3.4.0:
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
jackspeak@4.0.1: jackspeak@4.0.1:
dependencies: dependencies:
'@isaacs/cliui': 8.0.2 '@isaacs/cliui': 8.0.2
@ -22449,11 +22421,6 @@ snapshots:
lru-cache: 10.2.2 lru-cache: 10.2.2
minipass: 7.0.4 minipass: 7.0.4
path-scurry@1.11.1:
dependencies:
lru-cache: 10.2.2
minipass: 7.1.2
path-scurry@2.0.0: path-scurry@2.0.0:
dependencies: dependencies:
lru-cache: 11.0.0 lru-cache: 11.0.0