From a297fd030997982b55ca1c4f75146a180cb4e96a Mon Sep 17 00:00:00 2001 From: stratumadev Date: Wed, 30 Jul 2025 21:58:01 +0200 Subject: [PATCH] added crunchyroll cbr video + 192 kbps audio download --- @types/crunchyTypes.d.ts | 6 +- @types/playbackData.d.ts | 3 +- crunchy.ts | 527 +++++++++++++++------------ gui/server/services/crunchyroll.ts | 2 - modules/module.api-urls.ts | 25 +- modules/module.app-args.ts | 8 +- modules/module.args.ts | 88 +++-- modules/module.fetch.ts | 2 +- package.json | 22 +- pnpm-lock.yaml | 561 ++++++++++++++--------------- 10 files changed, 656 insertions(+), 588 deletions(-) diff --git a/@types/crunchyTypes.d.ts b/@types/crunchyTypes.d.ts index 0e76273..1c0ac35 100644 --- a/@types/crunchyTypes.d.ts +++ b/@types/crunchyTypes.d.ts @@ -6,8 +6,10 @@ import { CrunchyPlayStreams } from './enums'; export type CrunchyDownloadOptions = { hslang: string, - kstream: number, - cstream: keyof typeof CrunchyPlayStreams | 'none', + // kstream: number, + cstream: keyof typeof CrunchyPlayStreams, + vstream: keyof typeof CrunchyPlayStreams, + astream: keyof typeof CrunchyPlayStreams, novids?: boolean, noaudio?: boolean, x: number, diff --git a/@types/playbackData.d.ts b/@types/playbackData.d.ts index 8b4da69..015f41a 100644 --- a/@types/playbackData.d.ts +++ b/@types/playbackData.d.ts @@ -1,7 +1,8 @@ // Generated by https://quicktype.io export interface PlaybackData { total: number; - data: { [key: string]: { [key: string]: StreamDetails } }[]; + vpb: { [key: string]: { [key: string]: StreamDetails } }; + apb: { [key: string]: { [key: string]: StreamDetails } }; meta: Meta; } diff --git a/crunchy.ts b/crunchy.ts index bd7c167..38547bf 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -56,7 +56,6 @@ export type sxItem = { export default class Crunchy implements ServiceClass { public cfg: yamlCfg.ConfigObject; - public api: 'android' | 'web'; public locale: string; private token: Record; private req: reqModule.Req; @@ -70,7 +69,6 @@ export default class Crunchy implements ServiceClass { this.cfg = yamlCfg.loadCfg(); this.token = yamlCfg.loadCRToken(); this.req = new reqModule.Req(domain, debug, false, 'cr'); - this.api = 'android'; this.locale = 'en-US'; } @@ -81,7 +79,6 @@ export default class Crunchy implements ServiceClass { public async cli() { console.info(`\n=== Multi Downloader NX ${packageJson.version} ===\n`); const argv = yargs.appArgv(this.cfg.cli); - this.api = argv.crapi; this.locale = argv.locale; if (argv.debug) this.debug = true; @@ -249,44 +246,32 @@ export default class Crunchy implements ServiceClass { // seasons list let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - if (this.api == 'android') { - const reqEpsListOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/episodes?', - new URLSearchParams({ - 'force_locale': '', - 'preferred_audio_language': 'ja-JP', - 'locale': this.locale, - 'season_id': id, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; - } - //CrunchyEpisodeList - const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; - episodeList = { - total: episodeListAndroid.total, - data: episodeListAndroid.items, - meta: {} - }; - } else { - const reqEpsList = await this.req.getData(`${api.cms}/seasons/${id}/episodes?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; - } - //CrunchyEpisodeList - episodeList = await reqEpsList.res.json() as CrunchyEpisodeList; - - + const reqEpsListOpts = [ + api.cms_bucket, + this.cmsToken.cms.bucket, + '/episodes?', + new URLSearchParams({ + 'force_locale': '', + 'preferred_audio_language': 'ja-JP', + 'locale': this.locale, + 'season_id': id, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; } + //CrunchyEpisodeList + const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; + episodeList = { + total: episodeListAndroid.total, + data: episodeListAndroid.items, + meta: {} + }; for (const item of episodeList.data) { // stringify each object, then a newline console.log(JSON.stringify(item)); @@ -514,7 +499,7 @@ export default class Crunchy implements ServiceClass { }, useProxy: true }; - const profileReq = await this.req.getData(api.beta_profile, profileReqOptions); + const profileReq = await this.req.getData(api.profile, profileReqOptions); if(!profileReq.ok || !profileReq.res){ console.error('Get profile failed!'); return false; @@ -652,7 +637,7 @@ export default class Crunchy implements ServiceClass { }, useProxy: true }; - const cmsTokenReq = await this.req.getData(api.beta_cmsToken, cmsTokenReqOpts); + const cmsTokenReq = await this.req.getData(api.cmsToken, cmsTokenReqOpts); if(!cmsTokenReq.ok || !cmsTokenReq.res){ console.error('Authentication CMS token failed!'); return; @@ -669,7 +654,7 @@ export default class Crunchy implements ServiceClass { } // opts const indexReqOpts = [ - api.beta_cms, + api.cms_bucket, this.cmsToken.cms.bucket, '/index?', new URLSearchParams({ @@ -1060,7 +1045,7 @@ export default class Crunchy implements ServiceClass { n: '25', start: (page ? (page-1)*25 : 0).toString(), }).toString(); - const newlyAddedReq = await this.req.getData(`${api.beta_browse}?${newlyAddedParams}`, newlyAddedReqOpts); + const newlyAddedReq = await this.req.getData(`${api.browse}?${newlyAddedParams}`, newlyAddedReqOpts); if(!newlyAddedReq.ok || !newlyAddedReq.res){ console.error('Get newly added FAILED!'); return; @@ -1103,42 +1088,32 @@ export default class Crunchy implements ServiceClass { let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - if (this.api == 'android') { - const reqEpsListOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/episodes?', - new URLSearchParams({ - 'force_locale': '', - 'preferred_audio_language': 'ja-JP', - 'locale': this.locale, - 'season_id': id, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; - } - //CrunchyEpisodeList - const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; - episodeList = { - total: episodeListAndroid.total, - data: episodeListAndroid.items, - meta: {} - }; - } else { - const reqEpsList = await this.req.getData(`${api.cms}/seasons/${id}/episodes?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; - } - //CrunchyEpisodeList - episodeList = await reqEpsList.res.json() as CrunchyEpisodeList; + const reqEpsListOpts = [ + api.cms_bucket, + this.cmsToken.cms.bucket, + '/episodes?', + new URLSearchParams({ + 'force_locale': '', + 'preferred_audio_language': 'ja-JP', + 'locale': this.locale, + 'season_id': id, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return { isOk: false, reason: new Error('Episode List request failed. No more information provided.') }; } + //CrunchyEpisodeList + const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; + episodeList = { + total: episodeListAndroid.total, + data: episodeListAndroid.items, + meta: {} + }; const epNumList: { ep: number[], @@ -1274,7 +1249,7 @@ export default class Crunchy implements ServiceClass { const objectIds = []; for (const ob of epFilter.values) { const extIdReqOpts = [ - api.beta_cms, + api.cms_bucket, this.cmsToken.cms.bucket, '/channels/crunchyroll/objects', '?', @@ -1330,53 +1305,38 @@ export default class Crunchy implements ServiceClass { // reqs let objectInfo: ObjectInfo = { total: 0, data: [], meta: {} }; - if (this.api == 'android') { - const objectReqOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/objects/', - doEpsFilter.values.join(','), - '?', - new URLSearchParams({ - 'force_locale': '', - 'preferred_audio_language': 'ja-JP', - 'locale': this.locale, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - const objectReq = await this.req.getData(objectReqOpts, AuthHeaders); - if(!objectReq.ok || !objectReq.res){ - console.error('Objects Request FAILED!'); - if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ - const objectInfo = await objectReq.error.res.json(); - console.info('Body:', JSON.stringify(objectInfo, null, '\t')); - objectInfo.error = true; - return objectInfo; - } - return []; + const objectReqOpts = [ + api.cms_bucket, + this.cmsToken.cms.bucket, + '/objects/', + doEpsFilter.values.join(','), + '?', + new URLSearchParams({ + 'force_locale': '', + 'preferred_audio_language': 'ja-JP', + 'locale': this.locale, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const objectReq = await this.req.getData(objectReqOpts, AuthHeaders); + if(!objectReq.ok || !objectReq.res){ + console.error('Objects Request FAILED!'); + if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ + const objectInfo = await objectReq.error.res.json(); + console.info('Body:', JSON.stringify(objectInfo, null, '\t')); + objectInfo.error = true; + return objectInfo; } - const objectInfoAndroid = await objectReq.res.json() as CrunchyAndroidObject; - objectInfo = { - total: objectInfoAndroid.total, - data: objectInfoAndroid.items, - meta: {} - }; - } else { - const objectReq = await this.req.getData(`${api.cms}/objects/${doEpsFilter.values.join(',')}?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders); - if(!objectReq.ok || !objectReq.res){ - console.error('Objects Request FAILED!'); - if(objectReq.error && objectReq.error.res && objectReq.error.res.body){ - const objectInfo = await objectReq.error.res.json(); - console.info('Body:', JSON.stringify(objectInfo, null, '\t')); - objectInfo.error = true; - return objectInfo; - } - return []; - } - objectInfo = await objectReq.res.json() as ObjectInfo; + return []; } + const objectInfoAndroid = await objectReq.res.json() as CrunchyAndroidObject; + objectInfo = { + total: objectInfoAndroid.total, + data: objectInfoAndroid.items, + meta: {} + }; if(earlyReturn){ return objectInfo; @@ -1655,40 +1615,75 @@ export default class Crunchy implements ServiceClass { } } - const pbData = { total: 0, data: [{}], meta: {} } as PlaybackData; + const pbData = { total: 0, vpb: {}, apb: {}, meta: {} } as PlaybackData; - let playStream: CrunchyPlayStream | null = null; - if (options.cstream !== 'none') { - const playbackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v2/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyPlayStreams[options.cstream]}/play`, AuthHeaders); - if (!playbackReq.ok || !playbackReq.res) { - console.warn('Request Stream URLs FAILED!'); + let videoStream: CrunchyPlayStream | null = null; + let audioStream: CrunchyPlayStream | null = null; + + const videoPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyPlayStreams[options.vstream]}/play`, AuthHeaders); + if (!videoPlaybackReq.ok || !videoPlaybackReq.res) { + console.warn('Request Video Stream URLs FAILED!'); + } else { + videoStream = await videoPlaybackReq.res.json() as CrunchyPlayStream; + const derivedPlaystreams = {} as CrunchyStreams; + for (const hardsub in videoStream.hardSubs) { + const stream = videoStream.hardSubs[hardsub]; + derivedPlaystreams[hardsub] = { + url: stream.url, + 'hardsub_locale': stream.hlang + }; + } + derivedPlaystreams[''] = { + url: videoStream.url, + hardsub_locale: '' + }; + pbData.meta = { + audio_locale: videoStream.audioLocale, + bifs: [videoStream.bifs], + captions: videoStream.captions, + closed_captions: videoStream.captions, + media_id: videoStream.assetId, + subtitles: videoStream.subtitles, + versions: videoStream.versions + }; + pbData.vpb[`adaptive_${options.vstream}_${videoStream.url.includes('m3u8') ? 'hls' : 'dash'}_drm`] = { + ...derivedPlaystreams + }; + } + + if (!options.cstream && (options.vstream !== options.astream)) { + const audioPlaybackReq = await this.req.getData(`https://www.crunchyroll.com/playback/v3/${currentVersion ? currentVersion.guid : currentMediaId}/${CrunchyPlayStreams[options.astream]}/play`, AuthHeaders); + if (!audioPlaybackReq.ok || !audioPlaybackReq.res) { + console.warn('Request Audio Stream URLs FAILED!'); } else { - playStream = await playbackReq.res.json() as CrunchyPlayStream; + audioStream = await audioPlaybackReq.res.json() as CrunchyPlayStream; const derivedPlaystreams = {} as CrunchyStreams; - for (const hardsub in playStream.hardSubs) { - const stream = playStream.hardSubs[hardsub]; + for (const hardsub in audioStream.hardSubs) { + const stream = audioStream.hardSubs[hardsub]; derivedPlaystreams[hardsub] = { url: stream.url, 'hardsub_locale': stream.hlang }; } derivedPlaystreams[''] = { - url: playStream.url, + url: audioStream.url, hardsub_locale: '' }; pbData.meta = { - audio_locale: playStream.audioLocale, - bifs: [playStream.bifs], - captions: playStream.captions, - closed_captions: playStream.captions, - media_id: playStream.assetId, - subtitles: playStream.subtitles, - versions: playStream.versions + audio_locale: audioStream.audioLocale, + bifs: [audioStream.bifs], + captions: audioStream.captions, + closed_captions: audioStream.captions, + media_id: audioStream.assetId, + subtitles: audioStream.subtitles, + versions: audioStream.versions }; - pbData.data[0][`adaptive_${options.cstream}_${playStream.url.includes('m3u8') ? 'hls' : 'dash'}_drm`] = { + pbData.apb[`adaptive_${options.astream}_${audioStream.url.includes('m3u8') ? 'hls' : 'dash'}_drm`] = { ...derivedPlaystreams }; } + } else { + pbData.apb = pbData.vpb; } variables.push(...([ @@ -1707,9 +1702,11 @@ export default class Crunchy implements ServiceClass { } as Variable; })); - let streams: any[] = []; + let vstreams: any[] = []; + let astreams: any[] = []; let hsLangs: string[] = []; - const pbStreams = pbData.data[0]; + const vpbStreams = pbData.vpb; + const apbStreams = pbData.apb; if (!canDecrypt) { console.error('No valid Widevine or PlayReady CDM detected. Please ensure a supported and functional CDM is installed.'); @@ -1721,13 +1718,13 @@ export default class Crunchy implements ServiceClass { return undefined; } - for (const s of Object.keys(pbStreams)) { + for (const s of Object.keys(pbData.vpb)) { if ( (s.match(/hls/) || s.match(/dash/)) && !(s.match(/hls/) && s.match(/drm/)) && !s.match(/trailer/) ) { - const pb = Object.values(pbStreams[s]).map(v => { + const pb = Object.values(vpbStreams[s]).map(v => { v.hardsub_lang = v.hardsub_locale ? langsData.fixAndFindCrLC(v.hardsub_locale).locale : v.hardsub_locale; @@ -1739,26 +1736,67 @@ export default class Crunchy implements ServiceClass { ...{ format: s } }; }); - streams.push(...pb); + vstreams.push(...pb); } } - if (streams.length < 1) { - console.warn('No full streams found!'); + for (const s of Object.keys(pbData.apb)) { + if ( + (s.match(/hls/) || s.match(/dash/)) + && !(s.match(/hls/) && s.match(/drm/)) + && !s.match(/trailer/) + ) { + const pb = Object.values(apbStreams[s]).map(v => { + v.hardsub_lang = v.hardsub_locale + ? langsData.fixAndFindCrLC(v.hardsub_locale).locale + : v.hardsub_locale; + if(v.hardsub_lang && hsLangs.indexOf(v.hardsub_lang) < 0){ + hsLangs.push(v.hardsub_lang); + } + return { + ...v, + ...{ format: s } + }; + }); + astreams.push(...pb); + } + } + + if (vstreams.length < 1) { + console.warn('No full video streams found!'); + return undefined; + } + + if (astreams.length < 1) { + console.warn('No full audio streams found!'); return undefined; } const audDub = langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || '').code; hsLangs = langsData.sortTags(hsLangs); - streams = streams.map((s) => { + vstreams = vstreams.map((s) => { s.audio_lang = audDub; s.hardsub_lang = s.hardsub_lang ? s.hardsub_lang : '-'; s.type = `${s.format}/${s.audio_lang}/${s.hardsub_lang}`; return s; }); - streams = streams.sort((a, b) => { + vstreams = vstreams.sort((a, b) => { + if (a.type < b.type) { + return -1; + } + return 0; + }); + + astreams = astreams.map((s) => { + s.audio_lang = audDub; + s.hardsub_lang = s.hardsub_lang ? s.hardsub_lang : '-'; + s.type = `${s.format}/${s.audio_lang}/${s.hardsub_lang}`; + return s; + }); + + astreams = astreams.sort((a, b) => { if (a.type < b.type) { return -1; } @@ -1768,7 +1806,13 @@ export default class Crunchy implements ServiceClass { if(options.hslang != 'none'){ if(hsLangs.indexOf(options.hslang) > -1){ console.info('Selecting stream with %s hardsubs', langsData.locale2language(options.hslang).language); - streams = streams.filter((s) => { + vstreams = vstreams.filter((s) => { + if(s.hardsub_lang == '-'){ + return false; + } + return s.hardsub_lang == options.hslang; + }); + astreams = astreams.filter((s) => { if(s.hardsub_lang == '-'){ return false; } @@ -1783,11 +1827,21 @@ export default class Crunchy implements ServiceClass { dlFailed = true; } } else { - streams = streams.filter((s) => { + vstreams = vstreams.filter((s) => { return s.hardsub_lang == '-'; }); - if(streams.length < 1){ - console.warn('Raw streams not available!'); + astreams = astreams.filter((s) => { + return s.hardsub_lang == '-'; + }); + if(vstreams.length < 1){ + console.warn('Raw video streams not available!'); + if(hsLangs.length > 0){ + console.warn('Try hardsubs stream:', hsLangs.join(', ')); + } + dlFailed = true; + } + if(astreams.length < 1){ + console.warn('Raw audio streams not available!'); if(hsLangs.length > 0){ console.warn('Try hardsubs stream:', hsLangs.join(', ')); } @@ -1796,62 +1850,70 @@ export default class Crunchy implements ServiceClass { console.info('Selecting raw stream'); } - let curStream: - undefined|typeof streams[0] + let vcurStream: + undefined|typeof vstreams[0] = undefined; + let acurStream: + undefined|typeof astreams[0] + = undefined; + if (!dlFailed) { - options.kstream = typeof options.kstream == 'number' ? options.kstream : 1; - options.kstream = options.kstream > streams.length ? 1 : options.kstream; + console.info('Downloading...'); + vcurStream = vstreams[0]; + acurStream = astreams[0]; - streams.forEach((s, i) => { - const isSelected = options.kstream == i + 1 ? '✓' : ' '; - console.info('Full stream found! (%s%s: %s )', isSelected, i + 1, s.type); - }); - - console.info('Downloading video...'); - curStream = streams[options.kstream-1]; - - console.info('Playlists URL: %s (%s)', curStream.url, curStream.type); + console.info('Video Playlists URL: %s (%s)', vcurStream.url, vcurStream.type); + console.info('Audio Playlists URL: %s (%s)', acurStream.url, acurStream.type); } let tsFile = undefined; // Delete the stream if it's not needed if (options.novids && options.noaudio) { - if (playStream) { + if (videoStream) { await this.refreshToken(true, true); - await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + } + if (audioStream && (videoStream?.token !== audioStream.token)) { + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); } } - if(!dlFailed && curStream !== undefined && !(options.novids && options.noaudio)){ - const streamPlaylistsReq = await this.req.getData(curStream.url, AuthHeaders); - if(!streamPlaylistsReq.ok || !streamPlaylistsReq.res){ + if(!dlFailed && vcurStream && acurStream && vcurStream !== undefined && acurStream !== undefined && !(options.novids && options.noaudio)){ + const vstreamPlaylistsReq = await this.req.getData(vcurStream.url, AuthHeaders); + const astreamPlaylistsReq = vcurStream.url !== acurStream.url ? await this.req.getData(acurStream.url, AuthHeaders) : vstreamPlaylistsReq; + if(!vstreamPlaylistsReq.ok || !vstreamPlaylistsReq.res || !astreamPlaylistsReq.ok || !astreamPlaylistsReq.res){ console.error('CAN\'T FETCH VIDEO PLAYLISTS!'); dlFailed = true; } else { - const streamPlaylistBody = await streamPlaylistsReq.res.text(); - if (streamPlaylistBody.match('MPD')) { + const vstreamPlaylistBody = await vstreamPlaylistsReq.res.text(); + const astreamPlaylistBody = vcurStream.url !== acurStream.url ? await astreamPlaylistsReq.res.text() : vstreamPlaylistBody; + if (vstreamPlaylistBody.match('MPD') && astreamPlaylistBody.match('MPD')) { //Parse MPD Playlists - const streamPlaylists = await parse(streamPlaylistBody, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), curStream.url.match(/.*\.urlset\//)[0]); + const vstreamPlaylists = await parse(vstreamPlaylistBody, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), vcurStream.url.match(/.*\.urlset\//)[0]); + const astreamPlaylists = vcurStream.url !== acurStream.url ? await parse(astreamPlaylistBody, langsData.findLang(langsData.fixLanguageTag(pbData.meta.audio_locale as string) || ''), acurStream.url.match(/.*\.urlset\//)[0]) : vstreamPlaylists; //Get name of CDNs/Servers - const streamServers = Object.keys(streamPlaylists); + const vstreamServers = Object.keys(vstreamPlaylists); + const astreamServers = Object.keys(astreamPlaylists); - options.x = options.x > streamServers.length ? 1 : options.x; + options.x = options.x > vstreamServers.length ? 1 : options.x; - const selectedServer = streamServers[options.x - 1]; - const selectedList = streamPlaylists[selectedServer]; + const vselectedServer = vstreamServers[options.x - 1]; + const vselectedList = vstreamPlaylists[vselectedServer]; + + const aselectedServer = astreamServers[options.x - 1]; + const aselectedList = astreamPlaylists[aselectedServer]; //set Video Qualities - const videos = selectedList.video.map(item => { + const videos = vselectedList.video.map(item => { return { ...item, resolutionText: `${item.quality.width}x${item.quality.height} (${Math.round(item.bandwidth/1024)}KiB/s)` }; }); - const audios = selectedList.audio.map(item => { + const audios = aselectedList.audio.map(item => { return { ...item, resolutionText: `${Math.round(item.bandwidth/1000)}kB/s` @@ -1884,7 +1946,6 @@ export default class Crunchy implements ServiceClass { const chosenVideoSegments = videos[chosenVideoQuality]; const chosenAudioSegments = audios[chosenAudioQuality]; - console.info(`Servers available:\n\t${streamServers.join('\n\t')}`); console.info(`Available Video Qualities:\n\t${videos.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); console.info(`Available Audio Qualities:\n\t${audios.map((a, ind) => `[${ind+1}] ${a.resolutionText}`).join('\n\t')}`); @@ -1898,12 +1959,12 @@ export default class Crunchy implements ServiceClass { replaceWith: chosenVideoSegments.quality.width }); - const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); + const lang = langsData.languages.find(a => a.code === acurStream?.audio_lang); if (!lang) { - console.error(`Unable to find language for code ${curStream.audio_lang}`); + console.error(`Unable to find language for code ${acurStream.audio_lang}`); return; } - console.info(`Selected quality: \n\tVideo: ${chosenVideoSegments.resolutionText}\n\tAudio: ${chosenAudioSegments.resolutionText}\n\tServer: ${selectedServer}`); + console.info(`Selected quality: \n\tVideo: ${chosenVideoSegments.resolutionText}\n\tAudio: ${chosenAudioSegments.resolutionText}\n\tVideo Server: ${vselectedServer}\n\tAudio Server: ${aselectedServer}`); console.info('Stream URL:', chosenVideoSegments.segments[0].uri.split(',.urlset')[0]); // TODO check filename fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep); @@ -1950,7 +2011,7 @@ export default class Crunchy implements ServiceClass { 'Cache-Control': 'no-cache', 'content-type': 'application/octet-stream', 'x-cr-content-id': currentVersion ? currentVersion.guid : currentMediaId, - 'x-cr-video-token': playStream!.token + 'x-cr-video-token': videoStream!.token }); // Check if the audio pssh is different since Crunchyroll started to have different dec keys for audio tracks @@ -1963,16 +2024,19 @@ export default class Crunchy implements ServiceClass { 'Cache-Control': 'no-cache', 'content-type': 'application/octet-stream', 'x-cr-content-id': currentVersion ? currentVersion.guid : currentMediaId, - 'x-cr-video-token': playStream!.token + 'x-cr-video-token': audioStream!.token }); } else { encryptionKeysAudio = encryptionKeysVideo; } } - if (playStream) { + if (videoStream) { await this.refreshToken(true, true); - await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + } + if (audioStream && (videoStream?.token !== audioStream.token)) { + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); } // New Crunchyroll DRM endpoint for Playready (currently broken on Crunchyrolls part and therefore disabled) @@ -2199,7 +2263,7 @@ export default class Crunchy implements ServiceClass { } } } else if (!options.novids) { - const streamPlaylists = m3u8(streamPlaylistBody); + const streamPlaylists = m3u8(vstreamPlaylistBody); const plServerList: string[] = [], plStreams: Record> = {}, plQuality: { @@ -2255,9 +2319,7 @@ export default class Crunchy implements ServiceClass { } } - options.x = options.x > plServerList.length ? 1 : options.x; - - const plSelectedServer = plServerList[options.x - 1]; + const plSelectedServer = plServerList[0]; const plSelectedList = plStreams[plSelectedServer]; plQuality.sort((a, b) => { const aMatch: RegExpMatchArray | never[] = a.dim.match(/[0-9]+/) || []; @@ -2290,9 +2352,9 @@ export default class Crunchy implements ServiceClass { type: 'number', replaceWith: quality === 0 ? plQuality[plQuality.length - 1].RESOLUTION.width as number : plQuality[quality - 1].RESOLUTION.width }); - const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); + const lang = langsData.languages.find(a => a.code === vcurStream?.audio_lang); if (!lang) { - console.error(`Unable to find language for code ${curStream.audio_lang}`); + console.error(`Unable to find language for code ${vcurStream.audio_lang}`); return; } console.info(`Selected quality: ${Object.keys(plSelectedList).find(a => plSelectedList[a] === selPlUrl)} @ ${plSelectedServer}`); @@ -2309,9 +2371,12 @@ export default class Crunchy implements ServiceClass { dlFailed = true; } else { // We have the stream, so go ahead and delete the active stream - if (playStream) { + if (videoStream) { await this.refreshToken(true, true); - await this.req.getData(`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${playStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); + } + if (audioStream && (videoStream?.token !== audioStream.token)) { + await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`, {...{method: 'DELETE'}, ...AuthHeaders}); } const chunkPageBody = await chunkPage.res.text(); @@ -2377,9 +2442,9 @@ export default class Crunchy implements ServiceClass { if (!fs.existsSync(dirName)) { fs.mkdirSync(dirName, { recursive: true }); } - const lang = langsData.languages.find(a => a.code === curStream?.audio_lang); + const lang = langsData.languages.find(a => a.code === vcurStream?.audio_lang); if (!lang) { - console.error(`Unable to find language for code ${curStream.audio_lang}`); + console.error(`Unable to find language for code ${vcurStream.audio_lang}`); return; } fs.writeFileSync(`${tsFile}.txt`, compiledChapters.join('\r\n')); @@ -2884,42 +2949,32 @@ export default class Crunchy implements ServiceClass { let episodeList = { total: 0, data: [], meta: {} } as CrunchyEpisodeList; //get episode info - if (this.api == 'android') { - const reqEpsListOpts = [ - api.beta_cms, - this.cmsToken.cms.bucket, - '/episodes?', - new URLSearchParams({ - 'force_locale': '', - 'preferred_audio_language': 'ja-JP', - 'locale': this.locale, - 'season_id': item.id, - 'Policy': this.cmsToken.cms.policy, - 'Signature': this.cmsToken.cms.signature, - 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, - }), - ].join(''); - const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return; - } - //CrunchyEpisodeList - const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; - episodeList = { - total: episodeListAndroid.total, - data: episodeListAndroid.items, - meta: {} - }; - } else { - const reqEpsList = await this.req.getData(`${api.cms}/seasons/${item.id}/episodes?force_locale=&preferred_audio_language=ja-JP&locale=${this.locale}`, AuthHeaders); - if(!reqEpsList.ok || !reqEpsList.res){ - console.error('Episode List Request FAILED!'); - return; - } - //CrunchyEpisodeList - episodeList = await reqEpsList.res.json() as CrunchyEpisodeList; + const reqEpsListOpts = [ + api.cms_bucket, + this.cmsToken.cms.bucket, + '/episodes?', + new URLSearchParams({ + 'force_locale': '', + 'preferred_audio_language': 'ja-JP', + 'locale': this.locale, + 'season_id': item.id, + 'Policy': this.cmsToken.cms.policy, + 'Signature': this.cmsToken.cms.signature, + 'Key-Pair-Id': this.cmsToken.cms.key_pair_id, + }), + ].join(''); + const reqEpsList = await this.req.getData(reqEpsListOpts, AuthHeaders); + if(!reqEpsList.ok || !reqEpsList.res){ + console.error('Episode List Request FAILED!'); + return; } + //CrunchyEpisodeList + const episodeListAndroid = await reqEpsList.res.json() as CrunchyAndroidEpisodes; + episodeList = { + total: episodeListAndroid.total, + data: episodeListAndroid.items, + meta: {} + }; if(episodeList.total < 1){ console.info(' Season is empty!'); diff --git a/gui/server/services/crunchyroll.ts b/gui/server/services/crunchyroll.ts index da09adb..ab4235f 100644 --- a/gui/server/services/crunchyroll.ts +++ b/gui/server/services/crunchyroll.ts @@ -20,7 +20,6 @@ class CrunchyHandler extends Base implements MessageHandler { public getDefaults() { const _default = yargs.appArgv(this.crunchy.cfg.cli, true); - this.crunchy.api = _default.crapi; this.crunchy.locale = _default.locale; } @@ -103,7 +102,6 @@ class CrunchyHandler extends Base implements MessageHandler { console.debug(`Got download options: ${JSON.stringify(data)}`); this.setDownloading(true); const _default = yargs.appArgv(this.crunchy.cfg.cli, true); - this.crunchy.api = _default.crapi; const res = await this.crunchy.downloadFromSeriesID(data.id, { dubLang: data.dubLang, e: data.e diff --git a/modules/module.api-urls.ts b/modules/module.api-urls.ts index f76d3a3..2174670 100644 --- a/modules/module.api-urls.ts +++ b/modules/module.api-urls.ts @@ -24,13 +24,13 @@ export type APIType = { collections: string // beta api defaultUserAgent: string, - beta_profile: string - beta_cmsToken: string + profile: string + cmsToken: string browse_all_series: string, search: string cms: string - beta_browse: string - beta_cms: string, + cms_bucket: string + browse: string drm: string; drm_widevine: string; drm_playready: string; @@ -66,15 +66,14 @@ const api: APIType = { search3: `${domain.api}/autocomplete.0.json`, session: `${domain.api}/start_session.0.json`, collections: `${domain.api}/list_collections.0.json`, - // This User-Agent bypasses Cloudflare security of the newer Endpoint - defaultUserAgent: 'Crunchyroll/4.77.3 (bundle_identifier:com.crunchyroll.iphone; build_number:4148147.285670380) iOS/18.3.2 Gravity/4.77.3', - beta_profile: `${domain.api_beta}/accounts/v1/me/profile`, - beta_cmsToken: `${domain.api_beta}/index/v2`, - search: `${domain.api_beta}/content/v2/discover/search`, - cms: `${domain.api_beta}/content/v2/cms`, - beta_browse: `${domain.api_beta}/content/v1/browse`, - beta_cms: `${domain.api_beta}/cms/v2`, - browse_all_series: `${domain.api_beta}/content/v2/discover/browse`, + defaultUserAgent: 'Crunchyroll/4.83.0 (bundle_identifier:com.crunchyroll.iphone; build_number:4254815.324030705) iOS/19.0.0 Gravity/4.83.0', + profile: `${domain.www}/accounts/v1/me/profile`, + cmsToken: `${domain.www}/index/v2`, + search: `${domain.www}/content/v2/discover/search`, + cms: `${domain.www}/content/v2/cms`, + cms_bucket: `${domain.api_beta}/cms/v2`, + browse: `${domain.www}/content/v1/browse`, + browse_all_series: `${domain.www}/content/v2/discover/browse`, // beta api // broken - deprecated since 06.05.2025 drm: `${domain.api_beta}/drm/v1/auth`, diff --git a/modules/module.app-args.ts b/modules/module.app-args.ts index 6d2900d..b658b25 100644 --- a/modules/module.app-args.ts +++ b/modules/module.app-args.ts @@ -45,8 +45,10 @@ let argvC: { extid: string | undefined; q: number; x: number; - kstream: number; - cstream: keyof typeof CrunchyPlayStreams | 'none'; + // kstream: number; + cstream: keyof typeof CrunchyPlayStreams; + vstream: keyof typeof CrunchyPlayStreams; + astream: keyof typeof CrunchyPlayStreams; partsize: number; hslang: string; dlsubs: string[]; @@ -76,7 +78,7 @@ let argvC: { $0: string; dlVideoOnce: boolean; chapters: boolean; - crapi: 'android' | 'web'; + // crapi: 'android' | 'web'; removeBumpers: boolean; originalFontSize: boolean; keepAllVideos: boolean; diff --git a/modules/module.args.ts b/modules/module.args.ts index 7b6b410..9cdcbd2 100644 --- a/modules/module.args.ts +++ b/modules/module.args.ts @@ -255,20 +255,21 @@ const args: TAppArg[] = [ default: true } }, - { - name: 'crapi', - describe: 'Selects the API type for Crunchyroll', - type: 'string', - group: 'dl', - service: ['crunchy'], - docDescribe: 'If set to Android, it has lower quality, but Non-DRM streams,' - + '\nIf set to Web, it has a higher quality adaptive stream, but everything is DRM.', - usage: '', - choices: ['android', 'web'], - default: { - default: 'web' - } - }, + // Deprecated + // { + // name: 'crapi', + // describe: 'Selects the API type for Crunchyroll', + // type: 'string', + // group: 'dl', + // service: ['crunchy'], + // docDescribe: 'If set to Android, it has lower quality, but Non-DRM streams,' + // + '\nIf set to Web, it has a higher quality adaptive stream, but everything is DRM.', + // usage: '', + // choices: ['android', 'web'], + // default: { + // default: 'web' + // } + // }, { name: 'removeBumpers', describe: 'Remove bumpers from final video', @@ -305,30 +306,43 @@ const args: TAppArg[] = [ type: 'number', alias: 'server', docDescribe: true, - service: ['crunchy'], + service: ['all'], usage: '${server}' }, - { - name: 'kstream', - group: 'dl', - alias: 'k', - describe: 'Select specific stream', - choices: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - default: { - default: 1 - }, - docDescribe: true, - service: ['crunchy'], - type: 'number', - usage: '${stream}' - }, + // Deprecated + // { + // name: 'kstream', + // group: 'dl', + // alias: 'k', + // describe: 'Select specific stream', + // choices: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + // default: { + // default: 1 + // }, + // docDescribe: true, + // service: ['crunchy'], + // type: 'number', + // usage: '${stream}' + // }, + // About to Deprecate { name: 'cstream', group: 'dl', alias: 'cs', service: ['crunchy'], type: 'string', - describe: 'Select a specific Crunchyroll playback endpoint by device, or disable the stream using "none". Since Crunchyroll has started rolling out their new VBR encodes, we highly recommend using a TV endpoint (e.g. vidaa, samsungtv, lgtv, rokutv, chromecast, firetv, androidtv) to access the old CBR encodes. Please note: The older encodes do not include the new 192 kbps audio, the new audio is only available with the new VBR encodes.', + describe: '(Please use --vstream and --astream instead, this will deprecate soon) Select a specific Crunchyroll playback endpoint by device. Since Crunchyroll has started rolling out their new VBR encodes, we highly recommend using a TV endpoint (e.g. vidaa, samsungtv, lgtv, rokutv, chromecast, firetv, androidtv) to access the old CBR encodes. Please note: The older encodes do not include the new 192 kbps audio, the new audio is only available with the new VBR encodes.', + choices: [...Object.keys(CrunchyPlayStreams), 'none'], + docDescribe: true, + usage: '${device}' + }, + { + name: 'vstream', + group: 'dl', + alias: 'vs', + service: ['crunchy'], + type: 'string', + describe: 'Select a specific Crunchyroll video playback endpoint by device.', choices: [...Object.keys(CrunchyPlayStreams), 'none'], default: { default: 'lgtv' @@ -336,6 +350,20 @@ const args: TAppArg[] = [ docDescribe: true, usage: '${device}' }, + { + name: 'astream', + group: 'dl', + alias: 'as', + service: ['crunchy'], + type: 'string', + describe: 'Select a specific Crunchyroll audio playback endpoint by device.', + choices: [...Object.keys(CrunchyPlayStreams), 'none'], + default: { + default: 'firefox' + }, + docDescribe: true, + usage: '${device}' + }, { name: 'hslang', group: 'dl', diff --git a/modules/module.fetch.ts b/modules/module.fetch.ts index 4db8aec..616bfcb 100644 --- a/modules/module.fetch.ts +++ b/modules/module.fetch.ts @@ -5,7 +5,7 @@ import { connect } from 'puppeteer-real-browser'; export type Params = { method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; headers?: Record; - body?: string | Buffer; + body?: BodyInit | undefined; binary?: boolean; followRedirect?: 'follow' | 'error' | 'manual'; }; diff --git a/package.json b/package.json index 1fc2ce3..ef64ab1 100644 --- a/package.json +++ b/package.json @@ -41,15 +41,15 @@ "license": "MIT", "dependencies": { "@bufbuild/buf": "^1.55.1", - "@bufbuild/protobuf": "^2.6.1", - "@bufbuild/protoc-gen-es": "^2.6.1", - "@yao-pkg/pkg": "^6.5.1", + "@bufbuild/protobuf": "^2.6.2", + "@bufbuild/protoc-gen-es": "^2.6.2", + "@yao-pkg/pkg": "^6.6.0", "binary-parser": "^2.2.1", "binary-parser-encoder": "^1.5.3", "bn.js": "^5.2.2", "cors": "^2.8.5", "elliptic": "^6.6.1", - "esbuild": "^0.25.6", + "esbuild": "^0.25.8", "express": "^5.1.0", "fast-xml-parser": "^5.2.5", "ffprobe": "^1.1.2", @@ -65,31 +65,31 @@ "ofetch": "^1.4.1", "open": "^8.4.2", "protobufjs": "^7.5.3", - "puppeteer-real-browser": "^1.4.2", + "puppeteer-real-browser": "^1.4.3", "ws": "^8.18.3", "yaml": "^2.8.0", "yargs": "^17.7.2" }, "devDependencies": { - "@eslint/js": "^9.31.0", + "@eslint/js": "^9.32.0", "@types/bn.js": "^5.2.0", "@types/cors": "^2.8.19", "@types/elliptic": "^6.4.18", "@types/express": "^5.0.3", "@types/ffprobe": "^1.1.8", "@types/fs-extra": "^11.0.4", - "@types/node": "^24.0.14", + "@types/node": "^24.1.0", "@types/node-forge": "^1.3.13", "@types/ws": "^8.18.1", "@types/yargs": "^17.0.33", - "@typescript-eslint/eslint-plugin": "^8.37.0", - "@typescript-eslint/parser": "^8.37.0", - "eslint": "^9.31.0", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", + "eslint": "^9.32.0", "protoc": "^1.1.3", "removeNPMAbsolutePaths": "^3.0.1", "ts-node": "^10.9.2", "typescript": "^5.8.3", - "typescript-eslint": "^8.37.0" + "typescript-eslint": "^8.38.0" }, "scripts": { "prestart": "pnpm run tsc test", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24bf313..8ac992d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,14 +12,14 @@ importers: specifier: ^1.55.1 version: 1.55.1 '@bufbuild/protobuf': - specifier: ^2.6.1 - version: 2.6.1 + specifier: ^2.6.2 + version: 2.6.2 '@bufbuild/protoc-gen-es': - specifier: ^2.6.1 - version: 2.6.1(@bufbuild/protobuf@2.6.1) + specifier: ^2.6.2 + version: 2.6.2(@bufbuild/protobuf@2.6.2) '@yao-pkg/pkg': - specifier: ^6.5.1 - version: 6.5.1 + specifier: ^6.6.0 + version: 6.6.0 binary-parser: specifier: ^2.2.1 version: 2.2.1 @@ -36,8 +36,8 @@ importers: specifier: ^6.6.1 version: 6.6.1 esbuild: - specifier: ^0.25.6 - version: 0.25.6 + specifier: ^0.25.8 + version: 0.25.8 express: specifier: ^5.1.0 version: 5.1.0 @@ -84,8 +84,8 @@ importers: specifier: ^7.5.3 version: 7.5.3 puppeteer-real-browser: - specifier: ^1.4.2 - version: 1.4.2 + specifier: ^1.4.3 + version: 1.4.3 ws: specifier: ^8.18.3 version: 8.18.3 @@ -97,8 +97,8 @@ importers: version: 17.7.2 devDependencies: '@eslint/js': - specifier: ^9.31.0 - version: 9.31.0 + specifier: ^9.32.0 + version: 9.32.0 '@types/bn.js': specifier: ^5.2.0 version: 5.2.0 @@ -118,8 +118,8 @@ importers: specifier: ^11.0.4 version: 11.0.4 '@types/node': - specifier: ^24.0.14 - version: 24.0.14 + specifier: ^24.1.0 + version: 24.1.0 '@types/node-forge': specifier: ^1.3.13 version: 1.3.13 @@ -130,14 +130,14 @@ importers: specifier: ^17.0.33 version: 17.0.33 '@typescript-eslint/eslint-plugin': - specifier: ^8.37.0 - version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: ^8.38.0 + version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint@9.32.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^8.37.0 - version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) + specifier: ^8.38.0 + version: 8.38.0(eslint@9.32.0)(typescript@5.8.3) eslint: - specifier: ^9.31.0 - version: 9.31.0 + specifier: ^9.32.0 + version: 9.32.0 protoc: specifier: ^1.1.3 version: 1.1.3 @@ -146,13 +146,13 @@ importers: version: 3.0.1 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@24.0.14)(typescript@5.8.3) + version: 10.9.2(@types/node@24.1.0)(typescript@5.8.3) typescript: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.37.0 - version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) + specifier: ^8.38.0 + version: 8.38.0(eslint@9.32.0)(typescript@5.8.3) packages: @@ -173,12 +173,12 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.27.6': - resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + '@babel/runtime@7.28.2': + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.1': - resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} '@bufbuild/buf-darwin-arm64@1.55.1': @@ -228,178 +228,178 @@ packages: engines: {node: '>=12'} hasBin: true - '@bufbuild/protobuf@2.6.1': - resolution: {integrity: sha512-DaG6XlyKpz08bmHY5SGX2gfIllaqtDJ/KwVoxsmP22COOLYwDBe7yD3DZGwXem/Xq7QOc9cuR7R3MpAv5CFfDw==} + '@bufbuild/protobuf@2.6.2': + resolution: {integrity: sha512-vLu7SRY84CV/Dd+NUdgtidn2hS5hSMUC1vDBY0VcviTdgRYkU43vIz3vIFbmx14cX1r+mM7WjzE5Fl1fGEM0RQ==} - '@bufbuild/protoc-gen-es@2.6.1': - resolution: {integrity: sha512-LIxXANgj27QHGZrCekfB/v9zEWzuxDBQyE+91BjY7KbvrELcNQyN0kFGRLy/Eu2bLC4+n1hlCL9TIjN49wV2Fw==} + '@bufbuild/protoc-gen-es@2.6.2': + resolution: {integrity: sha512-23oadoLfK9E+p5jLulwz9fRf8oE4JiwbMp7/CXdogNhuADJxpbfnCV+2ByJgG+QTU+uaGQBro79XqO2CQdzRMQ==} engines: {node: '>=14'} hasBin: true peerDependencies: - '@bufbuild/protobuf': 2.6.1 + '@bufbuild/protobuf': 2.6.2 peerDependenciesMeta: '@bufbuild/protobuf': optional: true - '@bufbuild/protoplugin@2.6.1': - resolution: {integrity: sha512-pheV/ysWCMSCyRnR7oE2qjEM2QEFFkwYOtHaS86VbxbGfSohcw64rorU7ldCVFZHlFjIsSAqd/yIuTy4J3Itlg==} + '@bufbuild/protoplugin@2.6.2': + resolution: {integrity: sha512-EYVPnRXOkWuqBDqRYhLsquOnk5SGU2Ce3lAOBDEZyntHwu0xcjcIZeTI+as+HnArtcJwrXK0u6x3Rtmt2IcHlA==} '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@esbuild/aix-ppc64@0.25.6': - resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.6': - resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.6': - resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.6': - resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.6': - resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.6': - resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.6': - resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.6': - resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.6': - resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.6': - resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.6': - resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.6': - resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.6': - resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.6': - resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.6': - resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.6': - resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.6': - resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.6': - resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.6': - resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.6': - resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.6': - resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.6': - resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.6': - resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.6': - resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.6': - resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.6': - resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -430,16 +430,16 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.31.0': - resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} + '@eslint/js@9.32.0': + resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + '@eslint/plugin-kit@0.3.4': + resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -598,8 +598,8 @@ packages: '@types/node-forge@1.3.13': resolution: {integrity: sha512-zePQJSW5QkwSHKRApqWCVKeKoSOt4xvEnLENZPjyvm9Ezdf/EyDeJM7jqLzOwjVICQQzvLZ63T55MKdJB5H6ww==} - '@types/node@24.0.14': - resolution: {integrity: sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==} + '@types/node@24.1.0': + resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -625,63 +625,63 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.37.0': - resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==} + '@typescript-eslint/eslint-plugin@8.38.0': + resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.37.0 + '@typescript-eslint/parser': ^8.38.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.37.0': - resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==} + '@typescript-eslint/parser@8.38.0': + resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/project-service@8.37.0': - resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} + '@typescript-eslint/project-service@8.38.0': + resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.37.0': - resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} + '@typescript-eslint/scope-manager@8.38.0': + resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.37.0': - resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} + '@typescript-eslint/tsconfig-utils@8.38.0': + resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.37.0': - resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==} + '@typescript-eslint/type-utils@8.38.0': + resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.37.0': - resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} + '@typescript-eslint/types@8.38.0': + resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.37.0': - resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} + '@typescript-eslint/typescript-estree@8.38.0': + resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.37.0': - resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} + '@typescript-eslint/utils@8.38.0': + resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.37.0': - resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} + '@typescript-eslint/visitor-keys@8.38.0': + resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/vfs@1.6.1': @@ -701,12 +701,12 @@ packages: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} - '@yao-pkg/pkg-fetch@3.5.23': - resolution: {integrity: sha512-rn45sqVQSkcJNSBdTnYze3n+kyub4CN8aiWYlPgA9yp9FZeEF+BlpL68kSIm3HaVuANniF+7RBMH5DkC4zlHZA==} + '@yao-pkg/pkg-fetch@3.5.24': + resolution: {integrity: sha512-FPESCH1uXCYui6jeDp2aayWuFHR39w+uU1r88nI6JWRvPYOU64cHPUV/p6GSFoQdpna7ip92HnrZKbBC60l0gA==} hasBin: true - '@yao-pkg/pkg@6.5.1': - resolution: {integrity: sha512-z6XlySYfnqfm1AfVlBN8A3yeAQniIwL7TKQfDCGsswYSVYLt2snbRefQYsfQQ3pw5lVXrZdLqgTjzaqID9IkWA==} + '@yao-pkg/pkg@6.6.0': + resolution: {integrity: sha512-3/oiaSm7fS0Fc7dzp22r9B7vFaguGhO9vERgEReRYj2EUzdi5ssyYhe1uYJG4ec/dmo2GG6RRHOUAT8savl79Q==} engines: {node: '>=18.0.0'} hasBin: true @@ -981,14 +981,6 @@ packages: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -1080,8 +1072,8 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild@0.25.6: - resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} engines: {node: '>=18'} hasBin: true @@ -1113,8 +1105,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.31.0: - resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} + eslint@9.32.0: + resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1483,8 +1475,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lighthouse-logger@2.0.1: - resolution: {integrity: sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==} + lighthouse-logger@2.0.2: + resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} listenercount@1.0.1: resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} @@ -1602,9 +1594,6 @@ packages: resolution: {integrity: sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==} hasBin: true - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1811,8 +1800,8 @@ packages: puppeteer-core: optional: true - puppeteer-real-browser@1.4.2: - resolution: {integrity: sha512-HgL8HWy2VIViJSACK/FcklwqRDBRvMjVjIR4f7Pe8VNn5kPxBe9HTVEu0ckx9mE/61BgiUADJZn25nPQn4FOsw==} + puppeteer-real-browser@1.4.3: + resolution: {integrity: sha512-tul5v//zYe9AwV+jaQkT6gXa9yYxCfSXQqPx0AlnmiiELhlSkWuk6XeLbgXsS+w6YgdIilKbztaXEoKxE5cC3w==} qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} @@ -2107,8 +2096,8 @@ packages: typed-query-selector@2.12.0: resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} - typescript-eslint@8.37.0: - resolution: {integrity: sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==} + typescript-eslint@8.38.0: + resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2267,7 +2256,7 @@ snapshots: '@babel/generator@7.28.0': dependencies: '@babel/parser': 7.28.0 - '@babel/types': 7.28.1 + '@babel/types': 7.28.2 '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 @@ -2278,11 +2267,11 @@ snapshots: '@babel/parser@7.28.0': dependencies: - '@babel/types': 7.28.1 + '@babel/types': 7.28.2 - '@babel/runtime@7.27.6': {} + '@babel/runtime@7.28.2': {} - '@babel/types@7.28.1': + '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -2318,19 +2307,19 @@ snapshots: '@bufbuild/buf-win32-arm64': 1.55.1 '@bufbuild/buf-win32-x64': 1.55.1 - '@bufbuild/protobuf@2.6.1': {} + '@bufbuild/protobuf@2.6.2': {} - '@bufbuild/protoc-gen-es@2.6.1(@bufbuild/protobuf@2.6.1)': + '@bufbuild/protoc-gen-es@2.6.2(@bufbuild/protobuf@2.6.2)': dependencies: - '@bufbuild/protoplugin': 2.6.1 + '@bufbuild/protoplugin': 2.6.2 optionalDependencies: - '@bufbuild/protobuf': 2.6.1 + '@bufbuild/protobuf': 2.6.2 transitivePeerDependencies: - supports-color - '@bufbuild/protoplugin@2.6.1': + '@bufbuild/protoplugin@2.6.2': dependencies: - '@bufbuild/protobuf': 2.6.1 + '@bufbuild/protobuf': 2.6.2 '@typescript/vfs': 1.6.1(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: @@ -2340,87 +2329,87 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@esbuild/aix-ppc64@0.25.6': + '@esbuild/aix-ppc64@0.25.8': optional: true - '@esbuild/android-arm64@0.25.6': + '@esbuild/android-arm64@0.25.8': optional: true - '@esbuild/android-arm@0.25.6': + '@esbuild/android-arm@0.25.8': optional: true - '@esbuild/android-x64@0.25.6': + '@esbuild/android-x64@0.25.8': optional: true - '@esbuild/darwin-arm64@0.25.6': + '@esbuild/darwin-arm64@0.25.8': optional: true - '@esbuild/darwin-x64@0.25.6': + '@esbuild/darwin-x64@0.25.8': optional: true - '@esbuild/freebsd-arm64@0.25.6': + '@esbuild/freebsd-arm64@0.25.8': optional: true - '@esbuild/freebsd-x64@0.25.6': + '@esbuild/freebsd-x64@0.25.8': optional: true - '@esbuild/linux-arm64@0.25.6': + '@esbuild/linux-arm64@0.25.8': optional: true - '@esbuild/linux-arm@0.25.6': + '@esbuild/linux-arm@0.25.8': optional: true - '@esbuild/linux-ia32@0.25.6': + '@esbuild/linux-ia32@0.25.8': optional: true - '@esbuild/linux-loong64@0.25.6': + '@esbuild/linux-loong64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.25.6': + '@esbuild/linux-mips64el@0.25.8': optional: true - '@esbuild/linux-ppc64@0.25.6': + '@esbuild/linux-ppc64@0.25.8': optional: true - '@esbuild/linux-riscv64@0.25.6': + '@esbuild/linux-riscv64@0.25.8': optional: true - '@esbuild/linux-s390x@0.25.6': + '@esbuild/linux-s390x@0.25.8': optional: true - '@esbuild/linux-x64@0.25.6': + '@esbuild/linux-x64@0.25.8': optional: true - '@esbuild/netbsd-arm64@0.25.6': + '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.25.6': + '@esbuild/netbsd-x64@0.25.8': optional: true - '@esbuild/openbsd-arm64@0.25.6': + '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.25.6': + '@esbuild/openbsd-x64@0.25.8': optional: true - '@esbuild/openharmony-arm64@0.25.6': + '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/sunos-x64@0.25.6': + '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/win32-arm64@0.25.6': + '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/win32-ia32@0.25.6': + '@esbuild/win32-ia32@0.25.8': optional: true - '@esbuild/win32-x64@0.25.6': + '@esbuild/win32-x64@0.25.8': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0)': dependencies: - eslint: 9.31.0 + eslint: 9.32.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -2453,11 +2442,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.31.0': {} + '@eslint/js@9.32.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.3': + '@eslint/plugin-kit@0.3.4': dependencies: '@eslint/core': 0.15.1 levn: 0.4.1 @@ -2561,20 +2550,20 @@ snapshots: '@types/bn.js@5.2.0': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/connect@3.4.38': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/cors@2.8.19': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/debug@4.1.12': dependencies: @@ -2588,7 +2577,7 @@ snapshots: '@types/express-serve-static-core@5.0.7': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -2604,7 +2593,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/http-errors@2.0.5': {} @@ -2612,7 +2601,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/mime@1.3.5': {} @@ -2620,9 +2609,9 @@ snapshots: '@types/node-forge@1.3.13': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 - '@types/node@24.0.14': + '@types/node@24.1.0': dependencies: undici-types: 7.8.0 @@ -2633,17 +2622,17 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/send': 0.17.5 '@types/ws@8.18.1': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 '@types/yargs-parser@21.0.3': {} @@ -2653,18 +2642,18 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 optional: true - '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint@9.32.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.37.0 - eslint: 9.31.0 + '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.38.0 + eslint: 9.32.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -2673,56 +2662,56 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.37.0 + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.38.0 debug: 4.4.1 - eslint: 9.31.0 + eslint: 9.32.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.37.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.38.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) - '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3) + '@typescript-eslint/types': 8.38.0 debug: 4.4.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.37.0': + '@typescript-eslint/scope-manager@8.38.0': dependencies: - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 - '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)': dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.38.0(eslint@9.32.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3) debug: 4.4.1 - eslint: 9.31.0 + eslint: 9.32.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.37.0': {} + '@typescript-eslint/types@8.38.0': {} - '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.37.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/visitor-keys': 8.37.0 + '@typescript-eslint/project-service': 8.38.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3) + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -2733,20 +2722,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.38.0(eslint@9.32.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) - '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/types': 8.37.0 - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - eslint: 9.31.0 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) + eslint: 9.32.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.37.0': + '@typescript-eslint/visitor-keys@8.38.0': dependencies: - '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/types': 8.38.0 eslint-visitor-keys: 4.2.1 '@typescript/vfs@1.6.1(typescript@5.4.5)': @@ -2758,18 +2747,18 @@ snapshots: '@videojs/vhs-utils@4.0.0': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 global: 4.4.0 url-toolkit: 2.2.5 '@videojs/vhs-utils@4.1.1': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 global: 4.4.0 '@xmldom/xmldom@0.8.10': {} - '@yao-pkg/pkg-fetch@3.5.23': + '@yao-pkg/pkg-fetch@3.5.24': dependencies: https-proxy-agent: 5.0.1 node-fetch: 2.7.0 @@ -2782,12 +2771,12 @@ snapshots: - encoding - supports-color - '@yao-pkg/pkg@6.5.1': + '@yao-pkg/pkg@6.6.0': dependencies: '@babel/generator': 7.28.0 '@babel/parser': 7.28.0 - '@babel/types': 7.28.1 - '@yao-pkg/pkg-fetch': 3.5.23 + '@babel/types': 7.28.2 + '@yao-pkg/pkg-fetch': 3.5.24 into-stream: 6.0.0 minimist: 1.2.8 multistream: 4.1.0 @@ -2983,10 +2972,10 @@ snapshots: chrome-launcher@1.2.0: dependencies: - '@types/node': 24.0.14 + '@types/node': 24.1.0 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 2.0.1 + lighthouse-logger: 2.0.2 transitivePeerDependencies: - supports-color @@ -3060,10 +3049,6 @@ snapshots: date-format@4.0.14: {} - debug@2.6.9: - dependencies: - ms: 2.0.0 - debug@4.4.1: dependencies: ms: 2.1.3 @@ -3140,34 +3125,34 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild@0.25.6: + esbuild@0.25.8: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.6 - '@esbuild/android-arm': 0.25.6 - '@esbuild/android-arm64': 0.25.6 - '@esbuild/android-x64': 0.25.6 - '@esbuild/darwin-arm64': 0.25.6 - '@esbuild/darwin-x64': 0.25.6 - '@esbuild/freebsd-arm64': 0.25.6 - '@esbuild/freebsd-x64': 0.25.6 - '@esbuild/linux-arm': 0.25.6 - '@esbuild/linux-arm64': 0.25.6 - '@esbuild/linux-ia32': 0.25.6 - '@esbuild/linux-loong64': 0.25.6 - '@esbuild/linux-mips64el': 0.25.6 - '@esbuild/linux-ppc64': 0.25.6 - '@esbuild/linux-riscv64': 0.25.6 - '@esbuild/linux-s390x': 0.25.6 - '@esbuild/linux-x64': 0.25.6 - '@esbuild/netbsd-arm64': 0.25.6 - '@esbuild/netbsd-x64': 0.25.6 - '@esbuild/openbsd-arm64': 0.25.6 - '@esbuild/openbsd-x64': 0.25.6 - '@esbuild/openharmony-arm64': 0.25.6 - '@esbuild/sunos-x64': 0.25.6 - '@esbuild/win32-arm64': 0.25.6 - '@esbuild/win32-ia32': 0.25.6 - '@esbuild/win32-x64': 0.25.6 + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 escalade@3.2.0: {} @@ -3192,16 +3177,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.31.0: + eslint@9.32.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.0 '@eslint/core': 0.15.1 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.31.0 - '@eslint/plugin-kit': 0.3.3 + '@eslint/js': 9.32.0 + '@eslint/plugin-kit': 0.3.4 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -3633,9 +3618,9 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lighthouse-logger@2.0.1: + lighthouse-logger@2.0.2: dependencies: - debug: 2.6.9 + debug: 4.4.1 marky: 1.3.0 transitivePeerDependencies: - supports-color @@ -3670,7 +3655,7 @@ snapshots: m3u8-parser@7.2.0: dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 '@videojs/vhs-utils': 4.1.1 global: 4.4.0 @@ -3735,13 +3720,11 @@ snapshots: mpd-parser@1.3.1: dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.2 '@videojs/vhs-utils': 4.0.0 '@xmldom/xmldom': 0.8.10 global: 4.4.0 - ms@2.0.0: {} - ms@2.1.3: {} multistream@4.1.0: @@ -3904,7 +3887,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.0.14 + '@types/node': 24.1.0 long: 5.3.2 protoc@1.1.3: @@ -3952,7 +3935,7 @@ snapshots: transitivePeerDependencies: - supports-color - puppeteer-real-browser@1.4.2: + puppeteer-real-browser@1.4.3: dependencies: chrome-launcher: 1.2.0 ghost-cursor: 1.4.1 @@ -4287,14 +4270,14 @@ snapshots: dependencies: typescript: 5.8.3 - ts-node@10.9.2(@types/node@24.0.14)(typescript@5.8.3): + ts-node@10.9.2(@types/node@24.1.0)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.0.14 + '@types/node': 24.1.0 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -4323,13 +4306,13 @@ snapshots: typed-query-selector@2.12.0: {} - typescript-eslint@8.37.0(eslint@9.31.0)(typescript@5.8.3): + typescript-eslint@8.38.0(eslint@9.32.0)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) - eslint: 9.31.0 + '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.8.3))(eslint@9.32.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.8.3) + eslint: 9.32.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color