diff --git a/__checks__/api.check.ts b/__checks__/api.check.ts deleted file mode 100644 index e1ae53852..000000000 --- a/__checks__/api.check.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiCheck, AssertionBuilder } from 'checkly/constructs'; - -const homepageApiCheck = new ApiCheck('homepage-api-check-1', { - name: 'Fetch Book List', - alertChannels: [], - degradedResponseTime: 10000, - maxResponseTime: 20000, - request: { - url: 'https://danube-web.shop/api/books', - method: 'GET', - followRedirects: true, - skipSSL: false, - assertions: [ - AssertionBuilder.statusCode().equals(200), - AssertionBuilder.jsonBody('$[0].id').isNotNull(), - ], - }, -}); - -export default homepageApiCheck; diff --git a/__checks__/homepage.spec.ts b/__checks__/homepage.spec.ts deleted file mode 100644 index c080869ef..000000000 --- a/__checks__/homepage.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { test, expect, Page } from '@playwright/test'; - -// You can override the default Playwright test timeout of 30s -// test.setTimeout(60_000); - -test('Checkly Homepage', async ({ page }: { page: Page }) => { - const response = await page.goto('https://danube-web.shop'); - expect(response?.status()).toBeLessThan(400); - await expect(page).toHaveTitle(/Danube WebShop/); - await page.screenshot({ path: 'homepage.jpg' }); -}); diff --git a/package.json b/package.json index 0b459ac69..657efb50d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "devDependencies": { "@next/bundle-analyzer": "^13.0.0", "@next/eslint-plugin-next": "^13.0.0", - "@playwright/test": "^1.33.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@types/dockerode": "^3.3.9", @@ -110,7 +109,7 @@ "turbo": "latest", "typescript": "^5.0.4", "video.js": "^8.0.3", - "vitest": "^0.29.3", + "vitest": "^0.31.1", "vitest-fetch-mock": "^0.2.2" }, "resolutions": { diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx index 2f299e4e8..57b7810d5 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/IntegrationTab/Components/InputElements/IntegrationSelector.tsx @@ -1,5 +1,5 @@ /* eslint-disable @next/next/no-img-element */ -import { Group, Select, SelectItem, Text } from '@mantine/core'; +import { Group, Image, Select, SelectItem, Text } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; import { useTranslation } from 'next-i18next'; import { forwardRef } from 'react'; @@ -87,9 +87,14 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { }, { value: 'pihole', - image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pihole.png', + image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png', label: 'PiHole', }, + { + value: 'adGuardHome', + image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png', + label: 'AdGuard Home', + }, ].filter((x) => Object.keys(integrationFieldProperties).includes(x.value)); const getNewProperties = (value: string | null): AppIntegrationPropertyType[] => { @@ -133,11 +138,12 @@ export const IntegrationSelector = ({ form }: IntegrationSelectorProps) => { } icon={ form.values.integration?.type && ( - x.value === form.values.integration?.type)?.image} alt="integration" width={20} height={20} + fit="contain" /> ) } @@ -160,7 +166,7 @@ const SelectItemComponent = forwardRef( ({ image, label, description, ...others }: ItemProps, ref) => (
- integration icon + integration icon
{label} diff --git a/src/pages/api/modules/dns-hole/control.ts b/src/pages/api/modules/dns-hole/control.ts index 360b7b6dc..c3a3424dc 100644 --- a/src/pages/api/modules/dns-hole/control.ts +++ b/src/pages/api/modules/dns-hole/control.ts @@ -5,6 +5,8 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { getConfig } from '../../../../tools/config/getConfig'; import { findAppProperty } from '../../../../tools/client/app-properties'; import { PiHoleClient } from '../../../../tools/server/sdk/pihole/piHole'; +import { ConfigAppType } from '../../../../types/app'; +import { AdGuard } from '../../../../tools/server/sdk/adGuard/adGuard'; const getQuerySchema = z.object({ status: z.enum(['enabled', 'disabled']), @@ -21,29 +23,50 @@ export const Post = async (request: NextApiRequest, response: NextApiResponse) = return; } - const applicableApps = config.apps.filter((x) => x.integration?.type === 'pihole'); + const applicableApps = config.apps.filter( + (x) => x.integration?.type && ['pihole', 'adGuardHome'].includes(x.integration?.type) + ); for (let i = 0; i < applicableApps.length; i += 1) { const app = applicableApps[i]; - const pihole = new PiHoleClient( - app.url, - findAppProperty(app, 'password') - ); - - switch (parseResult.data.status) { - case 'enabled': - await pihole.enable(); - break; - case 'disabled': - await pihole.disable(); - break; + if (app.integration?.type === 'pihole') { + await processPiHole(app, parseResult.data.status === 'disabled'); + return; } + + await processAdGuard(app, parseResult.data.status === 'disabled'); } response.status(200).json({}); }; +const processAdGuard = async (app: ConfigAppType, enable: boolean) => { + const adGuard = new AdGuard( + app.url, + findAppProperty(app, 'username'), + findAppProperty(app, 'password') + ); + + if (enable) { + await adGuard.disable(); + return; + } + + await adGuard.enable(); +}; + +const processPiHole = async (app: ConfigAppType, enable: boolean) => { + const pihole = new PiHoleClient(app.url, findAppProperty(app, 'password')); + + if (enable) { + await pihole.enable(); + return; + } + + await pihole.disable(); +}; + export default async (request: NextApiRequest, response: NextApiResponse) => { if (request.method === 'POST') { return Post(request, response); diff --git a/src/pages/api/modules/dns-hole/summary.ts b/src/pages/api/modules/dns-hole/summary.ts index 5903ed44f..8970f0851 100644 --- a/src/pages/api/modules/dns-hole/summary.ts +++ b/src/pages/api/modules/dns-hole/summary.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-await-in-loop */ import Consola from 'consola'; import { getCookie } from 'cookies-next'; import { NextApiRequest, NextApiResponse } from 'next'; @@ -5,12 +6,15 @@ import { findAppProperty } from '../../../../tools/client/app-properties'; import { getConfig } from '../../../../tools/config/getConfig'; import { PiHoleClient } from '../../../../tools/server/sdk/pihole/piHole'; import { AdStatistics } from '../../../../widgets/dnshole/type'; +import { AdGuard } from '../../../../tools/server/sdk/adGuard/adGuard'; export const Get = async (request: NextApiRequest, response: NextApiResponse) => { const configName = getCookie('config-name', { req: request }); const config = getConfig(configName?.toString() ?? 'default'); - const applicableApps = config.apps.filter((x) => x.integration?.type === 'pihole'); + const applicableApps = config.apps.filter( + (x) => x.integration?.type && ['pihole', 'adGuardHome'].includes(x.integration?.type) + ); const data: AdStatistics = { domainsBeingBlocked: 0, @@ -26,21 +30,51 @@ export const Get = async (request: NextApiRequest, response: NextApiResponse) => const app = applicableApps[i]; try { - const piHole = new PiHoleClient(app.url, findAppProperty(app, 'password')); + switch (app.integration?.type) { + case 'pihole': { + const piHole = new PiHoleClient(app.url, findAppProperty(app, 'password')); + const summary = await piHole.getSummary(); - // eslint-disable-next-line no-await-in-loop - const summary = await piHole.getSummary(); + data.domainsBeingBlocked += summary.domains_being_blocked; + data.adsBlockedToday += summary.ads_blocked_today; + data.dnsQueriesToday += summary.dns_queries_today; + data.status.push({ + status: summary.status, + appId: app.id, + }); + adsBlockedTodayPercentageArr.push(summary.ads_percentage_today); + break; + } + case 'adGuardHome': { + const adGuard = new AdGuard( + app.url, + findAppProperty(app, 'username'), + findAppProperty(app, 'password') + ); - data.domainsBeingBlocked += summary.domains_being_blocked; - data.adsBlockedToday += summary.ads_blocked_today; - data.dnsQueriesToday += summary.dns_queries_today; - data.status.push({ - status: summary.status, - appId: app.id, - }); - adsBlockedTodayPercentageArr.push(summary.ads_percentage_today); + const stats = await adGuard.getStats(); + const status = await adGuard.getStatus(); + const countFilteredDomains = await adGuard.getCountFilteringDomains(); + + const blockedQueriesToday = stats.blocked_filtering.reduce((prev, sum) => prev + sum, 0); + const queriesToday = stats.dns_queries.reduce((prev, sum) => prev + sum, 0); + data.adsBlockedToday = blockedQueriesToday; + data.domainsBeingBlocked += countFilteredDomains; + data.dnsQueriesToday += queriesToday; + data.status.push({ + status: status.protection_enabled ? 'enabled' : 'disabled', + appId: app.id, + }); + adsBlockedTodayPercentageArr.push((queriesToday / blockedQueriesToday) * 100); + break; + } + default: { + Consola.error(`Integration communication for app ${app.id} failed: unknown type`); + break; + } + } } catch (err) { - Consola.error(`Failed to communicate with PiHole at ${app.url}: ${err}`); + Consola.error(`Failed to communicate with DNS hole at ${app.url}: ${err}`); } } diff --git a/src/pages/api/modules/media-requests/index.spec.ts b/src/pages/api/modules/media-requests/index.spec.ts index 9f1cb8dcd..0365c2820 100644 --- a/src/pages/api/modules/media-requests/index.spec.ts +++ b/src/pages/api/modules/media-requests/index.spec.ts @@ -78,7 +78,7 @@ describe('media-requests api', () => { logSpy.mockRestore(); }); - it('fetch and return requests in response', async () => { + it('fetch and return requests in response with external url', async () => { // Arrange const { req, res } = createMocks({ method: 'GET', @@ -108,7 +108,16 @@ describe('media-requests api', () => { }, }, ], - } as ConfigType); + widgets: [ + { + id: 'hjeruijgrig', + type: 'media-requests-list', + properties: { + replaceLinksWithExternalHost: true, + }, + }, + ], + } as unknown as ConfigType); const logSpy = vi.spyOn(Consola, 'error'); fetchMock.mockResponse((request) => { @@ -287,6 +296,235 @@ describe('media-requests api', () => { 'https://image.tmdb.org/t/p/w600_and_h900_bestv2//hf4j0928gq543njgh8935nqh8.jpg', status: 2, type: 'movie', + userLink: 'http://my-overseerr.external/users/1', + userName: 'Example User', + userProfilePicture: 'http://my-overseerr.external//os_logo_square.png', + }, + ]); + + expect(logSpy).not.toHaveBeenCalled(); + + logSpy.mockRestore(); + }); + + it('fetch and return requests in response with internal url', async () => { + // Arrange + const { req, res } = createMocks({ + method: 'GET', + }); + + vi.mock('./../../../../tools/config/getConfig.ts', () => ({ + get getConfig() { + return mockedGetConfig; + }, + })); + mockedGetConfig.mockReturnValue({ + apps: [ + { + url: 'http://my-overseerr.local', + behaviour: { + externalUrl: 'http://my-overseerr.external', + }, + integration: { + type: 'overseerr', + properties: [ + { + field: 'apiKey', + type: 'private', + value: 'abc', + }, + ], + }, + }, + ], + widgets: [ + { + id: 'hjeruijgrig', + type: 'media-requests-list', + properties: { + replaceLinksWithExternalHost: false, + }, + }, + ], + } as unknown as ConfigType); + const logSpy = vi.spyOn(Consola, 'error'); + + fetchMock.mockResponse((request) => { + if (request.url === 'http://my-overseerr.local/api/v1/request?take=25&skip=0&sort=added') { + return JSON.stringify({ + pageInfo: { pages: 3, pageSize: 20, results: 42, page: 1 }, + results: [ + { + id: 44, + status: 2, + createdAt: '2023-04-06T19:38:45.000Z', + updatedAt: '2023-04-06T19:38:45.000Z', + type: 'movie', + is4k: false, + serverId: 0, + profileId: 4, + tags: [], + isAutoRequest: false, + media: { + downloadStatus: [], + downloadStatus4k: [], + id: 999, + mediaType: 'movie', + tmdbId: 99999999, + tvdbId: null, + imdbId: null, + status: 5, + status4k: 1, + createdAt: '2023-02-06T19:38:45.000Z', + updatedAt: '2023-02-06T20:00:04.000Z', + lastSeasonChange: '2023-08-06T19:38:45.000Z', + mediaAddedAt: '2023-05-14T06:30:34.000Z', + serviceId: 0, + serviceId4k: null, + externalServiceId: 32, + externalServiceId4k: null, + externalServiceSlug: '000000000000', + externalServiceSlug4k: null, + ratingKey: null, + ratingKey4k: null, + jellyfinMediaId: '0000', + jellyfinMediaId4k: null, + mediaUrl: + 'http://your-jellyfin.local/web/index.html#!/details?id=mn8q2j4gq038g&context=home&serverId=jf83fj34gm340g', + serviceUrl: 'http://your-jellyfin.local/movie/0000', + }, + seasons: [], + modifiedBy: { + permissions: 2, + warnings: [], + id: 1, + email: 'example-user@homarr.dev', + plexUsername: null, + jellyfinUsername: 'example-user', + username: null, + recoveryLinkExpirationDate: null, + userType: 3, + plexId: null, + jellyfinUserId: '00000000000000000', + jellyfinDeviceId: '111111111111111111', + jellyfinAuthToken: '2222222222222222222', + plexToken: null, + avatar: '/os_logo_square.png', + movieQuotaLimit: null, + movieQuotaDays: null, + tvQuotaLimit: null, + tvQuotaDays: null, + createdAt: '2022-07-03T19:53:08.000Z', + updatedAt: '2022-07-03T19:53:08.000Z', + requestCount: 34, + displayName: 'Example User', + }, + requestedBy: { + permissions: 2, + warnings: [], + id: 1, + email: 'example-user@homarr.dev', + plexUsername: null, + jellyfinUsername: 'example-user', + username: null, + recoveryLinkExpirationDate: null, + userType: 3, + plexId: null, + jellyfinUserId: '00000000000000000', + jellyfinDeviceId: '111111111111111111', + jellyfinAuthToken: '2222222222222222222', + plexToken: null, + avatar: '/os_logo_square.png', + movieQuotaLimit: null, + movieQuotaDays: null, + tvQuotaLimit: null, + tvQuotaDays: null, + createdAt: '2022-07-03T19:53:08.000Z', + updatedAt: '2022-07-03T19:53:08.000Z', + requestCount: 34, + displayName: 'Example User', + }, + seasonCount: 0, + }, + ], + }); + } + + if (request.url === 'http://my-overseerr.local/api/v1/movie/99999999') { + return JSON.stringify({ + id: 0, + adult: false, + budget: 0, + genres: [ + { + id: 18, + name: 'Dashboards', + }, + ], + relatedVideos: [], + originalLanguage: 'jp', + originalTitle: 'Homarrrr Movie', + popularity: 9.352, + productionCompanies: [], + productionCountries: [], + releaseDate: '2023-12-08', + releases: { + results: [], + }, + revenue: 0, + spokenLanguages: [ + { + english_name: 'Japanese', + iso_639_1: 'jp', + name: '日本語', + }, + ], + status: 'Released', + title: 'Homarr Movie', + video: false, + voteAverage: 9.999, + voteCount: 0, + backdropPath: '/mhjq8jr0qgrjnghnh.jpg', + homepage: '', + imdbId: 'tt0000000', + overview: 'A very cool movie', + posterPath: '/hf4j0928gq543njgh8935nqh8.jpg', + runtime: 97, + tagline: '', + credits: {}, + collection: null, + externalIds: { + facebookId: null, + imdbId: null, + instagramId: null, + twitterId: null, + }, + watchProviders: [], + keywords: [], + }); + } + + return Promise.reject(new Error(`Bad url: ${request.url}`)); + }); + + // Act + await MediaRequestsRoute(req, res); + + // Assert + expect(res._getStatusCode()).toBe(200); + expect(res.finished).toBe(true); + expect(JSON.parse(res._getData())).toEqual([ + { + airDate: '2023-12-08', + backdropPath: 'https://image.tmdb.org/t/p/original//mhjq8jr0qgrjnghnh.jpg', + createdAt: '2023-04-06T19:38:45.000Z', + href: 'http://my-overseerr.local/movie/99999999', + id: 44, + name: 'Homarrrr Movie', + posterPath: + 'https://image.tmdb.org/t/p/w600_and_h900_bestv2//hf4j0928gq543njgh8935nqh8.jpg', + status: 2, + type: 'movie', userLink: 'http://my-overseerr.local/users/1', userName: 'Example User', userProfilePicture: 'http://my-overseerr.local//os_logo_square.png', diff --git a/src/pages/api/modules/media-requests/index.ts b/src/pages/api/modules/media-requests/index.ts index 5cd58c167..51205741a 100644 --- a/src/pages/api/modules/media-requests/index.ts +++ b/src/pages/api/modules/media-requests/index.ts @@ -51,7 +51,7 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => { type: item.type, name: genericItem.name, userName: item.requestedBy.displayName, - userProfilePicture: constructAvatarUrl(app, item), + userProfilePicture: constructAvatarUrl(appUrl, item), userLink: `${appUrl}/users/${item.requestedBy.id}`, airDate: genericItem.airDate, status: item.status, @@ -75,7 +75,7 @@ const Get = async (request: NextApiRequest, response: NextApiResponse) => { return response.status(200).json(mediaRequests); }; -const constructAvatarUrl = (app: ConfigAppType, item: OverseerrResponseItem) => { +const constructAvatarUrl = (appUrl: string, item: OverseerrResponseItem) => { const isAbsolute = item.requestedBy.avatar.startsWith('http://') || item.requestedBy.avatar.startsWith('https://'); @@ -83,7 +83,7 @@ const constructAvatarUrl = (app: ConfigAppType, item: OverseerrResponseItem) => return item.requestedBy.avatar; } - return `${app.url}/${item.requestedBy.avatar}`; + return `${appUrl}/${item.requestedBy.avatar}`; }; const retrieveDetailsForItem = async ( diff --git a/src/tools/server/sdk/adGuard/adGuard.schema.ts b/src/tools/server/sdk/adGuard/adGuard.schema.ts new file mode 100644 index 000000000..a57b595ec --- /dev/null +++ b/src/tools/server/sdk/adGuard/adGuard.schema.ts @@ -0,0 +1,41 @@ +import { z } from 'zod'; + +export const adGuardApiStatsResponseSchema = z.object({ + time_units: z.enum(['hours']), + top_queried_domains: z.array(z.record(z.string(), z.number())), + top_clients: z.array(z.record(z.string(), z.number())), + top_blocked_domains: z.array(z.record(z.string(), z.number())), + dns_queries: z.array(z.number()), + blocked_filtering: z.array(z.number()), + replaced_safebrowsing: z.array(z.number()), + replaced_parental: z.array(z.number()), + num_dns_queries: z.number().min(0), + num_blocked_filtering: z.number().min(0), + num_replaced_safebrowsing: z.number().min(0), + num_replaced_safesearch: z.number().min(0), + num_replaced_parental: z.number().min(0), + avg_processing_time: z.number().min(0).max(1), +}); + +export const adGuardApiStatusResponseSchema = z.object({ + version: z.string(), + language: z.string(), + dns_addresses: z.array(z.string()), + dns_port: z.number().positive(), + http_port: z.number().positive(), + protection_disabled_duration: z.number(), + protection_enabled: z.boolean(), + dhcp_available: z.boolean(), + running: z.boolean(), +}); + +export const adGuardApiFilteringStatusSchema = z.object({ + filters: z.array(z.object({ + url: z.string().url(), + name: z.string(), + last_updated: z.string().optional(), + id: z.number().nonnegative(), + rules_count: z.number().nonnegative(), + enabled: z.boolean(), + })), +}); diff --git a/src/tools/server/sdk/adGuard/adGuard.ts b/src/tools/server/sdk/adGuard/adGuard.ts new file mode 100644 index 000000000..e018a2e7c --- /dev/null +++ b/src/tools/server/sdk/adGuard/adGuard.ts @@ -0,0 +1,95 @@ +import { trimStringEnding } from '../../../shared/strings'; +import { + adGuardApiFilteringStatusSchema, + adGuardApiStatsResponseSchema, + adGuardApiStatusResponseSchema, +} from './adGuard.schema'; + +export class AdGuard { + private readonly baseHostName: string; + + constructor( + hostname: string, + private readonly username: string, + private readonly password: string + ) { + this.baseHostName = trimStringEnding(hostname, ['/#', '/']); + } + + async getStats(): Promise { + const response = await fetch(`${this.baseHostName}/control/stats`, { + headers: { + Authorization: `Basic ${this.getAuthorizationHeaderValue()}`, + }, + }); + + const data = await response.json(); + + return adGuardApiStatsResponseSchema.parseAsync(data); + } + + async getStatus() { + const response = await fetch(`${this.baseHostName}/control/status`, { + headers: { + Authorization: `Basic ${this.getAuthorizationHeaderValue()}`, + }, + }); + + const data = await response.json(); + + return adGuardApiStatusResponseSchema.parseAsync(data); + } + + async getCountFilteringDomains() { + const response = await fetch(`${this.baseHostName}/control/filtering/status`, { + headers: { + Authorization: `Basic ${this.getAuthorizationHeaderValue()}`, + }, + }); + + const data = await response.json(); + const schemaData = await adGuardApiFilteringStatusSchema.parseAsync(data); + + return schemaData.filters + .filter((filter) => filter.enabled) + .reduce((sum, filter) => filter.rules_count + sum, 0); + } + + async disable() { + await this.changeProtectionStatus(false); + } + async enable() { + await this.changeProtectionStatus(false); + } + + private async changeProtectionStatus(newStatus: boolean, duration = 0) { + await fetch(`${this.baseHostName}/control/protection`, { + method: 'POST', + body: JSON.stringify({ + enabled: newStatus, + duration, + }), + }); + } + + private getAuthorizationHeaderValue() { + return Buffer.from(`${this.username}:${this.password}`).toString('base64'); + } +} + +export type AdGuardStatsType = { + time_units: string; + top_queried_domains: { [key: string]: number }[]; + top_clients: { [key: string]: number }[]; + top_blocked_domains: { [key: string]: number }[]; + dns_queries: number[]; + blocked_filtering: number[]; + replaced_safebrowsing: number[]; + replaced_parental: number[]; + num_dns_queries: number; + num_blocked_filtering: number; + num_replaced_safebrowsing: number; + num_replaced_safesearch: number; + num_replaced_parental: number; + avg_processing_time: number; +}; diff --git a/src/tools/server/sdk/pihole/piHole.ts b/src/tools/server/sdk/pihole/piHole.ts index 38d24a2a1..09d7f9df4 100644 --- a/src/tools/server/sdk/pihole/piHole.ts +++ b/src/tools/server/sdk/pihole/piHole.ts @@ -1,10 +1,11 @@ +import { trimStringEnding } from '../../../shared/strings'; import { PiHoleApiStatusChangeResponse, PiHoleApiSummaryResponse } from './piHole.type'; export class PiHoleClient { private readonly baseHostName: string; constructor(hostname: string, private readonly apiToken: string) { - this.baseHostName = this.trimStringEnding(hostname, ['/admin/index.php', '/admin', '/']); + this.baseHostName = trimStringEnding(hostname, ['/admin/index.php', '/admin', '/']); } async getSummary() { @@ -60,15 +61,4 @@ export class PiHoleClient { return json as PiHoleApiStatusChangeResponse; } - - private trimStringEnding(original: string, toTrimIfExists: string[]) { - for (let i = 0; i < toTrimIfExists.length; i += 1) { - if (!original.endsWith(toTrimIfExists[i])) { - continue; - } - return original.substring(0, original.indexOf(toTrimIfExists[i])); - } - - return original; - } } diff --git a/src/tools/shared/strings.ts b/src/tools/shared/strings.ts new file mode 100644 index 000000000..a3c70dc4c --- /dev/null +++ b/src/tools/shared/strings.ts @@ -0,0 +1,10 @@ +export const trimStringEnding = (original: string, toTrimIfExists: string[]) => { + for (let i = 0; i < toTrimIfExists.length; i += 1) { + if (!original.endsWith(toTrimIfExists[i])) { + continue; + } + return original.substring(0, original.indexOf(toTrimIfExists[i])); + } + + return original; +}; diff --git a/src/types/app.ts b/src/types/app.ts index 5cdd16212..847ef8353 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -45,7 +45,8 @@ export type IntegrationType = | 'plex' | 'jellyfin' | 'nzbGet' - | 'pihole'; + | 'pihole' + | 'adGuardHome'; export type AppIntegrationType = { type: IntegrationType | null; @@ -86,6 +87,7 @@ export const integrationFieldProperties: { jellyfin: ['username', 'password'], plex: ['apiKey'], pihole: ['password'], + adGuardHome: ['username', 'password'], }; export type IntegrationFieldDefinitionType = { diff --git a/src/widgets/dnshole/DnsHoleSummary.tsx b/src/widgets/dnshole/DnsHoleSummary.tsx index ebd025a8a..ae382ffab 100644 --- a/src/widgets/dnshole/DnsHoleSummary.tsx +++ b/src/widgets/dnshole/DnsHoleSummary.tsx @@ -136,7 +136,7 @@ function DnsHoleSummaryWidgetTile({ widget }: DnsHoleSummaryWidgetProps) {
- {formatNumber(data.dnsQueriesToday, 0)} + {formatNumber(data.dnsQueriesToday, 3)} {t('card.metrics.queriesToday')} diff --git a/yarn.lock b/yarn.lock index c868c5c23..c8318e6cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,22 +1561,6 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:^1.33.0": - version: 1.33.0 - resolution: "@playwright/test@npm:1.33.0" - dependencies: - "@types/node": "*" - fsevents: 2.3.2 - playwright-core: 1.33.0 - dependenciesMeta: - fsevents: - optional: true - bin: - playwright: cli.js - checksum: cec3215fc92c1cb9f5bfba357ea1cbe97b54979ab82f9d34a2287b1687cda5e0966b8ea7290dcd35416e18668e56d5781b6b8c4cec64baf12f3ae8dde0f68f5e - languageName: node - linkType: hard - "@polka/url@npm:^1.0.0-next.20": version: 1.0.0-next.21 resolution: "@polka/url@npm:1.0.0-next.21" @@ -2060,7 +2044,7 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*, @types/chai@npm:^4.3.4": +"@types/chai@npm:*, @types/chai@npm:^4.3.5": version: 4.3.5 resolution: "@types/chai@npm:4.3.5" checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431 @@ -2560,34 +2544,46 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:0.29.8": - version: 0.29.8 - resolution: "@vitest/expect@npm:0.29.8" +"@vitest/expect@npm:0.31.1": + version: 0.31.1 + resolution: "@vitest/expect@npm:0.31.1" dependencies: - "@vitest/spy": 0.29.8 - "@vitest/utils": 0.29.8 + "@vitest/spy": 0.31.1 + "@vitest/utils": 0.31.1 chai: ^4.3.7 - checksum: a80f9c352a979eb46690be2ea54b5ca391d3575b4053be80c1359325fb0cea913d6217f48d54e64ff5dda3b15bd7a6873a5f8128e8c098f7ebad1365d4065c5e + checksum: 0d1e135ae753d913231eae830da00ee42afca53d354898fb43f97e82398dcf17298c02e9989dd6b19b9b2909989248ef76d203d63f6af6f9159dc96959ea654b languageName: node linkType: hard -"@vitest/runner@npm:0.29.8": - version: 0.29.8 - resolution: "@vitest/runner@npm:0.29.8" +"@vitest/runner@npm:0.31.1": + version: 0.31.1 + resolution: "@vitest/runner@npm:0.31.1" dependencies: - "@vitest/utils": 0.29.8 + "@vitest/utils": 0.31.1 + concordance: ^5.0.4 p-limit: ^4.0.0 pathe: ^1.1.0 - checksum: 8305370ff6c3fc6aea7189bd138ee4ff0e040a959c0fe6ab64bcb9e70ae5bf836b8dc058b1de288aa75c9d1cd648e5f112e7cd5691c03b7a1d32466d8bfc71a9 + checksum: cc8702e21b799d5e941409cb2afe6d0e576b4f3ac99df4a1393a8cd11b57f6b0b06e756cc24e2739812d095fbfd0824e22e861dbd6a71769ca387d485ade6fb5 languageName: node linkType: hard -"@vitest/spy@npm:0.29.8": - version: 0.29.8 - resolution: "@vitest/spy@npm:0.29.8" +"@vitest/snapshot@npm:0.31.1": + version: 0.31.1 + resolution: "@vitest/snapshot@npm:0.31.1" dependencies: - tinyspy: ^1.0.2 - checksum: 7b1607b696275bf94a497e92d7d10c466b9b3d08726bbedb3735bdf57f003763a9516e328af22746829526ce573f87eb6119ab64ce7db95794b2d220aa53b607 + magic-string: ^0.30.0 + pathe: ^1.1.0 + pretty-format: ^27.5.1 + checksum: de05fa9136864f26f0804baf3ae8068f67de28015f29047329c84e67fb33be7305c9e52661b016e834d30f4081c136b3b6d8d4054c024a5d52b22a7f90fc4be0 + languageName: node + linkType: hard + +"@vitest/spy@npm:0.31.1": + version: 0.31.1 + resolution: "@vitest/spy@npm:0.31.1" + dependencies: + tinyspy: ^2.1.0 + checksum: 8b06cf25fcc028c16106ec82f4ceb84d6dfa04d06f651bca4738ce2b99796d1fc4e0c10319767240755eff8ede2bff9d31d5a901fe92828d319c65001581137b languageName: node linkType: hard @@ -2604,15 +2600,14 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:0.29.8": - version: 0.29.8 - resolution: "@vitest/utils@npm:0.29.8" +"@vitest/utils@npm:0.31.1": + version: 0.31.1 + resolution: "@vitest/utils@npm:0.31.1" dependencies: - cli-truncate: ^3.1.0 - diff: ^5.1.0 + concordance: ^5.0.4 loupe: ^2.3.6 pretty-format: ^27.5.1 - checksum: fa18cccb6ab5295e43a1a43b9c022f070646a893adb0561c50b3e0c39f05ea74cbf379aef22ef485ea9acbf2bb8f0a224d457fd4f16b9e1bf509c13052c7f08b + checksum: 58016c185455e3814632cb77e37368c846bde5e342f8b4a66fa229bde64f455ca39abebc9c12e2483696ee38bc17b3c4300379f7a3b18d1087f24f474448a8d8 languageName: node linkType: hard @@ -2665,7 +2660,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2": +"acorn@npm:^8.0.4, acorn@npm:^8.4.1, acorn@npm:^8.8.0, acorn@npm:^8.8.2": version: 8.8.2 resolution: "acorn@npm:8.8.2" bin: @@ -2777,13 +2772,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.0.1": - version: 6.0.1 - resolution: "ansi-regex@npm:6.0.1" - checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 - languageName: node - linkType: hard - "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -2809,13 +2797,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0": - version: 6.2.1 - resolution: "ansi-styles@npm:6.2.1" - checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 - languageName: node - linkType: hard - "ansicolors@npm:~0.3.2": version: 0.3.2 resolution: "ansicolors@npm:0.3.2" @@ -3132,6 +3113,13 @@ __metadata: languageName: node linkType: hard +"blueimp-md5@npm:^2.10.0": + version: 2.19.0 + resolution: "blueimp-md5@npm:2.19.0" + checksum: 28095dcbd2c67152a2938006e8d7c74c3406ba6556071298f872505432feb2c13241b0476644160ee0a5220383ba94cb8ccdac0053b51f68d168728f9c382530 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -3532,16 +3520,6 @@ checkly@latest: languageName: node linkType: hard -"cli-truncate@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-truncate@npm:3.1.0" - dependencies: - slice-ansi: ^5.0.0 - string-width: ^5.0.0 - checksum: c3243e41974445691c63f8b405df1d5a24049dc33d324fe448dc572e561a7b772ae982692900b1a5960901cc4fc7def25a629b9c69a4208ee89d12ab3332617a - languageName: node - linkType: hard - "cli-width@npm:^3.0.0": version: 3.0.0 resolution: "cli-width@npm:3.0.0" @@ -3683,6 +3661,22 @@ checkly@latest: languageName: node linkType: hard +"concordance@npm:^5.0.4": + version: 5.0.4 + resolution: "concordance@npm:5.0.4" + dependencies: + date-time: ^3.1.0 + esutils: ^2.0.3 + fast-diff: ^1.2.0 + js-string-escape: ^1.0.1 + lodash: ^4.17.15 + md5-hex: ^3.0.1 + semver: ^7.3.2 + well-known-symbols: ^2.0.0 + checksum: 749153ba711492feb7c3d2f5bb04c107157440b3e39509bd5dd19ee7b3ac751d1e4cd75796d9f702e0a713312dbc661421c68aa4a2c34d5f6d91f47e3a1c64a6 + languageName: node + linkType: hard + "conf@npm:10.2.0": version: 10.2.0 resolution: "conf@npm:10.2.0" @@ -3997,6 +3991,15 @@ checkly@latest: languageName: node linkType: hard +"date-time@npm:^3.1.0": + version: 3.1.0 + resolution: "date-time@npm:3.1.0" + dependencies: + time-zone: ^1.0.0 + checksum: f9cfcd1b15dfeabab15c0b9d18eb9e4e2d9d4371713564178d46a8f91ad577a290b5178b80050718d02d9c0cf646f8a875011e12d1ed05871e9f72c72c8a8fe6 + languageName: node + linkType: hard + "dayjs@npm:^1.11.7": version: 1.11.7 resolution: "dayjs@npm:1.11.7" @@ -4174,13 +4177,6 @@ checkly@latest: languageName: node linkType: hard -"diff@npm:^5.1.0": - version: 5.1.0 - resolution: "diff@npm:5.1.0" - checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90 - languageName: node - linkType: hard - "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -4337,13 +4333,6 @@ checkly@latest: languageName: node linkType: hard -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed - languageName: node - linkType: hard - "ejs@npm:^3.1.6, ejs@npm:^3.1.8": version: 3.1.9 resolution: "ejs@npm:3.1.9" @@ -4971,7 +4960,7 @@ checkly@latest: languageName: node linkType: hard -"esutils@npm:^2.0.2": +"esutils@npm:^2.0.2, esutils@npm:^2.0.3": version: 2.0.3 resolution: "esutils@npm:2.0.3" checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87 @@ -5016,6 +5005,13 @@ checkly@latest: languageName: node linkType: hard +"fast-diff@npm:^1.2.0": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: d22d371b994fdc8cce9ff510d7b8dc4da70ac327bcba20df607dd5b9cae9f908f4d1028f5fe467650f058d1e7270235ae0b8230809a262b4df587a3b3aa216c3 + languageName: node + linkType: hard + "fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" @@ -5278,7 +5274,7 @@ checkly@latest: languageName: node linkType: hard -"fsevents@npm:2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:~2.3.2": version: 2.3.2 resolution: "fsevents@npm:2.3.2" dependencies: @@ -5288,7 +5284,7 @@ checkly@latest: languageName: node linkType: hard -"fsevents@patch:fsevents@2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": +"fsevents@patch:fsevents@~2.3.2#~builtin": version: 2.3.2 resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" dependencies: @@ -5745,7 +5741,6 @@ checkly@latest: "@next/eslint-plugin-next": ^13.0.0 "@nivo/core": ^0.80.0 "@nivo/line": ^0.80.0 - "@playwright/test": ^1.33.0 "@react-native-async-storage/async-storage": ^1.18.1 "@tabler/icons-react": ^2.18.0 "@tanstack/query-async-storage-persister": ^4.27.1 @@ -5806,7 +5801,7 @@ checkly@latest: typescript: ^5.0.4 uuid: ^8.3.2 video.js: ^8.0.3 - vitest: ^0.29.3 + vitest: ^0.31.1 vitest-fetch-mock: ^0.2.2 xml-js: ^1.6.11 yarn: ^1.22.19 @@ -6245,13 +6240,6 @@ checkly@latest: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^4.0.0": - version: 4.0.0 - resolution: "is-fullwidth-code-point@npm:4.0.0" - checksum: 8ae89bf5057bdf4f57b346fb6c55e9c3dd2549983d54191d722d5c739397a903012cc41a04ee3403fd872e811243ef91a7c5196da7b5841dc6b6aae31a264a8d - languageName: node - linkType: hard - "is-function@npm:^1.0.1": version: 1.0.2 resolution: "is-function@npm:1.0.2" @@ -6606,6 +6594,13 @@ checkly@latest: languageName: node linkType: hard +"js-string-escape@npm:^1.0.1": + version: 1.0.1 + resolution: "js-string-escape@npm:1.0.1" + checksum: f11e0991bf57e0c183b55c547acec85bd2445f043efc9ea5aa68b41bd2a3e7d3ce94636cb233ae0d84064ba4c1a505d32e969813c5b13f81e7d4be12c59256fe + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -6827,7 +6822,7 @@ checkly@latest: languageName: node linkType: hard -"local-pkg@npm:^0.4.2": +"local-pkg@npm:^0.4.3": version: 0.4.3 resolution: "local-pkg@npm:0.4.3" checksum: 7825aca531dd6afa3a3712a0208697aa4a5cd009065f32e3fb732aafcc42ed11f277b5ac67229222e96f4def55197171cdf3d5522d0381b489d2e5547b407d55 @@ -6972,6 +6967,15 @@ checkly@latest: languageName: node linkType: hard +"magic-string@npm:^0.30.0": + version: 0.30.0 + resolution: "magic-string@npm:0.30.0" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.13 + checksum: 7bdf22e27334d8a393858a16f5f840af63a7c05848c000fd714da5aa5eefa09a1bc01d8469362f25cc5c4a14ec01b46557b7fff8751365522acddf21e57c488d + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -7012,6 +7016,15 @@ checkly@latest: languageName: node linkType: hard +"md5-hex@npm:^3.0.1": + version: 3.0.1 + resolution: "md5-hex@npm:3.0.1" + dependencies: + blueimp-md5: ^2.10.0 + checksum: 6799a19e8bdd3e0c2861b94c1d4d858a89220488d7885c1fa236797e367d0c2e5f2b789e05309307083503f85be3603a9686a5915568a473137d6b4117419cc2 + languageName: node + linkType: hard + "media-typer@npm:0.3.0": version: 0.3.0 resolution: "media-typer@npm:0.3.0" @@ -7253,7 +7266,7 @@ checkly@latest: languageName: node linkType: hard -"mlly@npm:^1.1.0, mlly@npm:^1.2.0": +"mlly@npm:^1.2.0": version: 1.2.1 resolution: "mlly@npm:1.2.1" dependencies: @@ -8088,15 +8101,6 @@ checkly@latest: languageName: node linkType: hard -"playwright-core@npm:1.33.0": - version: 1.33.0 - resolution: "playwright-core@npm:1.33.0" - bin: - playwright: cli.js - checksum: 5fb7bda06a8b73b56b85b5a0b8f711211dde57a375d9379289e22239b2de879c6d93c8fdc9ba44b932bf100914ab1ca1a55697ad88440fdd0a39101fc020b77f - languageName: node - linkType: hard - "postcss@npm:8.4.14": version: 8.4.14 resolution: "postcss@npm:8.4.14" @@ -8857,7 +8861,7 @@ checkly@latest: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8": +"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8": version: 7.5.1 resolution: "semver@npm:7.5.1" dependencies: @@ -8961,16 +8965,6 @@ checkly@latest: languageName: node linkType: hard -"slice-ansi@npm:^5.0.0": - version: 5.0.0 - resolution: "slice-ansi@npm:5.0.0" - dependencies: - ansi-styles: ^6.0.0 - is-fullwidth-code-point: ^4.0.0 - checksum: 7e600a2a55e333a21ef5214b987c8358fe28bfb03c2867ff2cbf919d62143d1812ac27b4297a077fdaf27a03da3678e49551c93e35f9498a3d90221908a1180e - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -9013,13 +9007,6 @@ checkly@latest: languageName: node linkType: hard -"source-map@npm:^0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 - languageName: node - linkType: hard - "split-ca@npm:^1.0.1": version: 1.0.1 resolution: "split-ca@npm:1.0.1" @@ -9085,7 +9072,7 @@ checkly@latest: languageName: node linkType: hard -"std-env@npm:^3.3.1": +"std-env@npm:^3.3.1, std-env@npm:^3.3.2": version: 3.3.3 resolution: "std-env@npm:3.3.3" checksum: 6665f6d8bd63aae432d3eb9abbd7322847ad0d902603e6dce1e8051b4f42ceeb4f7f96a4faf70bb05ce65ceee2dc982502b701575c8a58b1bfad29f3dbb19f81 @@ -9126,17 +9113,6 @@ checkly@latest: languageName: node linkType: hard -"string-width@npm:^5.0.0": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: ^0.2.0 - emoji-regex: ^9.2.2 - strip-ansi: ^7.0.1 - checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 - languageName: node - linkType: hard - "string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -9220,15 +9196,6 @@ checkly@latest: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": - version: 7.0.1 - resolution: "strip-ansi@npm:7.0.1" - dependencies: - ansi-regex: ^6.0.1 - checksum: 257f78fa433520e7f9897722731d78599cb3fce29ff26a20a5e12ba4957463b50a01136f37c43707f4951817a75e90820174853d6ccc240997adc5df8f966039 - languageName: node - linkType: hard - "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -9252,7 +9219,7 @@ checkly@latest: languageName: node linkType: hard -"strip-literal@npm:^1.0.0": +"strip-literal@npm:^1.0.1": version: 1.0.1 resolution: "strip-literal@npm:1.0.1" dependencies: @@ -9436,24 +9403,31 @@ checkly@latest: languageName: node linkType: hard -"tinybench@npm:^2.3.1": +"time-zone@npm:^1.0.0": + version: 1.0.0 + resolution: "time-zone@npm:1.0.0" + checksum: e46f5a69b8c236dcd8e91e29d40d4e7a3495ed4f59888c3f84ce1d9678e20461421a6ba41233509d47dd94bc18f1a4377764838b21b584663f942b3426dcbce8 + languageName: node + linkType: hard + +"tinybench@npm:^2.5.0": version: 2.5.0 resolution: "tinybench@npm:2.5.0" checksum: 284bb9428f197ec8b869c543181315e65e41ccfdad3c4b6c916bb1fdae1b5c6785661b0d90cf135b48d833b03cb84dc5357b2d33ec65a1f5971fae0ab2023821 languageName: node linkType: hard -"tinypool@npm:^0.4.0": - version: 0.4.0 - resolution: "tinypool@npm:0.4.0" - checksum: 8abcac9e784793499f1eeeace8290c026454b9d7338c74029ce6a821643bab8dcab7caeb4051e39006baf681d6a62d57c3319e9c0f6e2317a45ab0fdbd76ee26 +"tinypool@npm:^0.5.0": + version: 0.5.0 + resolution: "tinypool@npm:0.5.0" + checksum: 4e0dfd8f28666d541c1d92304222edc4613f05d74fe2243c8520d466a2cc6596011a7072c1c41a7de7522351b82fda07e8038198e8f43673d8d69401c5903f8c languageName: node linkType: hard -"tinyspy@npm:^1.0.2": - version: 1.1.1 - resolution: "tinyspy@npm:1.1.1" - checksum: 4ea908fdfddb92044c4454193ec543f5980ced0bd25c5b3d240a94c1511e47e765ad39cd13ae6d3370fb730f62038eedc357f55e4e239416e126bc418f0eee79 +"tinyspy@npm:^2.1.0": + version: 2.1.0 + resolution: "tinyspy@npm:2.1.0" + checksum: cb83c1f74a79dd5934018bad94f60a304a29d98a2d909ea45fc367f7b80b21b0a7d8135a2ce588deb2b3ba56c7c607258b2a03e6001d89e4d564f9a95cc6a81f languageName: node linkType: hard @@ -10020,19 +9994,19 @@ turbo@latest: languageName: node linkType: hard -"vite-node@npm:0.29.8": - version: 0.29.8 - resolution: "vite-node@npm:0.29.8" +"vite-node@npm:0.31.1": + version: 0.31.1 + resolution: "vite-node@npm:0.31.1" dependencies: cac: ^6.7.14 debug: ^4.3.4 - mlly: ^1.1.0 + mlly: ^1.2.0 pathe: ^1.1.0 picocolors: ^1.0.0 vite: ^3.0.0 || ^4.0.0 bin: vite-node: vite-node.mjs - checksum: b0981d4d63b1f373579eb9da69ca5af9123bf27c81ac246c541cdecf879ef4ef542e0b521cb6ceaafd5ead2cc3d243105d1fb8bf076953d42a6b2203607ce928 + checksum: f70ffa3f6dcb4937cdc99f59bf360d42de83c556ba9a19eb1c3504ef20db4c1d1afa644d9a8e63240e851c0c95773b64c526bdb3eb4794b5e941ddcd57124aa9 languageName: node linkType: hard @@ -10084,33 +10058,34 @@ turbo@latest: languageName: node linkType: hard -"vitest@npm:^0.29.3": - version: 0.29.8 - resolution: "vitest@npm:0.29.8" +"vitest@npm:^0.31.1": + version: 0.31.1 + resolution: "vitest@npm:0.31.1" dependencies: - "@types/chai": ^4.3.4 + "@types/chai": ^4.3.5 "@types/chai-subset": ^1.3.3 "@types/node": "*" - "@vitest/expect": 0.29.8 - "@vitest/runner": 0.29.8 - "@vitest/spy": 0.29.8 - "@vitest/utils": 0.29.8 - acorn: ^8.8.1 + "@vitest/expect": 0.31.1 + "@vitest/runner": 0.31.1 + "@vitest/snapshot": 0.31.1 + "@vitest/spy": 0.31.1 + "@vitest/utils": 0.31.1 + acorn: ^8.8.2 acorn-walk: ^8.2.0 cac: ^6.7.14 chai: ^4.3.7 + concordance: ^5.0.4 debug: ^4.3.4 - local-pkg: ^0.4.2 + local-pkg: ^0.4.3 + magic-string: ^0.30.0 pathe: ^1.1.0 picocolors: ^1.0.0 - source-map: ^0.6.1 - std-env: ^3.3.1 - strip-literal: ^1.0.0 - tinybench: ^2.3.1 - tinypool: ^0.4.0 - tinyspy: ^1.0.2 + std-env: ^3.3.2 + strip-literal: ^1.0.1 + tinybench: ^2.5.0 + tinypool: ^0.5.0 vite: ^3.0.0 || ^4.0.0 - vite-node: 0.29.8 + vite-node: 0.31.1 why-is-node-running: ^2.2.2 peerDependencies: "@edge-runtime/vm": "*" @@ -10140,7 +10115,7 @@ turbo@latest: optional: true bin: vitest: vitest.mjs - checksum: 203e33bf093fdb99a6832c905a6c78175bb15313e06e1dcfbeb010a0e3efb8ff0aba4d317efedb4de76bd0086691bbd2c4bc7d6631f60fb1634b96832cba144f + checksum: b3f64a36102edc5b8594c085da648c838c0d275c620bd3b780624f936903b9c06579d6ef137fe9859e468f16deb8f154a50f009093119f9adb8b60ff1b7597ee languageName: node linkType: hard @@ -10200,6 +10175,13 @@ turbo@latest: languageName: node linkType: hard +"well-known-symbols@npm:^2.0.0": + version: 2.0.0 + resolution: "well-known-symbols@npm:2.0.0" + checksum: 4f54bbc3012371cb4d228f436891b8e7536d34ac61a57541890257e96788608e096231e0121ac24d08ef2f908b3eb2dc0adba35023eaeb2a7df655da91415402 + languageName: node + linkType: hard + "whatwg-encoding@npm:^2.0.0": version: 2.0.0 resolution: "whatwg-encoding@npm:2.0.0"