From 3c99809e8e6f043f60fcce81f7f3b679373d312a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 4 Mar 2026 23:24:05 +0000 Subject: [PATCH 01/16] rix reveresed checks --- client/src/Components/common/charts/HeatmapResponseTime.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/Components/common/charts/HeatmapResponseTime.tsx b/client/src/Components/common/charts/HeatmapResponseTime.tsx index 8178ffb92..f2aed0b7a 100644 --- a/client/src/Components/common/charts/HeatmapResponseTime.tsx +++ b/client/src/Components/common/charts/HeatmapResponseTime.tsx @@ -32,7 +32,7 @@ export const HeatmapResponseTime = ({ if (!checks || checks.length === 0) return null; - const latestChecks = checks.slice(-25).reverse(); + const latestChecks = checks.slice(-25); let data: Array; if (latestChecks.length !== 25) { From 82175f166feedf607c65ccc8ea8f444e86104752 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 4 Mar 2026 23:24:48 +0000 Subject: [PATCH 02/16] pingProvider, httpProvider --- server/src/config/services.ts | 12 ++- .../repositories/checks/IChecksRepository.ts | 11 ++- .../checks/MongoChecksRepistory.ts | 2 +- server/src/service/business/checkService.ts | 11 ++- .../infrastructure/network/AdvancedMatcher.ts | 45 +++++++++ .../infrastructure/network/HttpProvider.ts | 94 +++++++++++++++++++ .../infrastructure/network/IStatusProvider.ts | 8 ++ .../infrastructure/network/PingProvider.ts | 64 +++++++++++++ .../service/infrastructure/network/utils.ts | 85 +++++++++++++++++ .../service/infrastructure/networkService.ts | 66 ++++--------- server/src/types/network.ts | 4 +- 11 files changed, 345 insertions(+), 57 deletions(-) create mode 100644 server/src/service/infrastructure/network/AdvancedMatcher.ts create mode 100644 server/src/service/infrastructure/network/HttpProvider.ts create mode 100644 server/src/service/infrastructure/network/IStatusProvider.ts create mode 100644 server/src/service/infrastructure/network/PingProvider.ts create mode 100644 server/src/service/infrastructure/network/utils.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 56aef2f19..485297fe0 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -75,6 +75,9 @@ import { } from "@/repositories/index.js"; import { ILogger } from "@/utils/logger.js"; import { EnvConfig } from "@/service/system/settingsService.js"; +import { PingProvider } from "@/service/infrastructure/network/PingProvider.js"; +import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; +import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; export type InitializedServices = { settingsService: any; @@ -144,6 +147,11 @@ export const initializeServices = async ({ const teamsRepository = new MongoTeamsRepository(); const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository(); + // Providers + + const pingProvider = new PingProvider(ping); + const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath)); + const networkService = new NetworkService( axios, got, @@ -158,7 +166,9 @@ export const initializeServices = async ({ net, settingsService, grpc, - protoLoader + protoLoader, + pingProvider, + httpProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/repositories/checks/IChecksRepository.ts b/server/src/repositories/checks/IChecksRepository.ts index 31aac0ec9..c89e5d5fc 100644 --- a/server/src/repositories/checks/IChecksRepository.ts +++ b/server/src/repositories/checks/IChecksRepository.ts @@ -20,12 +20,19 @@ export interface IChecksRepository { monitorId: string, sortOrder: string, dateRange: string, - filter: string, + filter: string | undefined, page: number, rowsPerPage: number, status: boolean | undefined ): Promise; - findByTeamId(sortOrder: string, dateRange: string, filter: string, page: number, rowsPerPage: number, teamId: string): Promise; + findByTeamId( + sortOrder: string, + dateRange: string, + filter: string | undefined, + page: number, + rowsPerPage: number, + teamId: string + ): Promise; findLatestByMonitorIds(monitorIds: string[], options?: { limitPerMonitor?: number }): Promise; findByDateRangeAndMonitorId( monitorId: string, diff --git a/server/src/repositories/checks/MongoChecksRepistory.ts b/server/src/repositories/checks/MongoChecksRepistory.ts index 66e7f43f9..b019a0430 100644 --- a/server/src/repositories/checks/MongoChecksRepistory.ts +++ b/server/src/repositories/checks/MongoChecksRepistory.ts @@ -231,7 +231,7 @@ class MongoChecksRepository implements IChecksRepository { monitorId: string, sortOrder: string, dateRange: string, - filter: string, + filter: string | undefined, page: number, rowsPerPage: number, status: boolean | undefined diff --git a/server/src/service/business/checkService.ts b/server/src/service/business/checkService.ts index 2d2bf0e01..abeb577ed 100644 --- a/server/src/service/business/checkService.ts +++ b/server/src/service/business/checkService.ts @@ -124,8 +124,13 @@ class CheckService implements ICheckService { await this.monitorsRepository.findById(monitorId, teamId); const { sortOrder, dateRange, filter, page, rowsPerPage, status } = query; - if (!sortOrder || !dateRange || !filter || !page || !rowsPerPage) { - throw new AppError({ message: "Missing required query parameters", service: SERVICE_NAME, method: "getChecksByMonitor", status: 400 }); + if (!sortOrder || !dateRange || !page || !rowsPerPage) { + throw new AppError({ + message: `Missing required query parameters sortOrder: ${sortOrder}, dateRange: ${dateRange}, filter: ${filter}, page: ${page}, rowsPerPage: ${rowsPerPage}`, + service: SERVICE_NAME, + method: "getChecksByMonitor", + status: 400, + }); } const parsedStatus = typeof status === "undefined" ? status : ParseBoolean(status); const parsedPage = page ? parseInt(page) : 0; @@ -138,7 +143,7 @@ class CheckService implements ICheckService { getChecksByTeam = async ({ teamId, query }: { teamId: string; query: Record }) => { const { sortOrder, dateRange, filter, page, rowsPerPage } = query; - if (!sortOrder || !dateRange || !filter || !page || !rowsPerPage) { + if (!sortOrder || !dateRange || !page || !rowsPerPage) { throw new AppError({ message: "Missing required query parameters", service: SERVICE_NAME, method: "getChecksByTeam", status: 400 }); } diff --git a/server/src/service/infrastructure/network/AdvancedMatcher.ts b/server/src/service/infrastructure/network/AdvancedMatcher.ts new file mode 100644 index 000000000..57bf7022f --- /dev/null +++ b/server/src/service/infrastructure/network/AdvancedMatcher.ts @@ -0,0 +1,45 @@ +import { Monitor } from "@/types/monitor.js"; +import jmespath from "jmespath"; +type JmesPath = typeof jmespath; + +export class AdvancedMatcher { + constructor(private jmespath: JmesPath) {} + + private compare(actual: unknown, expected: string, method?: string): boolean { + if (method === "equal") return String(actual) === expected; + if (method === "include") return String(actual).includes(expected); + if (method === "regex") return new RegExp(expected).test(String(actual)); + return String(actual) === expected; // Default + } + + validate(payload: T, monitor: Monitor): { ok: boolean; message: string; extracted?: any } { + const { useAdvancedMatching, jsonPath, matchMethod, expectedValue } = monitor; + if (!useAdvancedMatching) return { ok: true, message: "Success" }; + + let dataToValidate = payload; + + if (jsonPath) { + try { + dataToValidate = this.jmespath.search(payload, jsonPath); + } catch { + return { ok: false, message: "Error evaluating JSON path" }; + } + } + + if (expectedValue) { + const ok = this.compare(dataToValidate, expectedValue, matchMethod); + return { + ok, + message: ok ? "Success" : "Expected value did not match", + extracted: dataToValidate, + }; + } + + const isFalsey = dataToValidate === false || dataToValidate === "false" || dataToValidate === undefined || dataToValidate === null; + return { + ok: !isFalsey, + message: !isFalsey ? "Success" : "Extracted value is falsey", + extracted: dataToValidate, + }; + } +} diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts new file mode 100644 index 000000000..84f290b16 --- /dev/null +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -0,0 +1,94 @@ +import { type Got, HTTPError, RequestError } from "got"; +import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { HttpStatusPayload } from "@/types/network.js"; +import { MonitorStatusResponse } from "@/types/network.js"; +import { Agent as HttpsAgent } from "https"; +import { Monitor } from "@/types/monitor.js"; +import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; + +export class HttpProvider implements IStatusProvider { + readonly type = "http"; + + constructor( + private got: Got, + private advancedMatcher: AdvancedMatcher + ) {} + + supports(type: string) { + return type === "http"; + } + + private handleHttpError(error: unknown, monitor: Monitor): MonitorStatusResponse { + if (error instanceof HTTPError || error instanceof RequestError) { + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: error.response?.statusCode ?? NETWORK_ERROR, + message: error.message, + responseTime: error.timings?.phases?.total ?? 0, + timings: error.timings, + payload: null as T, + }; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: NETWORK_ERROR, + message: error instanceof Error ? error.message : String(error), + responseTime: 0, + payload: null as T, + }; + } + + async handle(monitor: Monitor): Promise> { + const { url, secret, ignoreTlsErrors } = monitor; + const options: Record = { + headers: monitor.secret ? { Authorization: `Bearer ${secret}` } : undefined, + }; + + options.agent = { + https: new HttpsAgent({ rejectUnauthorized: !ignoreTlsErrors }), + }; + + try { + const response = await this.got(url, options); + let payload: T; + const isJson = response.headers["content-type"]?.includes("application/json"); + + if (isJson) { + try { + payload = JSON.parse(response.body) as T; + } catch { + payload = response.body as unknown as T; + } + } else { + payload = response.body as unknown as T; + } + + const matchResult = this.advancedMatcher.validate(payload, monitor); + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: response.ok && matchResult.ok, + code: response.statusCode, + message: matchResult.ok ? (response.statusMessage ?? "OK") : matchResult.message, + responseTime: response.timings.phases.total ?? 0, + timings: response.timings, + payload, + extracted: matchResult.extracted, + jsonPath: monitor.jsonPath, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + }; + } catch (error: unknown) { + return this.handleHttpError(error, monitor); + } + } +} diff --git a/server/src/service/infrastructure/network/IStatusProvider.ts b/server/src/service/infrastructure/network/IStatusProvider.ts new file mode 100644 index 000000000..bde6a2db0 --- /dev/null +++ b/server/src/service/infrastructure/network/IStatusProvider.ts @@ -0,0 +1,8 @@ +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { MonitorStatusResponse } from "@/types/network.js"; + +export interface IStatusProvider { + type: string; + supports: (type: MonitorType) => boolean; + handle(monitor: Monitor): Promise>; +} diff --git a/server/src/service/infrastructure/network/PingProvider.ts b/server/src/service/infrastructure/network/PingProvider.ts new file mode 100644 index 000000000..2749584f5 --- /dev/null +++ b/server/src/service/infrastructure/network/PingProvider.ts @@ -0,0 +1,64 @@ +import { PingStatusPayload } from "@/types/network.js"; +import { IStatusProvider } from "./IStatusProvider.js"; +import { MonitorType, Monitor } from "@/types/monitor.js"; +import { MonitorStatusResponse } from "@/types/network.js"; +import { AppError } from "@/utils/AppError.js"; +import ping from "ping"; +import { buildStatusResponse, timeRequest } from "@/service/infrastructure/network/utils.js"; +const SERVICE_NAME = "PingProvider"; + +type Ping = typeof ping; + +export class PingProvider implements IStatusProvider { + readonly type = "ping"; + + constructor(private ping: Ping) {} + + supports(type: MonitorType): boolean { + return type === "ping"; + } + + private sanitizeHost(url: string): string { + return url + .replace(/^https?:\/\//, "") + .replace(/\/.*$/, "") + .replace(/:.*/, ""); + } + + async handle(monitor: Monitor): Promise> { + try { + if (!monitor.url) { + throw new Error("URL is required for ping monitor"); + } + + const sanitizedHost = this.sanitizeHost(monitor.url); + const { response, error } = await timeRequest(() => this.ping.promise.probe(sanitizedHost)); + const safeTime = typeof response?.time === "number" ? response.time : parseFloat(String(response?.time)) || 0; + if (error) { + throw error; + } + + if (!response) { + throw new Error(`No response from ping for host: ${sanitizedHost}`); + } + + return buildStatusResponse({ + monitor, + payload: response, + overrides: { + status: response.alive ?? false, + code: 200, + message: "Success", + responseTime: safeTime, + }, + }); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err); + throw new AppError({ + message, + service: SERVICE_NAME, + method: "handle", + }); + } + } +} diff --git a/server/src/service/infrastructure/network/utils.ts b/server/src/service/infrastructure/network/utils.ts new file mode 100644 index 000000000..c70f06bf8 --- /dev/null +++ b/server/src/service/infrastructure/network/utils.ts @@ -0,0 +1,85 @@ +import { HTTPError, RequestError } from "got"; +import type { Response } from "got"; +import type { Monitor } from "@/types/monitor.js"; +import { MonitorStatusResponse, MonitorStatusResponseOverrides } from "@/types/network.js"; + +export const timeRequest = async (operation: () => Promise): Promise<{ response: T | null; responseTime: number; error: unknown }> => { + const start = process.hrtime.bigint(); + try { + const response = await operation(); + const elapsedMs = Math.round(Number(process.hrtime.bigint() - start) / 1_000_000); + return { response, responseTime: elapsedMs, error: null }; + } catch (error) { + const elapsedMs = Math.round(Number(process.hrtime.bigint() - start) / 1_000_000); + return { response: null, responseTime: elapsedMs, error }; + } +}; + +export const NETWORK_ERROR = 5000; +export const PING_ERROR = 5001; + +interface BuildStatusResponseArgs { + monitor: Monitor; + response?: Response | null; + error?: Error | RequestError | HTTPError | null; + payload?: T | null; + jsonPath?: string; + matchMethod?: MonitorStatusResponse["matchMethod"]; + expectedValue?: string; + extracted?: unknown; + overrides?: MonitorStatusResponseOverrides; +} + +export const buildStatusResponse = ({ + monitor, + response, + error, + payload, + jsonPath, + matchMethod, + expectedValue, + extracted, + overrides, +}: BuildStatusResponseArgs): MonitorStatusResponse => { + if (error) { + const statusResponse: MonitorStatusResponse = { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: NETWORK_ERROR, + message: error.message ?? "Network error", + responseTime: 0, + timings: undefined, + jsonPath, + matchMethod, + expectedValue, + extracted, + payload, + }; + + if (error instanceof HTTPError || error instanceof RequestError) { + statusResponse.code = error?.response?.statusCode ?? NETWORK_ERROR; + statusResponse.message = error.message; + statusResponse.responseTime = error.timings?.phases?.total ?? 0; + statusResponse.timings = error.timings; + } + return { ...statusResponse, ...(overrides ?? {}) }; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: response?.ok ?? false, + code: response?.statusCode ?? NETWORK_ERROR, + message: response?.statusMessage ?? "", + responseTime: response?.timings?.phases?.total ?? 0, + timings: response?.timings, + payload: payload ?? response?.body, + jsonPath, + matchMethod, + expectedValue, + extracted, + }; +}; diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 702affed2..2515b6145 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -16,14 +16,14 @@ import type { AxiosStatic } from "axios"; import { AppError } from "@/utils/AppError.js"; import path from "path"; import { fileURLToPath } from "url"; +import { MonitorStatusResponseOverrides } from "@/types/index.js"; import CacheableLookup from "cacheable-lookup"; import { ISettingsService } from "../system/settingsService.js"; import { ILogger } from "@/utils/logger.js"; +import { IStatusProvider } from "./network/IStatusProvider.js"; const SERVICE_NAME = "NetworkService"; -type MonitorStatusResponseOverrides = Partial, "monitorId" | "teamId" | "type">>; - interface BuildStatusResponseArgs { monitor: Monitor; response?: Response | null; @@ -94,6 +94,10 @@ class NetworkService implements INetworkService { private grpc: typeof import("@grpc/grpc-js"); private protoLoader: typeof import("@grpc/proto-loader"); + // New providers + private pingProvider; + private httpProvider; + private buildStatusResponse = ({ monitor, response, @@ -160,7 +164,11 @@ class NetworkService implements INetworkService { net: typeof import("net"), settingsService: ISettingsService, grpc: typeof import("@grpc/grpc-js"), - protoLoader: typeof import("@grpc/proto-loader") + protoLoader: typeof import("@grpc/proto-loader"), + + // New providers + pingProvider: IStatusProvider, + httpProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -185,6 +193,10 @@ class NetworkService implements INetworkService { this.grpc = grpc; this.protoLoader = protoLoader; + // New providers + this.pingProvider = pingProvider; + this.httpProvider = httpProvider; + const cacheable = new CacheableLookup(); this.got = got.extend({ @@ -231,9 +243,9 @@ class NetworkService implements INetworkService { const type = monitor?.type || "unknown"; switch (type) { case this.TYPE_PING: - return await this.requestPing(monitor); + return await this.pingProvider.handle(monitor); case this.TYPE_HTTP: - return await this.requestHttp(monitor); + return await this.httpProvider.handle(monitor); case this.TYPE_PAGESPEED: return await this.requestPageSpeed(monitor); case this.TYPE_HARDWARE: @@ -251,50 +263,6 @@ class NetworkService implements INetworkService { } } - private async requestPing(monitor: Monitor): Promise> { - try { - if (!monitor?.url) { - throw new Error("Monitor URL is required"); - } - - const rawUrl = monitor.url; - const sanitizedHost = rawUrl - .replace(/^https?:\/\//, "") - .replace(/\/.*$/, "") - .replace(/:.*/, ""); - const { response, error } = await this.timeRequest(() => this.ping.promise.probe(sanitizedHost)); - - if (!response) { - if (error) { - throw error; - } - throw new Error("Ping failed - no result returned"); - } - - const pingResponse = this.buildStatusResponse({ - monitor, - payload: response as PingStatusPayload, - overrides: { - status: (response as { alive?: boolean })?.alive ?? false, - code: 200, - message: "Success", - responseTime: (response as { time?: number })?.time ?? 0, - payload: response as PingStatusPayload, - } as MonitorStatusResponseOverrides, - }); - - return pingResponse; - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing ping", - service: this.SERVICE_NAME, - method: "requestPing", - details: { url: monitor.url }, - }); - } - } - private async requestHttp(monitor: Monitor): Promise> { const { url, secret, ignoreTlsErrors, useAdvancedMatching, jsonPath, matchMethod, expectedValue } = monitor; const httpResponse = this.buildStatusResponse({ diff --git a/server/src/types/network.ts b/server/src/types/network.ts index 2018523cd..083193e06 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -49,7 +49,7 @@ export interface PingStatusPayload { host: string; numeric_host?: string; alive: boolean; - time: number; + time: number | unknown; times?: number[]; output?: string; min?: string; @@ -134,3 +134,5 @@ export type StatusChangeResult = { temp: boolean; }; }; + +export type MonitorStatusResponseOverrides = Partial, "monitorId" | "teamId" | "type">>; From 8dd846d6cddf4e0365ed60a8c4f8255235cbc634 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 4 Mar 2026 23:36:36 +0000 Subject: [PATCH 03/16] harden http provider --- .../infrastructure/network/HttpProvider.ts | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts index 84f290b16..b47160e06 100644 --- a/server/src/service/infrastructure/network/HttpProvider.ts +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -47,7 +47,12 @@ export class HttpProvider implements IStatusProvider { } async handle(monitor: Monitor): Promise> { - const { url, secret, ignoreTlsErrors } = monitor; + const { url, secret, jsonPath, ignoreTlsErrors } = monitor; + + if (!url) { + throw new Error("URL is required for HTTP monitor"); + } + const options: Record = { headers: monitor.secret ? { Authorization: `Bearer ${secret}` } : undefined, }; @@ -58,9 +63,24 @@ export class HttpProvider implements IStatusProvider { try { const response = await this.got(url, options); - let payload: T; - const isJson = response.headers["content-type"]?.includes("application/json"); + const contentType = response.headers["content-type"] || ""; + const isJson = contentType.includes("application/json"); + if (jsonPath && !isJson) { + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: response.statusCode, + message: "Response is not JSON", + responseTime: response.timings.phases.total ?? 0, + timings: response.timings, + payload: response.body as unknown as T, + }; + } + + let payload: T; if (isJson) { try { payload = JSON.parse(response.body) as T; @@ -83,9 +103,6 @@ export class HttpProvider implements IStatusProvider { timings: response.timings, payload, extracted: matchResult.extracted, - jsonPath: monitor.jsonPath, - matchMethod: monitor.matchMethod, - expectedValue: monitor.expectedValue, }; } catch (error: unknown) { return this.handleHttpError(error, monitor); From ec82efebb47bd4a34e89c0acc5700b0f29631c83 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 17:54:16 +0000 Subject: [PATCH 04/16] pagespeed provider, hardware provider --- server/src/config/services.ts | 9 ++- .../network/HardwareProvider.ts | 29 ++++++++++ .../infrastructure/network/HttpProvider.ts | 4 +- .../network/PageSpeedProvider.ts | 55 +++++++++++++++++++ .../service/infrastructure/networkService.ts | 11 +++- 5 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 server/src/service/infrastructure/network/HardwareProvider.ts create mode 100644 server/src/service/infrastructure/network/PageSpeedProvider.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 485297fe0..11e795feb 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -78,6 +78,8 @@ import { EnvConfig } from "@/service/system/settingsService.js"; import { PingProvider } from "@/service/infrastructure/network/PingProvider.js"; import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; +import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js"; +import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; export type InitializedServices = { settingsService: any; @@ -151,7 +153,8 @@ export const initializeServices = async ({ const pingProvider = new PingProvider(ping); const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath)); - + const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger); + const hardwareProvider = new HardwareProvider(httpProvider); const networkService = new NetworkService( axios, got, @@ -168,7 +171,9 @@ export const initializeServices = async ({ grpc, protoLoader, pingProvider, - httpProvider + httpProvider, + pageSpeedProvider, + hardwareProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/service/infrastructure/network/HardwareProvider.ts b/server/src/service/infrastructure/network/HardwareProvider.ts new file mode 100644 index 000000000..a9180381e --- /dev/null +++ b/server/src/service/infrastructure/network/HardwareProvider.ts @@ -0,0 +1,29 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { HardwareStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; +import { AppError } from "@/utils/AppError.js"; + +export class HardwareProvider implements IStatusProvider { + readonly type = "hardware"; + constructor(private httpProvider: HttpProvider) {} + + supports(type: MonitorType) { + return type === "hardware"; + } + + async handle(monitor: Monitor): Promise> { + const { url } = monitor; + try { + if (!url) throw new Error("URL is required for Hardware monitor"); + return await this.httpProvider.handle(monitor); + } catch (err: unknown) { + throw new AppError({ + message: err instanceof Error ? err.message : "Error performing Hardware request", + service: "HardwareProvider", + method: "handle", + details: { url: monitor.url }, + }); + } + } +} diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts index b47160e06..e373e2cff 100644 --- a/server/src/service/infrastructure/network/HttpProvider.ts +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -4,7 +4,7 @@ import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvide import { HttpStatusPayload } from "@/types/network.js"; import { MonitorStatusResponse } from "@/types/network.js"; import { Agent as HttpsAgent } from "https"; -import { Monitor } from "@/types/monitor.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; export class HttpProvider implements IStatusProvider { @@ -15,7 +15,7 @@ export class HttpProvider implements IStatusProvider { private advancedMatcher: AdvancedMatcher ) {} - supports(type: string) { + supports(type: MonitorType) { return type === "http"; } diff --git a/server/src/service/infrastructure/network/PageSpeedProvider.ts b/server/src/service/infrastructure/network/PageSpeedProvider.ts new file mode 100644 index 000000000..121fe9bf8 --- /dev/null +++ b/server/src/service/infrastructure/network/PageSpeedProvider.ts @@ -0,0 +1,55 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { MonitorStatusResponse, PageSpeedStatusPayload } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; +import { ISettingsService } from "@/service/system/settingsService.js"; +import { ILogger } from "@/utils/logger.js"; +import { AppError } from "@/utils/AppError.js"; + +export class PageSpeedProvider implements IStatusProvider { + readonly type = "pagespeed"; + constructor( + private httpProvider: HttpProvider, + private settingsService: ISettingsService, + private logger: ILogger + ) {} + + supports(type: MonitorType) { + return type === "pagespeed"; + } + + async handle(monitor: Monitor): Promise> { + const { url } = monitor; + try { + if (!url) throw new Error("URL is required for PageSpeed monitor"); + const dbSettings = await this.settingsService.getDBSettings(); + const apiKey = dbSettings?.pagespeedApiKey; + let pageSpeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent( + url + )}&category=seo&category=accessibility&category=best-practices&category=performance`; + + if (apiKey) { + pageSpeedUrl += `&key=${apiKey}`; + } else { + this.logger.warn({ + message: "PageSpeed API key not found, performance may be throttled", + service: "PageSpeedProvider", + method: "handle", + details: { url }, + }); + } + + return await this.httpProvider.handle({ + ...monitor, + url: pageSpeedUrl, + }); + } catch (err: unknown) { + throw new AppError({ + message: err instanceof Error ? err.message : "Error performing PageSpeed request", + service: "PageSpeedProvider", + method: "handle", + details: { url: monitor.url }, + }); + } + } +} diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 2515b6145..539a1f8c2 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -97,6 +97,8 @@ class NetworkService implements INetworkService { // New providers private pingProvider; private httpProvider; + private pageSpeedProvider; + private hardwareProvider; private buildStatusResponse = ({ monitor, @@ -168,7 +170,9 @@ class NetworkService implements INetworkService { // New providers pingProvider: IStatusProvider, - httpProvider: IStatusProvider + httpProvider: IStatusProvider, + pagespeedProvider: IStatusProvider, + hardwareProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -196,7 +200,8 @@ class NetworkService implements INetworkService { // New providers this.pingProvider = pingProvider; this.httpProvider = httpProvider; - + this.pageSpeedProvider = pagespeedProvider; + this.hardwareProvider = hardwareProvider; const cacheable = new CacheableLookup(); this.got = got.extend({ @@ -247,7 +252,7 @@ class NetworkService implements INetworkService { case this.TYPE_HTTP: return await this.httpProvider.handle(monitor); case this.TYPE_PAGESPEED: - return await this.requestPageSpeed(monitor); + return await this.pageSpeedProvider.handle(monitor); case this.TYPE_HARDWARE: return await this.requestHardware(monitor); case this.TYPE_DOCKER: From f3b156f2d0c02c19983c65b6eb8b759aa6919a42 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:19:03 +0000 Subject: [PATCH 05/16] docker provider --- server/src/config/services.ts | 6 +- .../infrastructure/network/DockerProvider.ts | 160 ++++++++++++++++++ .../service/infrastructure/networkService.ts | 9 +- 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 server/src/service/infrastructure/network/DockerProvider.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 11e795feb..eed243817 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -80,6 +80,7 @@ import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js"; import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; +import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; export type InitializedServices = { settingsService: any; @@ -155,6 +156,8 @@ export const initializeServices = async ({ const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath)); const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger); const hardwareProvider = new HardwareProvider(httpProvider); + const dockerProvider = new DockerProvider(logger, Docker); + const networkService = new NetworkService( axios, got, @@ -173,7 +176,8 @@ export const initializeServices = async ({ pingProvider, httpProvider, pageSpeedProvider, - hardwareProvider + hardwareProvider, + dockerProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/service/infrastructure/network/DockerProvider.ts b/server/src/service/infrastructure/network/DockerProvider.ts new file mode 100644 index 000000000..8e84b5dc6 --- /dev/null +++ b/server/src/service/infrastructure/network/DockerProvider.ts @@ -0,0 +1,160 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { DockerStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { ILogger } from "@/utils/logger.js"; +import { AppError } from "@/utils/AppError.js"; +import Dockerode from "dockerode"; +import { NETWORK_ERROR, timeRequest } from "@/service/infrastructure/network/utils.js"; + +type DockerodeType = typeof Dockerode; + +const SERVICE_NAME = "DockerProvider"; + +export interface DockerError extends Error { + statusCode?: number; + reason?: string; + json?: { message?: string }; +} + +export class DockerProvider implements IStatusProvider { + readonly type = "docker"; + private docker: Dockerode; + constructor( + private logger: ILogger, + private DockerLib: DockerodeType + ) { + this.docker = new this.DockerLib(); + } + + supports(type: MonitorType): boolean { + return type === "docker"; + } + + private isDockerError(error: unknown): error is DockerError { + return error instanceof Error && ("statusCode" in error || "reason" in error || "json" in error); + } + + async handle(monitor: Monitor): Promise> { + const { url: containerInput } = monitor; + try { + if (!containerInput) { + throw new Error("Container name or ID is required for Docker monitor"); + } + + const containers = await this.docker.listContainers({ all: true }); + const normalizedInput = containerInput.replace(/^\/+/, "").toLowerCase(); + + // Priority-based matching to avoid ambiguity: + // 1. Exact full ID match (64-char) + const exactIdMatch = containers.find((c) => c.Id.toLowerCase() === normalizedInput); + + // 2. Exact container name match (case-insensitive) + const exactNameMatch = containers.find((c) => + c.Names.some((name: string) => { + const cleanName = name.replace(/^\/+/, "").toLowerCase(); + return cleanName === normalizedInput; + }) + ); + + // 3. Partial ID match (fallback for backwards compatibility) + const partialIdMatch = containers.find((c) => c.Id.toLowerCase().startsWith(normalizedInput)); + + // Select container based on priority + const targetContainer = exactIdMatch || exactNameMatch || partialIdMatch; + + // Handle no match + if (!targetContainer) { + this.logger.warn({ + message: `No container found for "${monitor.url}".`, + service: SERVICE_NAME, + method: "handle", + details: { url: monitor.url }, + }); + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: 404, + message: "Docker container not found", + responseTime: 0, + payload: null, + }; + } + + // 5. Handle Ambiguity Check + const matchTypes: string[] = []; + if (exactIdMatch) matchTypes.push("exact ID"); + if (exactNameMatch) matchTypes.push("exact name"); + if (partialIdMatch && !exactIdMatch) matchTypes.push("partial ID"); + + if (matchTypes.length > 1) { + const message = `Ambiguous container match for "${containerInput}". Matched by: ${matchTypes.join(", ")}. Using ${exactIdMatch ? "exact ID" : exactNameMatch ? "exact name" : "partial ID"} match.`; + + this.logger.warn({ + message, + service: SERVICE_NAME, + method: "handle", + details: { url: containerInput }, + }); + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: NETWORK_ERROR, + message, + responseTime: 0, + payload: null, + }; + } + + // 6. Inspect Container Status + const container = this.docker.getContainer(targetContainer.Id); + const { response, responseTime, error } = await timeRequest(() => container.inspect()); + + if (error) { + let message = "Failed to fetch Docker container information"; + let code = NETWORK_ERROR; + + if (this.isDockerError(error)) { + code = error.statusCode ?? NETWORK_ERROR; + message = error.json?.message ?? error.reason ?? error.message; + } else if (error instanceof Error) { + message = error.message; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: code, + message: message, + responseTime, + payload: null, + }; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: response?.State?.Status === "running", + code: 200, + message: "Docker container status fetched successfully", + responseTime, + payload: response as unknown as DockerStatusPayload, + }; + } catch (err: unknown) { + throw new AppError({ + message: err instanceof Error ? err.message : "Error performing Docker request", + service: SERVICE_NAME, + method: "handle", + details: { url: containerInput }, + }); + } + } +} diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 539a1f8c2..c79a2cee6 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -99,6 +99,7 @@ class NetworkService implements INetworkService { private httpProvider; private pageSpeedProvider; private hardwareProvider; + private dockerProvider; private buildStatusResponse = ({ monitor, @@ -172,7 +173,8 @@ class NetworkService implements INetworkService { pingProvider: IStatusProvider, httpProvider: IStatusProvider, pagespeedProvider: IStatusProvider, - hardwareProvider: IStatusProvider + hardwareProvider: IStatusProvider, + dockerProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -202,6 +204,7 @@ class NetworkService implements INetworkService { this.httpProvider = httpProvider; this.pageSpeedProvider = pagespeedProvider; this.hardwareProvider = hardwareProvider; + this.dockerProvider = dockerProvider; const cacheable = new CacheableLookup(); this.got = got.extend({ @@ -254,9 +257,9 @@ class NetworkService implements INetworkService { case this.TYPE_PAGESPEED: return await this.pageSpeedProvider.handle(monitor); case this.TYPE_HARDWARE: - return await this.requestHardware(monitor); + return await this.hardwareProvider.handle(monitor); case this.TYPE_DOCKER: - return await this.requestDocker(monitor); + return await this.dockerProvider.handle(monitor); case this.TYPE_PORT: return await this.requestPort(monitor); case this.TYPE_GAME: From 80603c9a4214012c7092cf7869c0e96eaa507da8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:30:06 +0000 Subject: [PATCH 06/16] port provider --- .../infrastructure/network/PortProvider.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 server/src/service/infrastructure/network/PortProvider.ts diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts new file mode 100644 index 000000000..bacbd19ff --- /dev/null +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -0,0 +1,98 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { PortStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { AppError } from "@/utils/AppError.js"; +import { NETWORK_ERROR, timeRequest } from "@/service/infrastructure/network/utils.js"; +import * as net from "net"; +type NetType = typeof net; + +const SERVICE_NAME = "PortProvider"; + +export class PortProvider implements IStatusProvider { + readonly type = "port"; + + constructor(private net: NetType) {} + + supports(type: MonitorType): boolean { + return type === "port"; + } + + async handle(monitor: Monitor): Promise> { + try { + const { url, port } = monitor; + if (!url || !port) { + throw new Error("URL and port are required for port monitoring"); + } + + const { responseTime, error } = await timeRequest(async () => { + return new Promise((resolve, reject) => { + const socket = this.net.createConnection( + { + host: url, + port, + }, + () => { + socket.end(); + socket.destroy(); + resolve({ success: true }); + } + ); + + socket.setTimeout(5000); + socket.on("timeout", () => { + socket.destroy(); + reject(new Error("Connection timeout")); + }); + + socket.on("error", (err: unknown) => { + socket.destroy(); + reject(err); + }); + }); + }); + if (error) { + const errorMessage = error instanceof Error ? error.message : "Port check failed"; + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: false, + code: NETWORK_ERROR, + message: errorMessage, + responseTime: responseTime, + timings: undefined, + payload: { success: false }, + jsonPath: monitor.jsonPath, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + extracted: undefined, + }; + } + + return { + monitorId: monitor.id, + teamId: monitor.teamId, + type: monitor.type, + status: true, + code: 200, + message: "Port check successful", + responseTime: responseTime, + timings: undefined, + payload: { success: true }, + jsonPath: monitor.jsonPath, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + extracted: undefined, + }; + } catch (err: unknown) { + const originalMessage = err instanceof Error ? err.message : String(err); + throw new AppError({ + message: originalMessage || "Error performing port check", + status: 500, + service: SERVICE_NAME, + method: "requestPort", + details: { url: monitor.url, port: monitor.port }, + }); + } + } +} From 49672db563524412ccd49d22ff710f1b8605b61a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:35:19 +0000 Subject: [PATCH 07/16] port provider --- server/src/config/services.ts | 5 ++++- .../src/service/infrastructure/network/PortProvider.ts | 10 +--------- server/src/service/infrastructure/networkService.ts | 7 +++++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/server/src/config/services.ts b/server/src/config/services.ts index eed243817..1d8558f9c 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -81,6 +81,7 @@ import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatche import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js"; import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; +import { PortProvider } from "@/service/infrastructure/network/PortProvider.js"; export type InitializedServices = { settingsService: any; @@ -157,6 +158,7 @@ export const initializeServices = async ({ const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger); const hardwareProvider = new HardwareProvider(httpProvider); const dockerProvider = new DockerProvider(logger, Docker); + const portProvider = new PortProvider(net); const networkService = new NetworkService( axios, @@ -177,7 +179,8 @@ export const initializeServices = async ({ httpProvider, pageSpeedProvider, hardwareProvider, - dockerProvider + dockerProvider, + portProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts index bacbd19ff..7c1c96ce4 100644 --- a/server/src/service/infrastructure/network/PortProvider.ts +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -62,10 +62,6 @@ export class PortProvider implements IStatusProvider { responseTime: responseTime, timings: undefined, payload: { success: false }, - jsonPath: monitor.jsonPath, - matchMethod: monitor.matchMethod, - expectedValue: monitor.expectedValue, - extracted: undefined, }; } @@ -79,10 +75,6 @@ export class PortProvider implements IStatusProvider { responseTime: responseTime, timings: undefined, payload: { success: true }, - jsonPath: monitor.jsonPath, - matchMethod: monitor.matchMethod, - expectedValue: monitor.expectedValue, - extracted: undefined, }; } catch (err: unknown) { const originalMessage = err instanceof Error ? err.message : String(err); @@ -90,7 +82,7 @@ export class PortProvider implements IStatusProvider { message: originalMessage || "Error performing port check", status: 500, service: SERVICE_NAME, - method: "requestPort", + method: "handle", details: { url: monitor.url, port: monitor.port }, }); } diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index c79a2cee6..f535eae6c 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -100,6 +100,7 @@ class NetworkService implements INetworkService { private pageSpeedProvider; private hardwareProvider; private dockerProvider; + private portProvider; private buildStatusResponse = ({ monitor, @@ -174,7 +175,8 @@ class NetworkService implements INetworkService { httpProvider: IStatusProvider, pagespeedProvider: IStatusProvider, hardwareProvider: IStatusProvider, - dockerProvider: IStatusProvider + dockerProvider: IStatusProvider, + portProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -205,6 +207,7 @@ class NetworkService implements INetworkService { this.pageSpeedProvider = pagespeedProvider; this.hardwareProvider = hardwareProvider; this.dockerProvider = dockerProvider; + this.portProvider = portProvider; const cacheable = new CacheableLookup(); this.got = got.extend({ @@ -261,7 +264,7 @@ class NetworkService implements INetworkService { case this.TYPE_DOCKER: return await this.dockerProvider.handle(monitor); case this.TYPE_PORT: - return await this.requestPort(monitor); + return await this.portProvider.handle(monitor); case this.TYPE_GAME: return await this.requestGame(monitor); case this.TYPE_GRPC: From 190d9dd2b1ae829db40391e2fd5816c9293cb74b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:45:27 +0000 Subject: [PATCH 08/16] gameProvider --- .../infrastructure/network/GameProvider.ts | 78 +++++++++++++++++++ .../infrastructure/network/PortProvider.ts | 1 + server/src/types/network.ts | 4 +- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 server/src/service/infrastructure/network/GameProvider.ts diff --git a/server/src/service/infrastructure/network/GameProvider.ts b/server/src/service/infrastructure/network/GameProvider.ts new file mode 100644 index 000000000..e2ace7433 --- /dev/null +++ b/server/src/service/infrastructure/network/GameProvider.ts @@ -0,0 +1,78 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { GameStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { GameDig } from "gamedig"; +import { AppError } from "@/utils/AppError.js"; +import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; +import { ILogger } from "@/utils/logger.js"; + +type GameDigType = typeof GameDig; + +const SERVICE_NAME = "GameProvider"; +export class GameProvider implements IStatusProvider { + readonly type = "game"; + constructor( + private logger: ILogger, + private gameDig: GameDigType + ) {} + + supports(type: MonitorType): boolean { + return type === "game"; + } + + async handle(monitor: Monitor): Promise> { + const { url, port, gameId, id, teamId, type } = monitor; + + // Clean the host in case a full URL was passed + const host = url?.replace(/^https?:\/\//, "").split(/[/?#:]/)[0] || ""; + try { + const state = await this.gameDig + .query({ + type: gameId ?? "unknown", + host: host, + port: port ?? 0, + }) + .catch((error: unknown) => { + this.logger.warn({ + message: error instanceof Error ? error.message : String(error), + service: SERVICE_NAME, + method: "handle", + details: { host, port, gameId }, + }); + return undefined; + }); + + if (!state) { + return { + monitorId: id, + teamId: teamId, + type: type, + status: false, + code: NETWORK_ERROR, + message: "No response from game server", + responseTime: 0, + payload: null, + }; + } + + return { + monitorId: id, + teamId: teamId, + type: type, + status: true, + code: 200, + message: "Success", + responseTime: state.ping ?? 0, + payload: state, + }; + } catch (err: unknown) { + const originalMessage = err instanceof Error ? err.message : String(err); + throw new AppError({ + message: originalMessage || "Error performing game server check", + service: SERVICE_NAME, + method: "handle", + details: { url: monitor.url, port: monitor.port, gameId: monitor.gameId }, + }); + } + } +} diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts index 7c1c96ce4..66fefd0fe 100644 --- a/server/src/service/infrastructure/network/PortProvider.ts +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -51,6 +51,7 @@ export class PortProvider implements IStatusProvider { }); }); if (error) { + console.log(error); const errorMessage = error instanceof Error ? error.message : "Port check failed"; return { monitorId: monitor.id, diff --git a/server/src/types/network.ts b/server/src/types/network.ts index 083193e06..93ddc4ac9 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -14,6 +14,8 @@ import type { MonitorStatus, } from "@/types/index.js"; +import type { QueryResult } from "gamedig"; + export interface MonitorStatusResponse< T = | HttpStatusPayload @@ -100,7 +102,7 @@ export interface PortStatusPayload { success: boolean; } -export type GameStatusPayload = Record; +export type GameStatusPayload = QueryResult; export interface GrpcStatusPayload { grpcStatusCode: number; From 27c44a0fbd69c55a4a7de994ca945972037d34e0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 18:48:19 +0000 Subject: [PATCH 09/16] config --- server/src/config/services.ts | 5 +- .../service/infrastructure/networkService.ts | 54 ++----------------- 2 files changed, 9 insertions(+), 50 deletions(-) diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 1d8558f9c..953d89b0f 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -82,6 +82,7 @@ import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedPro import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; import { PortProvider } from "@/service/infrastructure/network/PortProvider.js"; +import { GameProvider } from "@/service/infrastructure/network/GameProvider.js"; export type InitializedServices = { settingsService: any; @@ -159,6 +160,7 @@ export const initializeServices = async ({ const hardwareProvider = new HardwareProvider(httpProvider); const dockerProvider = new DockerProvider(logger, Docker); const portProvider = new PortProvider(net); + const gameProvider = new GameProvider(logger, GameDig); const networkService = new NetworkService( axios, @@ -180,7 +182,8 @@ export const initializeServices = async ({ pageSpeedProvider, hardwareProvider, dockerProvider, - portProvider + portProvider, + gameProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index f535eae6c..d727345da 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -101,6 +101,7 @@ class NetworkService implements INetworkService { private hardwareProvider; private dockerProvider; private portProvider; + private gameProvider; private buildStatusResponse = ({ monitor, @@ -176,7 +177,8 @@ class NetworkService implements INetworkService { pagespeedProvider: IStatusProvider, hardwareProvider: IStatusProvider, dockerProvider: IStatusProvider, - portProvider: IStatusProvider + portProvider: IStatusProvider, + gameProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -208,6 +210,7 @@ class NetworkService implements INetworkService { this.hardwareProvider = hardwareProvider; this.dockerProvider = dockerProvider; this.portProvider = portProvider; + this.gameProvider = gameProvider; const cacheable = new CacheableLookup(); this.got = got.extend({ @@ -266,7 +269,7 @@ class NetworkService implements INetworkService { case this.TYPE_PORT: return await this.portProvider.handle(monitor); case this.TYPE_GAME: - return await this.requestGame(monitor); + return await this.gameProvider.handle(monitor); case this.TYPE_GRPC: return await this.requestGrpc(monitor); default: @@ -628,53 +631,6 @@ class NetworkService implements INetworkService { } } - private async requestGame(monitor: Monitor): Promise> { - try { - const { url, port, gameId } = monitor; - - const gameResponse = this.buildStatusResponse({ - monitor, - overrides: { - code: 200, - status: true, - message: "Success", - }, - }); - - const state = await this.GameDig.query({ - type: gameId ?? "unknown", - host: url, - port: port ?? 0, - }).catch((error: unknown): undefined => { - this.logger.warn({ - message: error instanceof Error ? error.message : String(error), - service: this.SERVICE_NAME, - method: "requestGame", - details: { url, port, gameId }, - }); - return undefined; - }); - - if (!state) { - gameResponse.code = this.NETWORK_ERROR; - gameResponse.status = false; - gameResponse.message = "No response"; - return gameResponse; - } - - gameResponse.responseTime = state.ping ?? 0; - gameResponse.payload = state as GameStatusPayload; - return gameResponse; - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing game server check", - service: this.SERVICE_NAME, - method: "requestGame", - details: { url: monitor.url, port: monitor.port, gameId: monitor.gameId }, - }); - } - } private async requestGrpc(monitor: Monitor): Promise> { try { const { url, port, ignoreTlsErrors } = monitor; From c7689cc64a691f71d1fb06d5a5ce4085f7d1c255 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 19:18:29 +0000 Subject: [PATCH 10/16] grpc --- .../infrastructure/network/GrpcProvider.ts | 187 +++++++++ .../service/infrastructure/networkService.ts | 354 ------------------ 2 files changed, 187 insertions(+), 354 deletions(-) create mode 100644 server/src/service/infrastructure/network/GrpcProvider.ts diff --git a/server/src/service/infrastructure/network/GrpcProvider.ts b/server/src/service/infrastructure/network/GrpcProvider.ts new file mode 100644 index 000000000..4bf127785 --- /dev/null +++ b/server/src/service/infrastructure/network/GrpcProvider.ts @@ -0,0 +1,187 @@ +import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvider.js"; +import { GrpcStatusPayload, MonitorStatusResponse } from "@/types/network.js"; +import { Monitor, MonitorType } from "@/types/monitor.js"; +import { AppError } from "@/utils/AppError.js"; +import { NETWORK_ERROR, timeRequest } from "@/service/infrastructure/network/utils.js"; +import { ILogger } from "@/utils/logger.js"; +import * as grpc from "@grpc/grpc-js"; +import * as protoLoader from "@grpc/proto-loader"; + +import path from "path"; +import { fileURLToPath } from "url"; +import { Network } from "inspector/promises"; + +type GrpcType = typeof grpc; +type ProtoLoaderType = typeof protoLoader; + +const SERVICE_NAME = "GrpcProvider"; + +export interface GrpcError extends Error { + code: number; + details?: string; +} + +export class GrpcProvider implements IStatusProvider { + readonly type = "grpc"; + constructor( + private logger: ILogger, + private grpc: GrpcType, + private protoLoader: ProtoLoaderType + ) {} + + supports(type: MonitorType): boolean { + return type === "grpc"; + } + + private isGrpcError(error: unknown): error is GrpcError { + return error instanceof Error && "code" in error && typeof (error as Record).code === "number"; + } + + private getGrpcStatusName(code: number): string { + const statusNames: Record = { + 0: "OK", + 1: "CANCELLED", + 2: "UNKNOWN", + 3: "INVALID_ARGUMENT", + 4: "DEADLINE_EXCEEDED", + 5: "NOT_FOUND", + 6: "ALREADY_EXISTS", + 7: "PERMISSION_DENIED", + 8: "RESOURCE_EXHAUSTED", + 9: "FAILED_PRECONDITION", + 10: "ABORTED", + 11: "OUT_OF_RANGE", + 12: "UNIMPLEMENTED", + 13: "INTERNAL", + 14: "UNAVAILABLE", + 15: "DATA_LOSS", + 16: "UNAUTHENTICATED", + }; + return statusNames[code] || "UNKNOWN"; + } + + async handle(monitor: Monitor): Promise> { + const { id, teamId, type, url, port, ignoreTlsErrors } = monitor; + const grpcServiceName = monitor.grpcServiceName || ""; + const host = url?.replace(/^https?:\/\//, "").split(/[/?#:]/)[0]; + + try { + if (!host || !port) { + throw new Error("Valid host and port are required"); + } + const target = `${host}:${port}`; + const currentFilePath = fileURLToPath(import.meta.url); + const protoPath = path.join(path.dirname(currentFilePath), "protos", "health.proto"); + const packageDefinition = this.protoLoader.loadSync(protoPath, { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, + }); + const grpcObject = this.grpc.loadPackageDefinition(packageDefinition) as any; + const HealthClient = grpcObject.grpc.health.v1.Health; + let credentials; + if (ignoreTlsErrors) { + credentials = this.grpc.credentials.createSsl(null, null, null, { + checkServerIdentity: () => undefined, + }); + } else { + credentials = this.grpc.credentials.createInsecure(); + } + const client = new HealthClient(target, credentials); + const deadline = new Date(Date.now() + 10000); // 10s timeout + const { response, responseTime, error } = await timeRequest(() => { + return new Promise((resolve, reject) => { + client.Check({ service: grpcServiceName }, { deadline }, (err: unknown, response: unknown) => { + client.close(); + + if (err) { + const grpcErr = err as { code?: number; details?: string; message?: string }; + const payload: GrpcStatusPayload = { + grpcStatusCode: grpcErr.code ?? -1, + grpcStatusName: this.getGrpcStatusName(grpcErr.code ?? -1), + serviceName: grpcServiceName, + servingStatus: "UNKNOWN", + }; + const grpcError = new AppError({ + message: grpcErr.details || grpcErr.message || "gRPC error", + service: SERVICE_NAME, + method: "handle", + }) as AppError & { grpcPayload?: GrpcStatusPayload; grpcCode?: number }; + grpcError.grpcPayload = payload; + grpcError.grpcCode = grpcErr.code; + reject(grpcError); + return; + } + + const resp = response as { status?: string } | undefined; + const servingStatus = resp?.status ?? "UNKNOWN"; + resolve({ + grpcStatusCode: 0, + grpcStatusName: "OK", + serviceName: grpcServiceName, + servingStatus, + }); + }); + }); + }); + + if (error) { + let grpcCode = -1; + let message = error instanceof Error ? error.message : "gRPC health check failed"; + + if (this.isGrpcError(error)) { + grpcCode = error.code; + message = error.details || error.message; + } + + return { + monitorId: id, + teamId: teamId, + type: type, + status: false, + code: 500, + message: message, + responseTime, + payload: { + grpcStatusCode: grpcCode, + grpcStatusName: this.getGrpcStatusName(grpcCode), + serviceName: grpcServiceName, + servingStatus: "UNKNOWN", + }, + }; + } + + if (!response) { + throw new Error("No response received from gRPC health check"); + } + + const isServing = response.servingStatus === "SERVING"; + + return { + monitorId: id, + teamId: teamId, + type: type, + status: isServing, + code: isServing ? 200 : NETWORK_ERROR, + message: isServing ? "gRPC health check successful" : "gRPC health check failed", + responseTime, + payload: { + grpcStatusCode: isServing ? 200 : NETWORK_ERROR, + grpcStatusName: response.grpcStatusName, + serviceName: response.serviceName, + servingStatus: response.servingStatus || "UNKNOWN", + }, + }; + } catch (err: unknown) { + const originalMessage = err instanceof Error ? err.message : String(err); + throw new AppError({ + message: originalMessage || "Error performing gRPC health check", + service: SERVICE_NAME, + method: "handle", + details: { url: monitor.url, port: monitor.port, grpcServiceName: monitor.grpcServiceName }, + }); + } + } +} diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index d727345da..1a0d71553 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -277,360 +277,6 @@ class NetworkService implements INetworkService { } } - private async requestHttp(monitor: Monitor): Promise> { - const { url, secret, ignoreTlsErrors, useAdvancedMatching, jsonPath, matchMethod, expectedValue } = monitor; - const httpResponse = this.buildStatusResponse({ - monitor, - overrides: { - status: false, - code: this.NETWORK_ERROR, - message: "Request not executed", - }, - }); - - try { - if (!url) { - throw new Error("Monitor URL is required"); - } - const config: Record = { - headers: secret ? { Authorization: `Bearer ${secret}` } : undefined, - }; - - if (ignoreTlsErrors) { - config.agent = { - https: new this.https.Agent({ - rejectUnauthorized: false, - }), - }; - } - - const response = await this.got(url, config); - - let payload: T | string | undefined; - const contentType = response.headers["content-type"]; - - if (contentType && contentType.includes("application/json")) { - try { - payload = JSON.parse(response.body); - } catch { - payload = response.body; - } - } else { - payload = response.body; - } - - Object.assign(httpResponse, { - code: response.statusCode, - status: response.ok, - message: response.statusMessage ?? "", - responseTime: response.timings.phases.total || 0, - payload, - timings: response.timings, - }); - - if (!useAdvancedMatching) { - return httpResponse; - } - - if (expectedValue && !jsonPath) { - let ok = false; - if (matchMethod === "equal") ok = payload === expectedValue; - if (matchMethod === "include" && typeof payload === "string") ok = payload.includes(expectedValue); - if (matchMethod === "regex" && typeof payload === "string") ok = new RegExp(expectedValue).test(payload); - - if (ok === true) { - return httpResponse; - } else { - httpResponse.code = 500; - httpResponse.status = false; - httpResponse.message = "Expected value did not match"; - return httpResponse; - } - } - - if (jsonPath) { - const contentType = response.headers["content-type"]; - const isJson = contentType?.includes("application/json"); - if (!isJson) { - httpResponse.status = false; - httpResponse.message = "Response is not JSON"; - return httpResponse; - } - try { - const extracted = this.jmespath.search(payload, jsonPath); - if (expectedValue) { - let ok = false; - if (matchMethod === "equal") ok = extracted === expectedValue; - if (matchMethod === "include" && typeof extracted === "string") ok = extracted.includes(expectedValue); - if (matchMethod === "regex" && typeof extracted === "string") ok = new RegExp(expectedValue).test(extracted); - - if (ok) { - httpResponse.extracted = extracted; - return httpResponse; - } else { - httpResponse.status = false; - httpResponse.code = 500; - httpResponse.message = "Expected value did not match"; - httpResponse.extracted = extracted; - return httpResponse; - } - } else { - const isFalsey = extracted === false || extracted === "false" || extracted === undefined || extracted === null; - if (!isFalsey) { - httpResponse.extracted = extracted; - return httpResponse; - } else { - httpResponse.status = false; - httpResponse.code = 500; - httpResponse.message = "Expected value did not match"; - httpResponse.extracted = extracted; - return httpResponse; - } - } - } catch { - httpResponse.status = false; - httpResponse.message = "Error evaluating JSON path"; - return httpResponse; - } - } - return httpResponse; - } catch (err: unknown) { - if (err instanceof HTTPError || err instanceof RequestError) { - httpResponse.code = err.response?.statusCode || this.NETWORK_ERROR; - httpResponse.status = false; - httpResponse.message = String(err.response?.statusCode) || err.message; - httpResponse.responseTime = err.timings?.phases?.total || 0; - httpResponse.payload = null; - httpResponse.timings = err.timings || undefined; - return httpResponse; - } - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing HTTP request", - service: this.SERVICE_NAME, - method: "requestHttp", - details: { url: monitor.url }, - }); - } - } - - private async requestPageSpeed(monitor: Monitor): Promise> { - try { - const url = monitor.url; - if (!url) { - throw new Error("Monitor URL is required"); - } - let pageSpeedUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&category=seo&category=accessibility&category=best-practices&category=performance`; - const dbSettings = await this.settingsService.getDBSettings(); - if (dbSettings?.pagespeedApiKey) { - pageSpeedUrl += `&key=${dbSettings.pagespeedApiKey}`; - } else { - this.logger.warn({ - message: "PageSpeed API key not found, job not executed", - service: this.SERVICE_NAME, - method: "requestPagespeed", - details: { url }, - }); - } - return await this.requestHttp({ - ...monitor, - url: pageSpeedUrl, - }); - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing PageSpeed request", - service: this.SERVICE_NAME, - method: "requestPageSpeed", - details: { url: monitor.url }, - }); - } - } - - private async requestHardware(monitor: Monitor): Promise> { - try { - return await this.requestHttp(monitor); - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing hardware request", - service: this.SERVICE_NAME, - method: "requestHardware", - details: { url: monitor.url }, - }); - } - } - - private async requestDocker(monitor: Monitor): Promise> { - try { - if (!monitor.url) { - throw new Error("Monitor URL is required"); - } - - const docker = new this.Docker({ - socketPath: "/var/run/docker.sock", - }); - - const dockerResponse = this.buildStatusResponse({ - monitor, - overrides: { - status: false, - code: this.NETWORK_ERROR, - message: "No response", - }, - }); - - const containers = await docker.listContainers({ all: true }); - - // Normalize input: strip leading slashes and convert to lowercase for comparison - const normalizedInput = monitor.url.replace(/^\/+/, "").toLowerCase(); - - // Priority-based matching to avoid ambiguity: - // 1. Exact full ID match (64-char) - const exactIdMatch = containers.find((c) => c.Id.toLowerCase() === normalizedInput); - - // 2. Exact container name match (case-insensitive) - const exactNameMatch = containers.find((c) => - c.Names.some((name: string) => { - const cleanName = name.replace(/^\/+/, "").toLowerCase(); - return cleanName === normalizedInput; - }) - ); - - // 3. Partial ID match (fallback for backwards compatibility) - const partialIdMatch = containers.find((c) => c.Id.toLowerCase().startsWith(normalizedInput)); - - // Select container based on priority - const targetContainer = exactIdMatch || exactNameMatch || partialIdMatch; - - // Return negative response if no container - if (!targetContainer) { - this.logger.warn({ - message: `No container found for "${monitor.url}".`, - service: this.SERVICE_NAME, - method: "requestDocker", - details: { url: monitor.url }, - }); - - dockerResponse.code = 404; - dockerResponse.status = false; - dockerResponse.message = "Docker container not found"; - return dockerResponse; - } - - // Return negative response if ambiguous matches exist - const matchTypes = []; - if (exactIdMatch) matchTypes.push("exact ID"); - if (exactNameMatch) matchTypes.push("exact name"); - if (partialIdMatch && !exactIdMatch) matchTypes.push("partial ID"); - - if (matchTypes.length > 1) { - this.logger.warn({ - message: `Ambiguous container match for "${monitor.url}". Matched by: ${matchTypes.join(", ")}. Using ${exactIdMatch ? "exact ID" : exactNameMatch ? "exact name" : "partial ID"} match.`, - service: this.SERVICE_NAME, - method: "requestDocker", - details: { url: monitor.url }, - }); - dockerResponse.status = false; - dockerResponse.message = `Ambiguous container match for "${monitor.url}". Matched by: ${matchTypes.join(", ")}. Using ${exactIdMatch ? "exact ID" : exactNameMatch ? "exact name" : "partial ID"} match.`; - return dockerResponse; - } - - const container = docker.getContainer(targetContainer.Id); - const { response, responseTime, error }: { response?: { State: { Status: string } } | null; responseTime: number; error: unknown } = - await this.timeRequest(() => container.inspect()); - - dockerResponse.responseTime = responseTime; - dockerResponse.status = response?.State?.Status === "running" ? true : false; - dockerResponse.code = 200; - dockerResponse.message = "Docker container status fetched successfully"; - - if (error) { - dockerResponse.status = false; - dockerResponse.code = - (error && typeof error === "object" && "statusCode" in error ? (error as { statusCode?: number }).statusCode : undefined) || - this.NETWORK_ERROR; - dockerResponse.message = - (error && typeof error === "object" && "reason" in error ? (error as { reason?: string }).reason : undefined) || - "Failed to fetch Docker container information"; - return dockerResponse; - } - - return dockerResponse; - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing Docker request", - service: this.SERVICE_NAME, - method: "requestDocker", - details: { url: monitor.url }, - }); - } - } - - private async requestPort(monitor: Monitor): Promise> { - try { - const { url, port } = monitor; - if (!port) { - throw new Error("Port is required for port monitor"); - } - - const { response, responseTime, error } = await this.timeRequest(async () => { - return new Promise((resolve, reject) => { - const socket = this.net.createConnection( - { - host: url, - port, - }, - () => { - socket.end(); - socket.destroy(); - resolve({ success: true }); - } - ); - - socket.setTimeout(5000); - socket.on("timeout", () => { - socket.destroy(); - reject(new Error("Connection timeout")); - }); - - socket.on("error", (err: unknown) => { - socket.destroy(); - reject(err); - }); - }); - }); - - const portResponse = this.buildStatusResponse({ - monitor, - overrides: { - code: 200, - status: (response as { success?: boolean })?.success ?? false, - message: "Port check successful", - responseTime, - }, - }); - - if (error) { - portResponse.code = this.NETWORK_ERROR; - portResponse.status = false; - portResponse.message = "Port check failed"; - return portResponse; - } - - return portResponse; - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing port check", - service: this.SERVICE_NAME, - method: "requestPort", - details: { url: monitor.url, port: monitor.port }, - }); - } - } - private async requestGrpc(monitor: Monitor): Promise> { try { const { url, port, ignoreTlsErrors } = monitor; From ad7d4e54c397ee2c9cfe147e31434c3af00f1c99 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 19:34:57 +0000 Subject: [PATCH 11/16] cleanup --- server/src/config/services.ts | 18 +- .../infrastructure/network/GrpcProvider.ts | 97 +++--- .../infrastructure/network/HttpProvider.ts | 12 +- .../service/infrastructure/networkService.ts | 293 +----------------- 4 files changed, 66 insertions(+), 354 deletions(-) diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 953d89b0f..bd13b7dfc 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -29,7 +29,6 @@ import { NotificationMessageBuilder, INotificationMessageBuilder } from "../serv import axios from "axios"; import got from "got"; import ping from "ping"; -import https from "https"; import Docker from "dockerode"; import net from "net"; import fs from "fs"; @@ -83,6 +82,7 @@ import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvi import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; import { PortProvider } from "@/service/infrastructure/network/PortProvider.js"; import { GameProvider } from "@/service/infrastructure/network/GameProvider.js"; +import { GrpcProvider } from "@/service/infrastructure/network/GrpcProvider.js"; export type InitializedServices = { settingsService: any; @@ -161,29 +161,19 @@ export const initializeServices = async ({ const dockerProvider = new DockerProvider(logger, Docker); const portProvider = new PortProvider(net); const gameProvider = new GameProvider(logger, GameDig); + const grpcProvider = new GrpcProvider(grpc, protoLoader); const networkService = new NetworkService( axios, - got, - https, - jmespath, - GameDig as unknown as { - query: (options: { type: string; host: string; port?: number }) => Promise<{ ping?: number } & { [key: string]: unknown }>; - }, - ping, logger, - Docker, - net, - settingsService, - grpc, - protoLoader, pingProvider, httpProvider, pageSpeedProvider, hardwareProvider, dockerProvider, portProvider, - gameProvider + gameProvider, + grpcProvider ); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); diff --git a/server/src/service/infrastructure/network/GrpcProvider.ts b/server/src/service/infrastructure/network/GrpcProvider.ts index 4bf127785..66684ba0c 100644 --- a/server/src/service/infrastructure/network/GrpcProvider.ts +++ b/server/src/service/infrastructure/network/GrpcProvider.ts @@ -2,29 +2,21 @@ import { IStatusProvider } from "@/service/infrastructure/network/IStatusProvide import { GrpcStatusPayload, MonitorStatusResponse } from "@/types/network.js"; import { Monitor, MonitorType } from "@/types/monitor.js"; import { AppError } from "@/utils/AppError.js"; -import { NETWORK_ERROR, timeRequest } from "@/service/infrastructure/network/utils.js"; -import { ILogger } from "@/utils/logger.js"; +import { timeRequest } from "@/service/infrastructure/network/utils.js"; import * as grpc from "@grpc/grpc-js"; import * as protoLoader from "@grpc/proto-loader"; import path from "path"; import { fileURLToPath } from "url"; -import { Network } from "inspector/promises"; type GrpcType = typeof grpc; type ProtoLoaderType = typeof protoLoader; const SERVICE_NAME = "GrpcProvider"; -export interface GrpcError extends Error { - code: number; - details?: string; -} - export class GrpcProvider implements IStatusProvider { readonly type = "grpc"; constructor( - private logger: ILogger, private grpc: GrpcType, private protoLoader: ProtoLoaderType ) {} @@ -33,10 +25,6 @@ export class GrpcProvider implements IStatusProvider { return type === "grpc"; } - private isGrpcError(error: unknown): error is GrpcError { - return error instanceof Error && "code" in error && typeof (error as Record).code === "number"; - } - private getGrpcStatusName(code: number): string { const statusNames: Record = { 0: "OK", @@ -61,15 +49,20 @@ export class GrpcProvider implements IStatusProvider { } async handle(monitor: Monitor): Promise> { - const { id, teamId, type, url, port, ignoreTlsErrors } = monitor; - const grpcServiceName = monitor.grpcServiceName || ""; - const host = url?.replace(/^https?:\/\//, "").split(/[/?#:]/)[0]; - try { - if (!host || !port) { - throw new Error("Valid host and port are required"); + const { id, teamId, type, url, port, ignoreTlsErrors } = monitor; + const grpcServiceName = monitor.grpcServiceName || ""; + + if (!url) { + throw new AppError({ message: "Monitor host is required", service: SERVICE_NAME, method: "handle" }); } + if (!port) { + throw new AppError({ message: "Monitor port is required", service: SERVICE_NAME, method: "handle" }); + } + + const host = url?.replace(/^https?:\/\//, "").split(/[/?#:]/)[0]; const target = `${host}:${port}`; + const currentFilePath = fileURLToPath(import.meta.url); const protoPath = path.join(path.dirname(currentFilePath), "protos", "health.proto"); const packageDefinition = this.protoLoader.loadSync(protoPath, { @@ -79,8 +72,23 @@ export class GrpcProvider implements IStatusProvider { defaults: true, oneofs: true, }); - const grpcObject = this.grpc.loadPackageDefinition(packageDefinition) as any; - const HealthClient = grpcObject.grpc.health.v1.Health; + const grpcObject = this.grpc.loadPackageDefinition(packageDefinition) as unknown as { + grpc: { + health: { + v1: { + Health: new ( + target: string, + credentials: unknown + ) => { + Check: (request: { service: string }, options: { deadline: Date }, callback: (err: unknown, response: unknown) => void) => void; + close: () => void; + }; + }; + }; + }; + }; + const healthService = grpcObject.grpc.health.v1.Health; + let credentials; if (ignoreTlsErrors) { credentials = this.grpc.credentials.createSsl(null, null, null, { @@ -89,8 +97,12 @@ export class GrpcProvider implements IStatusProvider { } else { credentials = this.grpc.credentials.createInsecure(); } - const client = new HealthClient(target, credentials); - const deadline = new Date(Date.now() + 10000); // 10s timeout + + const client = new healthService(target, credentials); + + const TIMEOUT_MS = 10000; + const deadline = new Date(Date.now() + TIMEOUT_MS); + const { response, responseTime, error } = await timeRequest(() => { return new Promise((resolve, reject) => { client.Check({ service: grpcServiceName }, { deadline }, (err: unknown, response: unknown) => { @@ -128,51 +140,32 @@ export class GrpcProvider implements IStatusProvider { }); if (error) { - let grpcCode = -1; - let message = error instanceof Error ? error.message : "gRPC health check failed"; - - if (this.isGrpcError(error)) { - grpcCode = error.code; - message = error.details || error.message; - } - + const grpcError = error as AppError & { grpcPayload?: GrpcStatusPayload; grpcCode?: number }; + const payload = grpcError.grpcPayload; return { monitorId: id, teamId: teamId, type: type, status: false, - code: 500, - message: message, + code: grpcError.grpcCode ?? 5000, + message: grpcError.message ?? "gRPC health check failed", responseTime, - payload: { - grpcStatusCode: grpcCode, - grpcStatusName: this.getGrpcStatusName(grpcCode), - serviceName: grpcServiceName, - servingStatus: "UNKNOWN", - }, + payload: payload ?? null, }; } - if (!response) { - throw new Error("No response received from gRPC health check"); - } - - const isServing = response.servingStatus === "SERVING"; + const grpcPayload = response as GrpcStatusPayload; + const isServing = grpcPayload.servingStatus === "SERVING"; return { monitorId: id, teamId: teamId, type: type, status: isServing, - code: isServing ? 200 : NETWORK_ERROR, - message: isServing ? "gRPC health check successful" : "gRPC health check failed", + code: isServing ? 200 : 5000, + message: isServing ? `gRPC service healthy (${grpcPayload.servingStatus})` : `gRPC service unhealthy (${grpcPayload.servingStatus})`, responseTime, - payload: { - grpcStatusCode: isServing ? 200 : NETWORK_ERROR, - grpcStatusName: response.grpcStatusName, - serviceName: response.serviceName, - servingStatus: response.servingStatus || "UNKNOWN", - }, + payload: grpcPayload, }; } catch (err: unknown) { const originalMessage = err instanceof Error ? err.message : String(err); diff --git a/server/src/service/infrastructure/network/HttpProvider.ts b/server/src/service/infrastructure/network/HttpProvider.ts index e373e2cff..42fa39a40 100644 --- a/server/src/service/infrastructure/network/HttpProvider.ts +++ b/server/src/service/infrastructure/network/HttpProvider.ts @@ -6,6 +6,7 @@ import { MonitorStatusResponse } from "@/types/network.js"; import { Agent as HttpsAgent } from "https"; import { Monitor, MonitorType } from "@/types/monitor.js"; import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; +import CacheableLookup from "cacheable-lookup"; export class HttpProvider implements IStatusProvider { readonly type = "http"; @@ -13,7 +14,16 @@ export class HttpProvider implements IStatusProvider { constructor( private got: Got, private advancedMatcher: AdvancedMatcher - ) {} + ) { + const cacheable = new CacheableLookup(); + this.got = got.extend({ + dnsCache: cacheable, + timeout: { + request: 30000, + }, + retry: { limit: 1 }, + }); + } supports(type: MonitorType) { return type === "http"; diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 1a0d71553..7b7c739d8 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -1,5 +1,4 @@ -import { HTTPError, RequestError } from "got"; -import type { Got, Response } from "got"; +import type { Got } from "got"; import type { Monitor, MonitorStatusResponse, @@ -14,28 +13,12 @@ import type { } from "@/types/index.js"; import type { AxiosStatic } from "axios"; import { AppError } from "@/utils/AppError.js"; -import path from "path"; -import { fileURLToPath } from "url"; -import { MonitorStatusResponseOverrides } from "@/types/index.js"; -import CacheableLookup from "cacheable-lookup"; import { ISettingsService } from "../system/settingsService.js"; import { ILogger } from "@/utils/logger.js"; import { IStatusProvider } from "./network/IStatusProvider.js"; const SERVICE_NAME = "NetworkService"; -interface BuildStatusResponseArgs { - monitor: Monitor; - response?: Response | null; - error?: Error | RequestError | HTTPError | null; - payload?: T | null; - jsonPath?: string; - matchMethod?: MonitorStatusResponse["matchMethod"]; - expectedValue?: string; - extracted?: unknown; - overrides?: MonitorStatusResponseOverrides; -} - export interface INetworkService { readonly serviceName: string; requestStatus( @@ -82,19 +65,7 @@ class NetworkService implements INetworkService { private PING_ERROR: number; private axios: AxiosStatic; - private got: Got; - private https: typeof import("https"); - private jmespath: { search: (data: unknown, expression: string) => unknown }; - private GameDig: { query: (options: { type: string; host: string; port?: number }) => Promise<{ ping?: number } & { [key: string]: unknown }> }; - private ping: typeof import("ping"); private logger: ILogger; - private Docker: typeof import("dockerode"); - private net: typeof import("net"); - private settingsService: ISettingsService; - private grpc: typeof import("@grpc/grpc-js"); - private protoLoader: typeof import("@grpc/proto-loader"); - - // New providers private pingProvider; private httpProvider; private pageSpeedProvider; @@ -102,83 +73,19 @@ class NetworkService implements INetworkService { private dockerProvider; private portProvider; private gameProvider; - - private buildStatusResponse = ({ - monitor, - response, - error, - payload, - jsonPath, - matchMethod, - expectedValue, - extracted, - overrides, - }: BuildStatusResponseArgs): MonitorStatusResponse => { - if (error) { - const statusResponse: MonitorStatusResponse = { - monitorId: monitor.id, - teamId: monitor.teamId, - type: monitor.type, - status: false, - code: this.NETWORK_ERROR, - message: error.message ?? "Network error", - responseTime: 0, - timings: undefined, - jsonPath, - matchMethod, - expectedValue, - extracted, - payload, - }; - if (error instanceof HTTPError || error instanceof RequestError) { - statusResponse.code = error?.response?.statusCode ?? this.NETWORK_ERROR; - statusResponse.message = error.message; - statusResponse.responseTime = error.timings?.phases?.total ?? 0; - statusResponse.timings = error.timings; - } - return { ...statusResponse, ...(overrides ?? {}) } as MonitorStatusResponse; - } - - return { - monitorId: monitor.id, - teamId: monitor.teamId, - type: monitor.type, - status: response?.ok ?? false, - code: response?.statusCode ?? this.NETWORK_ERROR, - message: response?.statusMessage ?? "", - responseTime: response?.timings?.phases?.total ?? 0, - timings: response?.timings, - payload: payload ?? response?.body, - jsonPath, - matchMethod, - expectedValue, - extracted, - ...(overrides ?? {}), - } as MonitorStatusResponse; - }; + private grpcProvider; constructor( axios: AxiosStatic, - got: Got, - https: typeof import("https"), - jmespath: { search: (data: unknown, expression: string) => unknown }, - GameDig: { query: (options: { type: string; host: string; port?: number }) => Promise<{ ping?: number } & { [key: string]: unknown }> }, - ping: typeof import("ping"), logger: ILogger, - Docker: typeof import("dockerode"), - net: typeof import("net"), - settingsService: ISettingsService, - grpc: typeof import("@grpc/grpc-js"), - protoLoader: typeof import("@grpc/proto-loader"), - - // New providers pingProvider: IStatusProvider, httpProvider: IStatusProvider, pagespeedProvider: IStatusProvider, hardwareProvider: IStatusProvider, dockerProvider: IStatusProvider, portProvider: IStatusProvider, - gameProvider: IStatusProvider + gameProvider: IStatusProvider, + grpcProvider: IStatusProvider ) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -192,18 +99,8 @@ class NetworkService implements INetworkService { this.NETWORK_ERROR = 5000; this.PING_ERROR = 5001; this.axios = axios; - this.https = https; - this.jmespath = jmespath; - this.GameDig = GameDig; - this.ping = ping; this.logger = logger; - this.Docker = Docker; - this.net = net; - this.settingsService = settingsService; - this.grpc = grpc; - this.protoLoader = protoLoader; - // New providers this.pingProvider = pingProvider; this.httpProvider = httpProvider; this.pageSpeedProvider = pagespeedProvider; @@ -211,34 +108,13 @@ class NetworkService implements INetworkService { this.dockerProvider = dockerProvider; this.portProvider = portProvider; this.gameProvider = gameProvider; - const cacheable = new CacheableLookup(); - - this.got = got.extend({ - dnsCache: cacheable, - timeout: { - request: 30000, - }, - retry: { limit: 1 }, - }); + this.grpcProvider = grpcProvider; } get serviceName(): string { return NetworkService.SERVICE_NAME; } - // Helper functions - private async timeRequest(operation: () => Promise): Promise<{ response: T | null; responseTime: number; error: unknown }> { - const start = process.hrtime.bigint(); - try { - const response = await operation(); - const elapsedMs = Math.round(Number(process.hrtime.bigint() - start) / 1_000_000); - return { response, responseTime: elapsedMs, error: null }; - } catch (error) { - const elapsedMs = Math.round(Number(process.hrtime.bigint() - start) / 1_000_000); - return { response: null, responseTime: elapsedMs, error }; - } - } - // Main entry point async requestStatus( monitor: Monitor @@ -271,169 +147,12 @@ class NetworkService implements INetworkService { case this.TYPE_GAME: return await this.gameProvider.handle(monitor); case this.TYPE_GRPC: - return await this.requestGrpc(monitor); + return await this.grpcProvider.handle(monitor); default: return this.handleUnsupportedType(type); } } - private async requestGrpc(monitor: Monitor): Promise> { - try { - const { url, port, ignoreTlsErrors } = monitor; - const grpcServiceName = monitor.grpcServiceName || ""; - - if (!url) { - throw new AppError({ message: "Monitor host is required", service: this.SERVICE_NAME, method: "requestGrpc" }); - } - if (!port) { - throw new AppError({ message: "Monitor port is required", service: this.SERVICE_NAME, method: "requestGrpc" }); - } - - const target = `${url}:${port}`; - - const currentFilePath = fileURLToPath(import.meta.url); - const protoPath = path.join(path.dirname(currentFilePath), "protos", "health.proto"); - - const packageDefinition = this.protoLoader.loadSync(protoPath, { - keepCase: true, - longs: String, - enums: String, - defaults: true, - oneofs: true, - }); - const grpcObject = this.grpc.loadPackageDefinition(packageDefinition) as unknown as { - grpc: { - health: { - v1: { - Health: new ( - target: string, - credentials: unknown - ) => { - Check: (request: { service: string }, options: { deadline: Date }, callback: (err: unknown, response: unknown) => void) => void; - close: () => void; - }; - }; - }; - }; - }; - const healthService = grpcObject.grpc.health.v1.Health; - - let credentials; - if (ignoreTlsErrors) { - credentials = this.grpc.credentials.createSsl(null, null, null, { - checkServerIdentity: () => undefined, - }); - } else { - credentials = this.grpc.credentials.createInsecure(); - } - - const client = new healthService(target, credentials); - - const TIMEOUT_MS = 10000; - const deadline = new Date(Date.now() + TIMEOUT_MS); - - const grpcResponse = this.buildStatusResponse({ - monitor, - overrides: { - status: false, - code: this.NETWORK_ERROR, - message: "gRPC health check not executed", - }, - }); - - const { response, responseTime, error } = await this.timeRequest(() => { - return new Promise((resolve, reject) => { - client.Check({ service: grpcServiceName }, { deadline }, (err: unknown, response: unknown) => { - client.close(); - - if (err) { - const grpcErr = err as { code?: number; details?: string; message?: string }; - const payload: GrpcStatusPayload = { - grpcStatusCode: grpcErr.code ?? -1, - grpcStatusName: this.getGrpcStatusName(grpcErr.code ?? -1), - serviceName: grpcServiceName, - servingStatus: "UNKNOWN", - }; - const grpcError = new AppError({ - message: grpcErr.details || grpcErr.message || "gRPC error", - service: this.SERVICE_NAME, - method: "requestGrpc", - }) as AppError & { grpcPayload?: GrpcStatusPayload; grpcCode?: number }; - grpcError.grpcPayload = payload; - grpcError.grpcCode = grpcErr.code; - reject(grpcError); - return; - } - - const resp = response as { status?: string } | undefined; - const servingStatus = resp?.status ?? "UNKNOWN"; - resolve({ - grpcStatusCode: 0, - grpcStatusName: "OK", - serviceName: grpcServiceName, - servingStatus, - }); - }); - }); - }); - - if (error) { - const grpcError = error as AppError & { grpcPayload?: GrpcStatusPayload; grpcCode?: number }; - const payload = grpcError.grpcPayload; - grpcResponse.status = false; - grpcResponse.code = grpcError.grpcCode ?? this.NETWORK_ERROR; - grpcResponse.message = grpcError.message ?? "gRPC health check failed"; - grpcResponse.responseTime = responseTime; - grpcResponse.payload = payload ?? null; - return grpcResponse; - } - - const grpcPayload = response as GrpcStatusPayload; - const isServing = grpcPayload.servingStatus === "SERVING"; - - grpcResponse.status = isServing; - grpcResponse.code = isServing ? 200 : this.NETWORK_ERROR; - grpcResponse.message = isServing - ? `gRPC service healthy (${grpcPayload.servingStatus})` - : `gRPC service unhealthy (${grpcPayload.servingStatus})`; - grpcResponse.responseTime = responseTime; - grpcResponse.payload = grpcPayload; - - return grpcResponse; - } catch (err: unknown) { - const originalMessage = err instanceof Error ? err.message : String(err); - throw new AppError({ - message: originalMessage || "Error performing gRPC health check", - service: this.SERVICE_NAME, - method: "requestGrpc", - details: { url: monitor.url, port: monitor.port, grpcServiceName: monitor.grpcServiceName }, - }); - } - } - - private getGrpcStatusName(code: number): string { - const statusNames: Record = { - 0: "OK", - 1: "CANCELLED", - 2: "UNKNOWN", - 3: "INVALID_ARGUMENT", - 4: "DEADLINE_EXCEEDED", - 5: "NOT_FOUND", - 6: "ALREADY_EXISTS", - 7: "PERMISSION_DENIED", - 8: "RESOURCE_EXHAUSTED", - 9: "FAILED_PRECONDITION", - 10: "ABORTED", - 11: "OUT_OF_RANGE", - 12: "UNIMPLEMENTED", - 13: "INTERNAL", - 14: "UNAVAILABLE", - 15: "DATA_LOSS", - 16: "UNAUTHENTICATED", - }; - return statusNames[code] || "UNKNOWN"; - } - private async handleUnsupportedType(type: string): Promise { return { monitorId: "unknown", From f8c0b57d212882ed36c9d5317f43d05343aa82d5 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 19:37:32 +0000 Subject: [PATCH 12/16] remove unused imports --- server/src/service/infrastructure/networkService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 7b7c739d8..2d6c3fe08 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -1,4 +1,3 @@ -import type { Got } from "got"; import type { Monitor, MonitorStatusResponse, @@ -14,7 +13,6 @@ import type { import type { AxiosStatic } from "axios"; import { AppError } from "@/utils/AppError.js"; -import { ISettingsService } from "../system/settingsService.js"; import { ILogger } from "@/utils/logger.js"; import { IStatusProvider } from "./network/IStatusProvider.js"; const SERVICE_NAME = "NetworkService"; From c5c9a1a88b4f7681986de0effe330b374e1ada76 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 13:57:39 -0800 Subject: [PATCH 13/16] fix missing await --- server/src/service/infrastructure/emailService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/service/infrastructure/emailService.ts b/server/src/service/infrastructure/emailService.ts index 0840d5a4c..5246351c0 100755 --- a/server/src/service/infrastructure/emailService.ts +++ b/server/src/service/infrastructure/emailService.ts @@ -94,7 +94,7 @@ class EmailService implements IEmailService { if (!mjml) { throw new Error(`Template ${template} not found`); } - const html = this.mjml2html(mjml); + const html = await this.mjml2html(mjml); return html.html; } catch (error: unknown) { this.logger.error({ From 69e07975e35872c47cafe097fbb811a3b40811bb Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 13:57:45 -0800 Subject: [PATCH 14/16] bump dependencies --- client/package-lock.json | 308 ++++----- client/package.json | 2 +- server/package-lock.json | 1387 +++++++++++++++++++------------------- server/package.json | 4 +- 4 files changed, 841 insertions(+), 860 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 338de1e26..e1dfadad5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -58,7 +58,7 @@ "lint-staged": "^16.2.7", "prettier": "^3.3.3", "typescript": "5.9.3", - "vite": "6.3.6" + "vite": "6.4.1" } }, "node_modules/@babel/code-frame": { @@ -83,6 +83,7 @@ "node_modules/@babel/core": { "version": "7.29.0", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -353,6 +354,7 @@ "node_modules/@emotion/react": { "version": "11.14.0", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -390,6 +392,7 @@ "node_modules/@emotion/styled": { "version": "11.14.1", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -434,7 +437,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -451,7 +453,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -468,7 +469,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -485,7 +485,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -502,7 +501,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -519,7 +517,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -536,7 +533,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -553,7 +549,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -570,7 +565,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -587,7 +581,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -604,7 +597,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -621,7 +613,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -638,7 +629,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -655,7 +645,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -672,7 +661,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -689,7 +677,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -704,7 +691,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -721,7 +707,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -738,7 +723,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -755,7 +739,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -772,7 +755,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -789,7 +771,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -806,7 +787,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -823,7 +803,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -840,7 +819,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -857,7 +835,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1185,6 +1162,7 @@ "node_modules/@mui/material": { "version": "7.3.7", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/core-downloads-tracker": "^7.3.7", @@ -1289,6 +1267,7 @@ "node_modules/@mui/system": { "version": "7.3.7", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/private-theming": "^7.3.7", @@ -1660,13 +1639,12 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1674,13 +1652,12 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1688,13 +1665,12 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1702,13 +1678,12 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1716,13 +1691,12 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1730,13 +1704,12 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1744,13 +1717,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1758,13 +1730,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1772,13 +1743,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1786,13 +1756,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1800,13 +1769,12 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1814,13 +1782,12 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1828,13 +1795,12 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1842,13 +1808,12 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1856,13 +1821,12 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1870,13 +1834,12 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1884,13 +1847,12 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1898,11 +1860,12 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1910,13 +1873,12 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1924,13 +1886,12 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1938,13 +1899,12 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1952,13 +1912,12 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1966,13 +1925,12 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1980,13 +1938,12 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1994,13 +1951,12 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2350,8 +2306,9 @@ }, "node_modules/@types/node": { "version": "24.5.2", - "dev": true, + "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.12.0" } @@ -2366,8 +2323,8 @@ }, "node_modules/@types/react": { "version": "18.3.27", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2489,6 +2446,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2505,7 +2463,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -2811,6 +2771,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3242,7 +3203,8 @@ }, "node_modules/dayjs": { "version": "1.11.13", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/debug": { "version": "4.4.3", @@ -3566,7 +3528,6 @@ }, "node_modules/esbuild": { "version": "0.25.12", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3625,6 +3586,7 @@ "version": "8.57.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3884,7 +3846,6 @@ }, "node_modules/fdir": { "version": "6.5.0", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -4018,7 +3979,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4371,6 +4331,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.27.6" }, @@ -5154,6 +5115,7 @@ "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.19.0.tgz", "integrity": "sha512-REhYUN8gNP3HlcIZS6QU2uy8iovl31cXsrNDkCcqWSQbCkcpdYLczqDz5PVIwNH42UQNyvukjes/RoHPDrOUmQ==", "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -5251,7 +5213,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -5317,7 +5281,6 @@ }, "node_modules/nanoid": { "version": "3.3.11", - "dev": true, "funding": [ { "type": "github", @@ -5650,7 +5613,6 @@ }, "node_modules/postcss": { "version": "8.5.6", - "dev": true, "funding": [ { "type": "opencollective", @@ -5801,6 +5763,7 @@ "node_modules/react-dom": { "version": "18.3.1", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5812,6 +5775,7 @@ "node_modules/react-hook-form": { "version": "7.71.1", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6011,7 +5975,8 @@ }, "node_modules/redux": { "version": "5.0.1", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-persist": { "version": "6.0.0", @@ -6157,9 +6122,11 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.57.1", - "dev": true, + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6171,31 +6138,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -6530,7 +6497,6 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6818,7 +6784,6 @@ }, "node_modules/tinyglobby": { "version": "0.2.15", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6948,8 +6913,9 @@ }, "node_modules/typescript": { "version": "5.9.3", - "dev": true, + "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6992,7 +6958,7 @@ }, "node_modules/undici-types": { "version": "7.12.0", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/union-value": { @@ -7081,9 +7047,11 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "dev": true, + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -7366,7 +7334,7 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/client/package.json b/client/package.json index dd585636b..ba2d59275 100644 --- a/client/package.json +++ b/client/package.json @@ -66,6 +66,6 @@ "lint-staged": "^16.2.7", "prettier": "^3.3.3", "typescript": "5.9.3", - "vite": "6.3.6" + "vite": "6.4.1" } } diff --git a/server/package-lock.json b/server/package-lock.json index ced3a5d0e..889c08f15 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -23,7 +23,7 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "express-rate-limit": "8.0.1", - "gamedig": "^5.3.1", + "gamedig": "5.2.0", "got": "14.4.7", "handlebars": "^4.7.8", "helmet": "^8.0.0", @@ -36,7 +36,7 @@ "mjml": "^5.0.0-alpha.4", "mongoose": "^8.3.3", "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.14", + "nodemailer": "8.0.1", "ping": "0.4.4", "sharp": "0.33.5", "ssl-checker": "2.0.10", @@ -262,558 +262,530 @@ } }, "node_modules/@aws-sdk/client-sesv2": { - "version": "3.917.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.917.0.tgz", - "integrity": "sha512-Jwz+3rO9fc2H8JyF4ainL5x06tiQ0n4ewKIpdUeGIauKdndMItYP1aeM0Y8wJQPTF1XEpWFC36cTSDgk/eypxA==", + "version": "3.1003.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.1003.0.tgz", + "integrity": "sha512-NR1rfYBcT4QWMG+VN2CTgbtWpf8I6aYsrU81hslucQd+z9O0qHMzZ3KHEdIcT9+PGYtqDwqhxUArboF7G09Oiw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.916.0", - "@aws-sdk/credential-provider-node": "3.917.0", - "@aws-sdk/middleware-host-header": "3.914.0", - "@aws-sdk/middleware-logger": "3.914.0", - "@aws-sdk/middleware-recursion-detection": "3.914.0", - "@aws-sdk/middleware-user-agent": "3.916.0", - "@aws-sdk/region-config-resolver": "3.914.0", - "@aws-sdk/signature-v4-multi-region": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@aws-sdk/util-endpoints": "3.916.0", - "@aws-sdk/util-user-agent-browser": "3.914.0", - "@aws-sdk/util-user-agent-node": "3.916.0", - "@smithy/config-resolver": "^4.4.0", - "@smithy/core": "^3.17.1", - "@smithy/fetch-http-handler": "^5.3.4", - "@smithy/hash-node": "^4.2.3", - "@smithy/invalid-dependency": "^4.2.3", - "@smithy/middleware-content-length": "^4.2.3", - "@smithy/middleware-endpoint": "^4.3.5", - "@smithy/middleware-retry": "^4.4.5", - "@smithy/middleware-serde": "^4.2.3", - "@smithy/middleware-stack": "^4.2.3", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/node-http-handler": "^4.4.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.4", - "@smithy/util-defaults-mode-node": "^4.2.6", - "@smithy/util-endpoints": "^3.2.3", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-retry": "^4.2.3", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/credential-provider-node": "^3.972.17", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.18", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/signature-v4-multi-region": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.3", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.8", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.22", + "@smithy/middleware-retry": "^4.4.39", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.38", + "@smithy/util-defaults-mode-node": "^4.2.41", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.916.0.tgz", - "integrity": "sha512-Eu4PtEUL1MyRvboQnoq5YKg0Z9vAni3ccebykJy615xokVZUdA3di2YxHM/hykDQX7lcUC62q9fVIvh0+UNk/w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.916.0", - "@aws-sdk/middleware-host-header": "3.914.0", - "@aws-sdk/middleware-logger": "3.914.0", - "@aws-sdk/middleware-recursion-detection": "3.914.0", - "@aws-sdk/middleware-user-agent": "3.916.0", - "@aws-sdk/region-config-resolver": "3.914.0", - "@aws-sdk/types": "3.914.0", - "@aws-sdk/util-endpoints": "3.916.0", - "@aws-sdk/util-user-agent-browser": "3.914.0", - "@aws-sdk/util-user-agent-node": "3.916.0", - "@smithy/config-resolver": "^4.4.0", - "@smithy/core": "^3.17.1", - "@smithy/fetch-http-handler": "^5.3.4", - "@smithy/hash-node": "^4.2.3", - "@smithy/invalid-dependency": "^4.2.3", - "@smithy/middleware-content-length": "^4.2.3", - "@smithy/middleware-endpoint": "^4.3.5", - "@smithy/middleware-retry": "^4.4.5", - "@smithy/middleware-serde": "^4.2.3", - "@smithy/middleware-stack": "^4.2.3", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/node-http-handler": "^4.4.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.4", - "@smithy/util-defaults-mode-node": "^4.2.6", - "@smithy/util-endpoints": "^3.2.3", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-retry": "^4.2.3", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/core": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.916.0.tgz", - "integrity": "sha512-1JHE5s6MD5PKGovmx/F1e01hUbds/1y3X8rD+Gvi/gWVfdg5noO7ZCerpRsWgfzgvCMZC9VicopBqNHCKLykZA==", + "version": "3.973.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.18.tgz", + "integrity": "sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@aws-sdk/xml-builder": "3.914.0", - "@smithy/core": "^3.17.1", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/signature-v4": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/xml-builder": "^3.972.10", + "@smithy/core": "^3.23.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.916.0.tgz", - "integrity": "sha512-3gDeqOXcBRXGHScc6xb7358Lyf64NRG2P08g6Bu5mv1Vbg9PKDyCAZvhKLkG7hkdfAM8Yc6UJNhbFxr1ud/tCQ==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.16.tgz", + "integrity": "sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/property-provider": "^4.2.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.916.0.tgz", - "integrity": "sha512-NmooA5Z4/kPFJdsyoJgDxuqXC1C6oPMmreJjbOPqcwo6E/h2jxaG8utlQFgXe5F9FeJsMx668dtxVxSYnAAqHQ==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.18.tgz", + "integrity": "sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/fetch-http-handler": "^5.3.4", - "@smithy/node-http-handler": "^4.4.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/util-stream": "^4.5.4", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.917.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.917.0.tgz", - "integrity": "sha512-rvQ0QamLySRq+Okc0ZqFHZ3Fbvj3tYuWNIlzyEKklNmw5X5PM1idYKlOJflY2dvUGkIqY3lUC9SC2WL+1s7KIw==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.16.tgz", + "integrity": "sha512-hzAnzNXKV0A4knFRWGu2NCt72P4WWxpEGnOc6H3DptUjC4oX3hGw846oN76M1rTHAOwDdbhjU0GAOWR4OUfTZg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/credential-provider-env": "3.916.0", - "@aws-sdk/credential-provider-http": "3.916.0", - "@aws-sdk/credential-provider-process": "3.916.0", - "@aws-sdk/credential-provider-sso": "3.916.0", - "@aws-sdk/credential-provider-web-identity": "3.917.0", - "@aws-sdk/nested-clients": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/credential-provider-imds": "^4.2.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/credential-provider-env": "^3.972.16", + "@aws-sdk/credential-provider-http": "^3.972.18", + "@aws-sdk/credential-provider-login": "^3.972.16", + "@aws-sdk/credential-provider-process": "^3.972.16", + "@aws-sdk/credential-provider-sso": "^3.972.16", + "@aws-sdk/credential-provider-web-identity": "^3.972.16", + "@aws-sdk/nested-clients": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.16.tgz", + "integrity": "sha512-VI0kXTlr0o1FTay+Jvx6AKqx5ECBgp7X4VevGBEbuXdCXnNp7SPU0KvjsOLVhIz3OoPK4/lTXphk43t0IVk65w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.917.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.917.0.tgz", - "integrity": "sha512-n7HUJ+TgU9wV/Z46yR1rqD9hUjfG50AKi+b5UXTlaDlVD8bckg40i77ROCllp53h32xQj/7H0yBIYyphwzLtmg==", + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.17.tgz", + "integrity": "sha512-98MAcQ2Dk7zkvgwZ5f6fLX2lTyptC3gTSDx4EpvTdJWET8qs9lBPYggoYx7GmKp/5uk0OwVl0hxIDZsDNS/Y9g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "3.916.0", - "@aws-sdk/credential-provider-http": "3.916.0", - "@aws-sdk/credential-provider-ini": "3.917.0", - "@aws-sdk/credential-provider-process": "3.916.0", - "@aws-sdk/credential-provider-sso": "3.916.0", - "@aws-sdk/credential-provider-web-identity": "3.917.0", - "@aws-sdk/types": "3.914.0", - "@smithy/credential-provider-imds": "^4.2.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/credential-provider-env": "^3.972.16", + "@aws-sdk/credential-provider-http": "^3.972.18", + "@aws-sdk/credential-provider-ini": "^3.972.16", + "@aws-sdk/credential-provider-process": "^3.972.16", + "@aws-sdk/credential-provider-sso": "^3.972.16", + "@aws-sdk/credential-provider-web-identity": "^3.972.16", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.916.0.tgz", - "integrity": "sha512-SXDyDvpJ1+WbotZDLJW1lqP6gYGaXfZJrgFSXIuZjHb75fKeNRgPkQX/wZDdUvCwdrscvxmtyJorp2sVYkMcvA==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.16.tgz", + "integrity": "sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.916.0.tgz", - "integrity": "sha512-gu9D+c+U/Dp1AKBcVxYHNNoZF9uD4wjAKYCjgSN37j4tDsazwMEylbbZLuRNuxfbXtizbo4/TiaxBXDbWM7AkQ==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.16.tgz", + "integrity": "sha512-b9of7tQgERxgcEcwAFWvRe84ivw+Kw6b3jVuz/6LQzonkomiY5UoWfprkbjc8FSCQ2VjDqKTvIRA9F0KSQ025w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.916.0", - "@aws-sdk/core": "3.916.0", - "@aws-sdk/token-providers": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.6", + "@aws-sdk/token-providers": "3.1003.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.917.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.917.0.tgz", - "integrity": "sha512-pZncQhFbwW04pB0jcD5OFv3x2gAddDYCVxyJVixgyhSw7bKCYxqu6ramfq1NxyVpmm+qsw+ijwi/3cCmhUHF/A==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.16.tgz", + "integrity": "sha512-PaOH5jFoPQX4WkqpKzKh9cM7rieKtbgEGqrZ+ybGmotJhcvhI/xl69yCwMbHGnpQJJmHZIX9q2zaPB7HTBn/4w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/nested-clients": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.914.0.tgz", - "integrity": "sha512-7r9ToySQ15+iIgXMF/h616PcQStByylVkCshmQqcdeynD/lCn2l667ynckxW4+ql0Q+Bo/URljuhJRxVJzydNA==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", + "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.914.0.tgz", - "integrity": "sha512-/gaW2VENS5vKvJbcE1umV4Ag3NuiVzpsANxtrqISxT3ovyro29o1RezW/Avz/6oJqjnmgz8soe9J1t65jJdiNg==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", + "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@smithy/types": "^4.8.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.914.0.tgz", - "integrity": "sha512-yiAjQKs5S2JKYc+GrkvGMwkUvhepXDigEXpSJqUseR/IrqHhvGNuOxDxq+8LbDhM4ajEW81wkiBbU+Jl9G82yQ==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", + "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@aws/lambda-invoke-store": "^0.0.1", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/types": "^3.973.5", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.916.0.tgz", - "integrity": "sha512-pjmzzjkEkpJObzmTthqJPq/P13KoNFuEi/x5PISlzJtHofCNcyXeVAQ90yvY2dQ6UXHf511Rh1/ytiKy2A8M0g==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.18.tgz", + "integrity": "sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.17.1", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/signature-v4": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-stream": "^4.5.4", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.8", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.916.0.tgz", - "integrity": "sha512-mzF5AdrpQXc2SOmAoaQeHpDFsK2GE6EGcEACeNuoESluPI2uYMpuuNMYrUufdnIAIyqgKlis0NVxiahA5jG42w==", + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.18.tgz", + "integrity": "sha512-KcqQDs/7WtoEnp52+879f8/i1XAJkgka5i4arOtOCPR10o4wWo3VRecDI9Gxoh6oghmLCnIiOSKyRcXI/50E+w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@aws-sdk/util-endpoints": "3.916.0", - "@smithy/core": "^3.17.1", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@smithy/core": "^3.23.8", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.916.0.tgz", - "integrity": "sha512-tgg8e8AnVAer0rcgeWucFJ/uNN67TbTiDHfD+zIOPKep0Z61mrHEoeT/X8WxGIOkEn4W6nMpmS4ii8P42rNtnA==", + "version": "3.996.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.6.tgz", + "integrity": "sha512-blNJ3ugn4gCQ9ZSZi/firzKCvVl5LvPFVxv24LprENeWI4R8UApG006UQkF4SkmLygKq2BQXRad2/anQ13Te4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.916.0", - "@aws-sdk/middleware-host-header": "3.914.0", - "@aws-sdk/middleware-logger": "3.914.0", - "@aws-sdk/middleware-recursion-detection": "3.914.0", - "@aws-sdk/middleware-user-agent": "3.916.0", - "@aws-sdk/region-config-resolver": "3.914.0", - "@aws-sdk/types": "3.914.0", - "@aws-sdk/util-endpoints": "3.916.0", - "@aws-sdk/util-user-agent-browser": "3.914.0", - "@aws-sdk/util-user-agent-node": "3.916.0", - "@smithy/config-resolver": "^4.4.0", - "@smithy/core": "^3.17.1", - "@smithy/fetch-http-handler": "^5.3.4", - "@smithy/hash-node": "^4.2.3", - "@smithy/invalid-dependency": "^4.2.3", - "@smithy/middleware-content-length": "^4.2.3", - "@smithy/middleware-endpoint": "^4.3.5", - "@smithy/middleware-retry": "^4.4.5", - "@smithy/middleware-serde": "^4.2.3", - "@smithy/middleware-stack": "^4.2.3", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/node-http-handler": "^4.4.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.4", - "@smithy/util-defaults-mode-node": "^4.2.6", - "@smithy/util-endpoints": "^3.2.3", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-retry": "^4.2.3", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.18", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.3", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.8", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.22", + "@smithy/middleware-retry": "^4.4.39", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.38", + "@smithy/util-defaults-mode-node": "^4.2.41", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.914.0.tgz", - "integrity": "sha512-KlmHhRbn1qdwXUdsdrJ7S/MAkkC1jLpQ11n+XvxUUUCGAJd1gjC7AjxPZUM7ieQ2zcb8bfEzIU7al+Q3ZT0u7Q==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", + "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@smithy/config-resolver": "^4.4.0", - "@smithy/types": "^4.8.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.916.0.tgz", - "integrity": "sha512-fuzUMo6xU7e0NBzBA6TQ4FUf1gqNbg4woBSvYfxRRsIfKmSMn9/elXXn4sAE5UKvlwVQmYnb6p7dpVRPyFvnQA==", + "version": "3.996.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.6.tgz", + "integrity": "sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/protocol-http": "^5.3.3", - "@smithy/signature-v4": "^5.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.916.0.tgz", - "integrity": "sha512-13GGOEgq5etbXulFCmYqhWtpcEQ6WI6U53dvXbheW0guut8fDFJZmEv7tKMTJgiybxh7JHd0rWcL9JQND8DwoQ==", + "version": "3.1003.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1003.0.tgz", + "integrity": "sha512-SOyyWNdT7njKRwtZ1JhwHlH1csv6Pkgf305X96/OIfnhq1pU/EjmT6W6por57rVrjrKuHBuEIXgpWv8OgoMHpg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.916.0", - "@aws-sdk/nested-clients": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/core": "^3.973.18", + "@aws-sdk/nested-clients": "^3.996.6", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.914.0.tgz", - "integrity": "sha512-kQWPsRDmom4yvAfyG6L1lMmlwnTzm1XwMHOU+G5IFlsP4YEaMtXidDzW/wiivY0QFrhfCz/4TVmu0a2aPU57ug==", + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", + "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", - "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.916.0.tgz", - "integrity": "sha512-bAgUQwvixdsiGNcuZSDAOWbyHlnPtg8G8TyHD6DTfTmKTHUW6tAn+af/ZYJPXEzXhhpwgJqi58vWnsiDhmr7NQ==", + "version": "3.996.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", + "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", - "@smithy/util-endpoints": "^3.2.3", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-endpoints": "^3.3.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", - "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.914.0.tgz", - "integrity": "sha512-rMQUrM1ECH4kmIwlGl9UB0BtbHy6ZuKdWFrIknu8yGTRI/saAucqNTh5EI1vWBxZ0ElhK5+g7zOnUuhSmVQYUA==", + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", + "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.914.0", - "@smithy/types": "^4.8.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.916.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.916.0.tgz", - "integrity": "sha512-CwfWV2ch6UdjuSV75ZU99N03seEUb31FIUrXBnwa6oONqj/xqXwrxtlUMLx6WH3OJEE4zI3zt5PjlTdGcVwf4g==", + "version": "3.973.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.3.tgz", + "integrity": "sha512-8s2cQmTUOwcBlIJyI9PAZNnnnF+cGtdhHc1yzMMsSD/GR/Hxj7m0IGUE92CslXXb8/p5Q76iqOCjN1GFwyf+1A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.916.0", - "@aws-sdk/types": "3.914.0", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/types": "^4.8.0", + "@aws-sdk/middleware-user-agent": "^3.972.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" @@ -825,24 +797,44 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.914.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.914.0.tgz", - "integrity": "sha512-k75evsBD5TcIjedycYS7QXQ98AmOtbnxRJOPtCo0IwYRmy7UvqgS/gBL5SmrIqeV6FDSYRQMgdBxSMp6MLmdew==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", + "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", - "fast-xml-parser": "5.2.5", + "@smithy/types": "^4.13.0", + "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder/node_modules/fast-xml-parser": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", + "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" } }, "node_modules/@aws/lambda-invoke-store": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", - "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3487,13 +3479,13 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.3.tgz", - "integrity": "sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.11.tgz", + "integrity": "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3501,17 +3493,17 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.0.tgz", - "integrity": "sha512-Kkmz3Mup2PGp/HNJxhCWkLNdlajJORLSjwkcfrj0E7nu6STAEdcMR1ir5P9/xOmncx8xXfru0fbUYLlZog/cFg==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.10.tgz", + "integrity": "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.3", - "@smithy/types": "^4.8.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.3", - "@smithy/util-middleware": "^4.2.3", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -3519,21 +3511,21 @@ } }, "node_modules/@smithy/core": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.17.1.tgz", - "integrity": "sha512-V4Qc2CIb5McABYfaGiIYLTmo/vwNIK7WXI5aGveBd9UcdhbOMwcvIMxIw/DJj1S9QgOMa/7FBkarMdIC0EOTEQ==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.8.tgz", + "integrity": "sha512-f7uPeBi7ehmLT4YF2u9j3qx6lSnurG1DLXOsTtJrIRNDF7VXio4BGHQ+SQteN/BrUVudbkuL4v7oOsRCzq4BqA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-stream": "^4.5.4", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -3541,16 +3533,16 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz", - "integrity": "sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.11.tgz", + "integrity": "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -3558,16 +3550,16 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz", - "integrity": "sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw==", + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.13.tgz", + "integrity": "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.3", - "@smithy/querystring-builder": "^4.2.3", - "@smithy/types": "^4.8.0", - "@smithy/util-base64": "^4.3.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -3575,15 +3567,15 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.3.tgz", - "integrity": "sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.11.tgz", + "integrity": "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3591,13 +3583,13 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz", - "integrity": "sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.11.tgz", + "integrity": "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3605,9 +3597,9 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3618,14 +3610,14 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz", - "integrity": "sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.11.tgz", + "integrity": "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3633,19 +3625,19 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.5.tgz", - "integrity": "sha512-SIzKVTvEudFWJbxAaq7f2GvP3jh2FHDpIFI6/VAf4FOWGFZy0vnYMPSRj8PGYI8Hjt29mvmwSRgKuO3bK4ixDw==", + "version": "4.4.22", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.22.tgz", + "integrity": "sha512-sc81w1o4Jy+/MAQlY3sQ8C7CmSpcvIi3TAzXblUv2hjG11BBSJi/Cw8vDx5BxMxapuH2I+Gc+45vWsgU07WZRQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.17.1", - "@smithy/middleware-serde": "^4.2.3", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", - "@smithy/url-parser": "^4.2.3", - "@smithy/util-middleware": "^4.2.3", + "@smithy/core": "^3.23.8", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" }, "engines": { @@ -3653,20 +3645,20 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.5.tgz", - "integrity": "sha512-DCaXbQqcZ4tONMvvdz+zccDE21sLcbwWoNqzPLFlZaxt1lDtOE2tlVpRSwcTOJrjJSUThdgEYn7HrX5oLGlK9A==", + "version": "4.4.39", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.39.tgz", + "integrity": "sha512-MCVCxaCzuZgiHtHGV2Ke44nh6t4+8/tO+rTYOzrr2+G4nMLU/qbzNCWKBX54lyEaVcGQrfOJiG2f8imtiw+nIQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/service-error-classification": "^4.2.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-retry": "^4.2.3", - "@smithy/uuid": "^1.1.0", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -3674,14 +3666,14 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz", - "integrity": "sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.12.tgz", + "integrity": "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3689,13 +3681,13 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz", - "integrity": "sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.11.tgz", + "integrity": "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3703,15 +3695,15 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz", - "integrity": "sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.11.tgz", + "integrity": "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.3", - "@smithy/shared-ini-file-loader": "^4.3.3", - "@smithy/types": "^4.8.0", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3719,16 +3711,16 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.3.tgz", - "integrity": "sha512-MAwltrDB0lZB/H6/2M5PIsISSwdI5yIh6DaBB9r0Flo9nx3y0dzl/qTMJPd7tJvPdsx6Ks/cwVzheGNYzXyNbQ==", + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.14.tgz", + "integrity": "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/querystring-builder": "^4.2.3", - "@smithy/types": "^4.8.0", + "@smithy/abort-controller": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3736,13 +3728,13 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.3.tgz", - "integrity": "sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.11.tgz", + "integrity": "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3750,13 +3742,13 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.3.tgz", - "integrity": "sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.11.tgz", + "integrity": "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3764,14 +3756,14 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz", - "integrity": "sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.11.tgz", + "integrity": "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", - "@smithy/util-uri-escape": "^4.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3779,13 +3771,13 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz", - "integrity": "sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.11.tgz", + "integrity": "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3793,26 +3785,26 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz", - "integrity": "sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.11.tgz", + "integrity": "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0" + "@smithy/types": "^4.13.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz", - "integrity": "sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.6.tgz", + "integrity": "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3820,19 +3812,19 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.3.tgz", - "integrity": "sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.11.tgz", + "integrity": "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.3", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3840,18 +3832,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.1.tgz", - "integrity": "sha512-Ngb95ryR5A9xqvQFT5mAmYkCwbXvoLavLFwmi7zVg/IowFPCfiqRfkOKnbc/ZRL8ZKJ4f+Tp6kSu6wjDQb8L/g==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.2.tgz", + "integrity": "sha512-HezY3UuG0k4T+4xhFKctLXCA5N2oN+Rtv+mmL8Gt7YmsUY2yhmcLyW75qrSzldfj75IsCW/4UhY3s20KcFnZqA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.17.1", - "@smithy/middleware-endpoint": "^4.3.5", - "@smithy/middleware-stack": "^4.2.3", - "@smithy/protocol-http": "^5.3.3", - "@smithy/types": "^4.8.0", - "@smithy/util-stream": "^4.5.4", + "@smithy/core": "^3.23.8", + "@smithy/middleware-endpoint": "^4.4.22", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" }, "engines": { @@ -3859,9 +3851,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.0.tgz", - "integrity": "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", + "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3872,14 +3864,14 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.3.tgz", - "integrity": "sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.11.tgz", + "integrity": "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.3", - "@smithy/types": "^4.8.0", + "@smithy/querystring-parser": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3887,14 +3879,14 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3902,9 +3894,9 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3915,9 +3907,9 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", - "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3928,13 +3920,13 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", + "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -3942,9 +3934,9 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3955,15 +3947,15 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.4.tgz", - "integrity": "sha512-qI5PJSW52rnutos8Bln8nwQZRpyoSRN6k2ajyoUHNMUzmWqHnOJCnDELJuV6m5PML0VkHI+XcXzdB+6awiqYUw==", + "version": "4.3.38", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.38.tgz", + "integrity": "sha512-c8P1mFLNxcsdAMabB8/VUQUbWzFmgujWi4bAXSggcqLYPc8V4U5abqFqOyn+dK4YT+q8UyCVkTO8807t4t2syA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3971,18 +3963,18 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.6.tgz", - "integrity": "sha512-c6M/ceBTm31YdcFpgfgQAJaw3KbaLuRKnAz91iMWFLSrgxRpYm03c3bu5cpYojNMfkV9arCUelelKA7XQT36SQ==", + "version": "4.2.41", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.41.tgz", + "integrity": "sha512-/UG+9MT3UZAR0fLzOtMJMfWGcjjHvgggq924x/CRy8vRbL+yFf3Z6vETlvq8vDH92+31P/1gSOFoo7303wN8WQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.4.0", - "@smithy/credential-provider-imds": "^4.2.3", - "@smithy/node-config-provider": "^4.3.3", - "@smithy/property-provider": "^4.2.3", - "@smithy/smithy-client": "^4.9.1", - "@smithy/types": "^4.8.0", + "@smithy/config-resolver": "^4.4.10", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.2", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -3990,14 +3982,14 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz", - "integrity": "sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.2.tgz", + "integrity": "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.3", - "@smithy/types": "^4.8.0", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -4005,9 +3997,9 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4018,13 +4010,13 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.3.tgz", - "integrity": "sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.11.tgz", + "integrity": "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.8.0", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -4032,14 +4024,14 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.3.tgz", - "integrity": "sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.11.tgz", + "integrity": "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.3", - "@smithy/types": "^4.8.0", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -4047,19 +4039,19 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.4.tgz", - "integrity": "sha512-+qDxSkiErejw1BAIXUFBSfM5xh3arbz1MmxlbMCKanDDZtVEQ7PSKW9FQS0Vud1eI/kYn0oCTVKyNzRlq+9MUw==", + "version": "4.5.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.17.tgz", + "integrity": "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.4", - "@smithy/node-http-handler": "^4.4.3", - "@smithy/types": "^4.8.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -4067,9 +4059,9 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4080,13 +4072,13 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -4094,9 +4086,9 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4735,6 +4727,7 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -5389,9 +5382,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -5546,13 +5539,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.0.tgz", - "integrity": "sha512-zt40Pz4zcRXra9CVV31KeyofwiNvAbJ5B6YPz9pMJ+yOSLikvPT4Yi5LjfgjRa9CawVYBaD1JQzIVcIvBejKeA==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -5879,9 +5872,9 @@ "license": "ISC" }, "node_modules/bowser": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", - "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "dev": true, "license": "MIT" }, @@ -7188,9 +7181,9 @@ "license": "MIT" }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -7316,10 +7309,13 @@ } }, "node_modules/dompurify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", - "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", "license": "(MPL-2.0 OR Apache-2.0)", + "engines": { + "node": ">=20" + }, "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -7391,14 +7387,6 @@ "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==", "license": "ISC" }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -8137,23 +8125,18 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } + "license": "MIT" }, "node_modules/fastq": { "version": "1.20.1", @@ -8343,9 +8326,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -8434,19 +8417,18 @@ } }, "node_modules/gamedig": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-5.3.2.tgz", - "integrity": "sha512-R2b1LwjW783PZsHRl9M8R06UkvJwXmJ6PDKk48UukJQ9ktiwrCeAk90MAZx6nF3oA444uf7r5eHjfaYbNoSV+Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-5.2.0.tgz", + "integrity": "sha512-YAk9KmQnbiThASSXYiL2LPeppgfthMjG8PIQK4UK8DveF/X/zhwYaUKqi+op05d0iLLoCzvx5J2Q2vUr+OVzQg==", "license": "MIT", "dependencies": { - "fast-xml-parser": "5.2.5", + "cheerio": "1.0.0-rc.12", "gbxremote": "0.2.1", "got": "13.0.0", - "iconv-lite": "0.7.0", - "long": "5.3.2", + "iconv-lite": "0.6.3", + "long": "5.2.3", "minimist": "1.2.8", "seek-bzip": "2.0.0", - "telnet-client": "2.2.6", "varint": "6.0.0" }, "bin": { @@ -8533,21 +8515,23 @@ } }, "node_modules/gamedig/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, + "node_modules/gamedig/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "license": "Apache-2.0" + }, "node_modules/gamedig/node_modules/p-cancelable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", @@ -8751,12 +8735,12 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10683,9 +10667,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -11116,9 +11100,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11236,12 +11220,12 @@ } }, "node_modules/mjml-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -11859,12 +11843,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, - "node_modules/net": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", - "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==", - "license": "MIT" - }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -11962,9 +11940,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", - "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.1.tgz", + "integrity": "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -12960,9 +12938,9 @@ } }, "node_modules/postcss-svgo/node_modules/svgo": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", - "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", "license": "MIT", "dependencies": { "commander": "^11.1.0", @@ -12971,7 +12949,7 @@ "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", - "sax": "^1.4.1" + "sax": "^1.5.0" }, "bin": { "svgo": "bin/svgo.js" @@ -13271,9 +13249,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -13640,9 +13618,9 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", + "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -14127,15 +14105,6 @@ "node": ">= 0.8" } }, - "node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "license": "MIT", - "dependencies": { - "emitter-component": "^1.1.1" - } - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -14376,9 +14345,10 @@ } }, "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "dev": true, "funding": [ { "type": "github", @@ -14473,6 +14443,63 @@ "node": ">=8" } }, + "node_modules/svgo": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", + "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", + "license": "MIT", + "optional": true, + "dependencies": { + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0", + "optional": true + }, "node_modules/swagger-ui-dist": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.0.tgz", @@ -14547,20 +14574,6 @@ "node": ">=6" } }, - "node_modules/telnet-client": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.6.tgz", - "integrity": "sha512-ZUYrLsPtQupQww3eSEORDVOb6ztdtKEghya6TVXPo2tg/UQq2pn5rHhvwuUvyYpbnsoqdNY1fyD1GNkXHR8dYA==", - "license": "MIT", - "dependencies": { - "net": "^1.0.2", - "stream": "^0.0.2" - }, - "funding": { - "type": "paypal", - "url": "https://paypal.me/kozjak" - } - }, "node_modules/test-exclude": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", @@ -14587,13 +14600,13 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -15035,9 +15048,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", - "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/server/package.json b/server/package.json index b94a1e852..ce2c0822e 100755 --- a/server/package.json +++ b/server/package.json @@ -38,7 +38,7 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "express-rate-limit": "8.0.1", - "gamedig": "^5.3.1", + "gamedig": "5.2.0", "got": "14.4.7", "handlebars": "^4.7.8", "helmet": "^8.0.0", @@ -51,7 +51,7 @@ "mjml": "^5.0.0-alpha.4", "mongoose": "^8.3.3", "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.14", + "nodemailer": "8.0.1", "ping": "0.4.4", "sharp": "0.33.5", "ssl-checker": "2.0.10", From 0718e63e2f9d1107e8b67b8692fbcc2d26c1bfce Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 14:11:11 -0800 Subject: [PATCH 15/16] cleanup network service --- server/src/config/services.ts | 8 +- .../infrastructure/network/PortProvider.ts | 1 - .../service/infrastructure/networkService.ts | 136 +++--------------- 3 files changed, 21 insertions(+), 124 deletions(-) diff --git a/server/src/config/services.ts b/server/src/config/services.ts index bd13b7dfc..f7746ef47 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -163,9 +163,7 @@ export const initializeServices = async ({ const gameProvider = new GameProvider(logger, GameDig); const grpcProvider = new GrpcProvider(grpc, protoLoader); - const networkService = new NetworkService( - axios, - logger, + const networkService = new NetworkService(axios, logger, [ pingProvider, httpProvider, pageSpeedProvider, @@ -173,8 +171,8 @@ export const initializeServices = async ({ dockerProvider, portProvider, gameProvider, - grpcProvider - ); + grpcProvider, + ]); const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); const notificationMessageBuilder = new NotificationMessageBuilder(); diff --git a/server/src/service/infrastructure/network/PortProvider.ts b/server/src/service/infrastructure/network/PortProvider.ts index 66fefd0fe..7c1c96ce4 100644 --- a/server/src/service/infrastructure/network/PortProvider.ts +++ b/server/src/service/infrastructure/network/PortProvider.ts @@ -51,7 +51,6 @@ export class PortProvider implements IStatusProvider { }); }); if (error) { - console.log(error); const errorMessage = error instanceof Error ? error.message : "Port check failed"; return { monitorId: monitor.id, diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index 2d6c3fe08..f9c79ac29 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -1,38 +1,14 @@ -import type { - Monitor, - MonitorStatusResponse, - GrpcStatusPayload, - PageSpeedStatusPayload, - HttpStatusPayload, - HardwareStatusPayload, - PingStatusPayload, - DockerStatusPayload, - PortStatusPayload, - GameStatusPayload, -} from "@/types/index.js"; +import type { Monitor, MonitorStatusResponse } from "@/types/index.js"; import type { AxiosStatic } from "axios"; import { AppError } from "@/utils/AppError.js"; - +import { NETWORK_ERROR } from "@/service/infrastructure/network/utils.js"; import { ILogger } from "@/utils/logger.js"; import { IStatusProvider } from "./network/IStatusProvider.js"; const SERVICE_NAME = "NetworkService"; export interface INetworkService { readonly serviceName: string; - requestStatus( - monitor: Monitor - ): Promise< - MonitorStatusResponse< - | PingStatusPayload - | HttpStatusPayload - | PageSpeedStatusPayload - | HardwareStatusPayload - | DockerStatusPayload - | PortStatusPayload - | GameStatusPayload - | GrpcStatusPayload - > - >; + requestStatus(monitor: Monitor): Promise>; requestWebhook( type: string, url: string, @@ -50,63 +26,16 @@ export interface INetworkService { class NetworkService implements INetworkService { static SERVICE_NAME = SERVICE_NAME; - private TYPE_PING: string; - private TYPE_HTTP: string; - private TYPE_PAGESPEED: string; - private TYPE_HARDWARE: string; - private TYPE_DOCKER: string; - private TYPE_PORT: string; - private TYPE_GAME: string; - private TYPE_GRPC: string; - private SERVICE_NAME: string; - private NETWORK_ERROR: number; - private PING_ERROR: number; - private axios: AxiosStatic; private logger: ILogger; - private pingProvider; - private httpProvider; - private pageSpeedProvider; - private hardwareProvider; - private dockerProvider; - private portProvider; - private gameProvider; - private grpcProvider; constructor( axios: AxiosStatic, logger: ILogger, - pingProvider: IStatusProvider, - httpProvider: IStatusProvider, - pagespeedProvider: IStatusProvider, - hardwareProvider: IStatusProvider, - dockerProvider: IStatusProvider, - portProvider: IStatusProvider, - gameProvider: IStatusProvider, - grpcProvider: IStatusProvider + private providers: IStatusProvider[] ) { - this.TYPE_PING = "ping"; - this.TYPE_HTTP = "http"; - this.TYPE_PAGESPEED = "pagespeed"; - this.TYPE_HARDWARE = "hardware"; - this.TYPE_DOCKER = "docker"; - this.TYPE_PORT = "port"; - this.TYPE_GAME = "game"; - this.TYPE_GRPC = "grpc"; - this.SERVICE_NAME = SERVICE_NAME; - this.NETWORK_ERROR = 5000; - this.PING_ERROR = 5001; this.axios = axios; this.logger = logger; - - this.pingProvider = pingProvider; - this.httpProvider = httpProvider; - this.pageSpeedProvider = pagespeedProvider; - this.hardwareProvider = hardwareProvider; - this.dockerProvider = dockerProvider; - this.portProvider = portProvider; - this.gameProvider = gameProvider; - this.grpcProvider = grpcProvider; } get serviceName(): string { @@ -114,41 +43,12 @@ class NetworkService implements INetworkService { } // Main entry point - async requestStatus( - monitor: Monitor - ): Promise< - MonitorStatusResponse< - | PingStatusPayload - | HttpStatusPayload - | PageSpeedStatusPayload - | HardwareStatusPayload - | DockerStatusPayload - | PortStatusPayload - | GameStatusPayload - | GrpcStatusPayload - > - > { - const type = monitor?.type || "unknown"; - switch (type) { - case this.TYPE_PING: - return await this.pingProvider.handle(monitor); - case this.TYPE_HTTP: - return await this.httpProvider.handle(monitor); - case this.TYPE_PAGESPEED: - return await this.pageSpeedProvider.handle(monitor); - case this.TYPE_HARDWARE: - return await this.hardwareProvider.handle(monitor); - case this.TYPE_DOCKER: - return await this.dockerProvider.handle(monitor); - case this.TYPE_PORT: - return await this.portProvider.handle(monitor); - case this.TYPE_GAME: - return await this.gameProvider.handle(monitor); - case this.TYPE_GRPC: - return await this.grpcProvider.handle(monitor); - default: - return this.handleUnsupportedType(type); + async requestStatus(monitor: Monitor) { + const provider = this.providers.find((p) => p.supports(monitor.type)); + if (!provider) { + return this.handleUnsupportedType(monitor.type); } + return provider.handle(monitor); } private async handleUnsupportedType(type: string): Promise { @@ -157,7 +57,7 @@ class NetworkService implements INetworkService { teamId: "unknown", type: "unknown", status: false, - code: this.NETWORK_ERROR, + code: NETWORK_ERROR, message: `Unsupported type: ${type}`, }; } @@ -181,7 +81,7 @@ class NetworkService implements INetworkService { } catch (err: unknown) { this.logger.warn({ message: err instanceof Error ? err.message : String(err), - service: this.SERVICE_NAME, + service: SERVICE_NAME, method: "requestWebhook", }); @@ -190,7 +90,7 @@ class NetworkService implements INetworkService { return { type: "webhook", status: false, - code: axiosError.response?.status ?? this.NETWORK_ERROR, + code: axiosError.response?.status ?? NETWORK_ERROR, message: `Failed to send ${type} notification`, payload: axiosError.response?.data, }; @@ -199,7 +99,7 @@ class NetworkService implements INetworkService { return { type: "webhook", status: false, - code: this.NETWORK_ERROR, + code: NETWORK_ERROR, message: `Failed to send ${type} notification`, }; } @@ -225,7 +125,7 @@ class NetworkService implements INetworkService { throw new AppError({ message: originalMessage || "Error sending PagerDuty notification", - service: this.SERVICE_NAME, + service: SERVICE_NAME, method: "requestPagerDuty", details: { responseData: err && typeof err === "object" && "response" in err ? (err as { response?: { data?: unknown } }).response?.data : undefined, @@ -268,7 +168,7 @@ class NetworkService implements INetworkService { if (err instanceof Error) { this.logger.warn({ message: err.message, - service: this.SERVICE_NAME, + service: SERVICE_NAME, method: "requestMatrix", }); @@ -276,7 +176,7 @@ class NetworkService implements INetworkService { const axiosError = err as { response?: { status?: number; data?: unknown } }; return { status: false, - code: axiosError.response?.status || this.NETWORK_ERROR, + code: axiosError.response?.status || NETWORK_ERROR, message: "Failed to send Matrix notification", payload: axiosError.response?.data, }; @@ -285,13 +185,13 @@ class NetworkService implements INetworkService { this.logger.warn({ message: String(err), - service: this.SERVICE_NAME, + service: SERVICE_NAME, method: "requestMatrix", }); return { status: false, - code: this.NETWORK_ERROR, + code: NETWORK_ERROR, message: "Failed to send Matrix notification", }; } From bd32854c7a6c58784c5bbb864bdb29621f88197b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 5 Mar 2026 14:46:28 -0800 Subject: [PATCH 16/16] cleanup services --- server/src/config/services.ts | 115 +++++++++++------- server/src/db/IDb.ts | 4 + server/src/db/MongoDB.ts | 20 +-- server/src/index.ts | 2 +- server/src/service/business/checkService.ts | 3 +- .../src/service/business/diagnosticService.ts | 19 +-- .../src/service/business/geoChecksService.ts | 3 +- .../src/service/business/incidentService.ts | 27 +++- server/src/service/business/inviteService.ts | 9 +- .../business/maintenanceWindowService.ts | 16 ++- server/src/service/business/userService.ts | 65 ++++++---- server/src/service/index.ts | 31 ++++- .../SuperSimpleQueue/SuperSimpleQueue.ts | 3 +- .../SuperSimpleQueueHelper.ts | 9 +- .../service/infrastructure/bufferService.ts | 3 +- .../service/infrastructure/emailService.ts | 3 +- .../infrastructure/globalPingService.ts | 3 +- .../service/infrastructure/networkService.ts | 3 +- server/src/service/system/settingsService.ts | 3 +- 19 files changed, 221 insertions(+), 120 deletions(-) create mode 100644 server/src/db/IDb.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index f7746ef47..b55bd0f87 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -1,31 +1,65 @@ import MongoDB from "../db/MongoDB.js"; -import NetworkService from "../service/infrastructure/networkService.js"; -import EmailService from "../service/infrastructure/emailService.js"; -import BufferService from "../service/infrastructure/bufferService.js"; +import { IDb } from "@/db/IDb.js"; import { + // Service classes + NetworkService, + EmailService, + BufferService, + GlobalPingService, + SuperSimpleQueue, + SuperSimpleQueueHelper, NotificationsService, StatusService, + NotificationMessageBuilder, + MonitorService, + StatusPageService, + UserService, + CheckService, + GeoChecksService, + DiagnosticService, + InviteService, + MaintenanceWindowService, + IncidentService, + // Notification providers WebhookProvider, SlackProvider, EmailProvider, DiscordProvider, PagerDutyProvider, MatrixProvider, + // Interfaces + INetworkService, + IEmailService, + IBufferService, + ISuperSimpleQueue, INotificationsService, + IStatusService, + IMonitorService, + IUserService, + ICheckService, + IGeoChecksService, + IDiagnosticService, + IInviteService, + IMaintenanceWindowService, + IStatusPageService, + IIncidentService, + INotificationMessageBuilder, + ISettingsService, + EnvConfig, } from "@/service/index.js"; -import SuperSimpleQueueHelper from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; -import SuperSimpleQueue from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; -import UserService from "../service/business/userService.js"; -import CheckService from "../service/business/checkService.js"; -import GeoChecksService from "../service/business/geoChecksService.js"; -import GlobalPingService from "../service/infrastructure/globalPingService.js"; -import DiagnosticService from "../service/business/diagnosticService.js"; -import InviteService from "../service/business/inviteService.js"; -import MaintenanceWindowService from "../service/business/maintenanceWindowService.js"; -import { MonitorService } from "@/service/index.js"; -import { StatusPageService, IStatusPageService } from "../service/business/statusPageService.js"; -import IncidentService from "../service/business/incidentService.js"; -import { NotificationMessageBuilder, INotificationMessageBuilder } from "../service/infrastructure/notificationMessageBuilder.js"; + +// Network providers +import { PingProvider } from "@/service/infrastructure/network/PingProvider.js"; +import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; +import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; +import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js"; +import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; +import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; +import { PortProvider } from "@/service/infrastructure/network/PortProvider.js"; +import { GameProvider } from "@/service/infrastructure/network/GameProvider.js"; +import { GrpcProvider } from "@/service/infrastructure/network/GrpcProvider.js"; + +// Third-party import axios from "axios"; import got from "got"; import ping from "ping"; @@ -44,7 +78,7 @@ import jmespath from "jmespath"; import * as grpc from "@grpc/grpc-js"; import * as protoLoader from "@grpc/proto-loader"; -// repositories +// Repositories import { MongoMonitorsRepository, MongoChecksRepository, @@ -73,33 +107,23 @@ import { IMaintenanceWindowsRepository, } from "@/repositories/index.js"; import { ILogger } from "@/utils/logger.js"; -import { EnvConfig } from "@/service/system/settingsService.js"; -import { PingProvider } from "@/service/infrastructure/network/PingProvider.js"; -import { HttpProvider } from "@/service/infrastructure/network/HttpProvider.js"; -import { AdvancedMatcher } from "@/service/infrastructure/network/AdvancedMatcher.js"; -import { PageSpeedProvider } from "@/service/infrastructure/network/PageSpeedProvider.js"; -import { HardwareProvider } from "@/service/infrastructure/network/HardwareProvider.js"; -import { DockerProvider } from "@/service/infrastructure/network/DockerProvider.js"; -import { PortProvider } from "@/service/infrastructure/network/PortProvider.js"; -import { GameProvider } from "@/service/infrastructure/network/GameProvider.js"; -import { GrpcProvider } from "@/service/infrastructure/network/GrpcProvider.js"; export type InitializedServices = { - settingsService: any; - db: any; - networkService: any; - emailService: any; - bufferService: any; - statusService: any; - jobQueue: any; - userService: any; - checkService: any; - geoChecksService: any; - diagnosticService: any; - inviteService: any; - maintenanceWindowService: any; - monitorService: any; - incidentService: any; + settingsService: ISettingsService; + db: IDb; + networkService: INetworkService; + emailService: IEmailService; + bufferService: IBufferService; + statusService: IStatusService; + jobQueue: ISuperSimpleQueue; + userService: IUserService; + checkService: ICheckService; + geoChecksService: IGeoChecksService; + diagnosticService: IDiagnosticService; + inviteService: IInviteService; + maintenanceWindowService: IMaintenanceWindowService; + monitorService: IMonitorService; + incidentService: IIncidentService; logger: ILogger; notificationsService: INotificationsService; statusPageService: IStatusPageService; @@ -129,7 +153,7 @@ export const initializeServices = async ({ }: { logger: ILogger; envSettings: EnvConfig; - settingsService: any; + settingsService: ISettingsService; settingsRepository: ISettingsRepository; }): Promise => { // Create DB @@ -152,8 +176,7 @@ export const initializeServices = async ({ const teamsRepository = new MongoTeamsRepository(); const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository(); - // Providers - + // Network providers const pingProvider = new PingProvider(ping); const httpProvider = new HttpProvider(got, new AdvancedMatcher(jmespath)); const pageSpeedProvider = new PageSpeedProvider(httpProvider, settingsService, logger); @@ -194,6 +217,7 @@ export const initializeServices = async ({ const statusService = new StatusService(logger, bufferService, monitorsRepository, monitorStatsRepository, checksRepository); + // Notification providers const webhookProvider = new WebhookProvider(logger); const slackProvider = new SlackProvider(logger); const emailProvider = new EmailProvider(emailService, logger); @@ -277,7 +301,6 @@ export const initializeServices = async ({ const statusPageService = new StatusPageService(statusPagesRepository); const services = { - //v1 settingsService, db, networkService, diff --git a/server/src/db/IDb.ts b/server/src/db/IDb.ts new file mode 100644 index 000000000..4c719999f --- /dev/null +++ b/server/src/db/IDb.ts @@ -0,0 +1,4 @@ +export interface IDb { + connect(): Promise; + disconnect(): Promise; +} diff --git a/server/src/db/MongoDB.ts b/server/src/db/MongoDB.ts index d62f4bbad..82d05c3a2 100755 --- a/server/src/db/MongoDB.ts +++ b/server/src/db/MongoDB.ts @@ -1,10 +1,12 @@ import mongoose from "mongoose"; -import AppSettings from "./models/AppSettings.js"; -import { runMigrations } from "./migration/index.js"; +import AppSettings from "@/db/models/AppSettings.js"; +import { runMigrations } from "@/db/migration/index.js"; import { ILogger } from "@/utils/logger.js"; import { EnvConfig } from "@/service/system/settingsService.js"; const SERVICE_NAME = "MongoDB"; -class MongoDB { +import { IDb } from "@/db/IDb.js"; + +class MongoDB implements IDb { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; @@ -46,12 +48,12 @@ class MongoDB { }); await runMigrations(); - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "connect", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); throw error; } @@ -63,12 +65,12 @@ class MongoDB { await mongoose.disconnect(); this.logger.info({ message: "Disconnected from MongoDB", service: SERVICE_NAME, method: "disconnect" }); return; - } catch (error: any) { + } catch (error: unknown) { this.logger.error({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "disconnect", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); } }; diff --git a/server/src/index.ts b/server/src/index.ts index e5b679beb..1a6195d80 100755 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -9,7 +9,7 @@ import fs from "fs"; import { runMigrations } from "./db/migration/index.js"; import Logger, { ILogger } from "@/utils/logger.js"; -import SettingsService from "@/service/system/settingsService.js"; +import { SettingsService } from "@/service/index.js"; import { MongoSettingsRepository } from "./repositories/index.js"; const SERVICE_NAME = "Server"; diff --git a/server/src/service/business/checkService.ts b/server/src/service/business/checkService.ts index abeb577ed..8779b0a00 100644 --- a/server/src/service/business/checkService.ts +++ b/server/src/service/business/checkService.ts @@ -19,7 +19,7 @@ export interface ICheckService { updateChecksTTL(params: { teamId: string; ttl: string }): Promise; } -class CheckService implements ICheckService { +export class CheckService implements ICheckService { static SERVICE_NAME = SERVICE_NAME; private monitorsRepository: IMonitorsRepository; @@ -195,4 +195,3 @@ class CheckService implements ICheckService { }; } -export default CheckService; diff --git a/server/src/service/business/diagnosticService.ts b/server/src/service/business/diagnosticService.ts index 67729ce98..f7a8b8ff9 100644 --- a/server/src/service/business/diagnosticService.ts +++ b/server/src/service/business/diagnosticService.ts @@ -3,15 +3,19 @@ import os from "os"; const SERVICE_NAME = "diagnosticService"; -interface MemoryUsageMetrics { - rss: number; - heapTotal: number; - heapUsed: number; - external: number; - arrayBuffers: number; +export interface IDiagnosticService { + getCPUUsage(): Promise<{ userUsageMs: number; systemUsageMs: number; usagePercentage: number }>; + getSystemStats(): Promise<{ + osStats: { freeMemoryBytes: number; totalMemoryBytes: number }; + memoryUsage: Record; + cpuUsage: { userUsageMs: number; systemUsageMs: number; usagePercentage: number }; + v8HeapStats: { totalHeapSizeBytes: number; usedHeapSizeBytes: number; heapSizeLimitBytes: number }; + eventLoopDelayMs: number; + uptimeMs: number; + }>; } -class DiagnosticService { +export class DiagnosticService implements IDiagnosticService { static SERVICE_NAME = SERVICE_NAME; constructor() { @@ -104,4 +108,3 @@ class DiagnosticService { }; } -export default DiagnosticService; diff --git a/server/src/service/business/geoChecksService.ts b/server/src/service/business/geoChecksService.ts index 1e5720656..5c5869d94 100644 --- a/server/src/service/business/geoChecksService.ts +++ b/server/src/service/business/geoChecksService.ts @@ -16,7 +16,7 @@ export interface IGeoChecksService { getGeoChecksByMonitor(args: { monitorId: string; query: any; teamId: string }): Promise; } -class GeoChecksService implements IGeoChecksService { +export class GeoChecksService implements IGeoChecksService { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; @@ -189,4 +189,3 @@ class GeoChecksService implements IGeoChecksService { }; } -export default GeoChecksService; diff --git a/server/src/service/business/incidentService.ts b/server/src/service/business/incidentService.ts index db44bdbb2..81a98893a 100644 --- a/server/src/service/business/incidentService.ts +++ b/server/src/service/business/incidentService.ts @@ -5,12 +5,34 @@ import { AppError } from "@/utils/AppError.js"; import { ParseBoolean } from "@/utils/utils.js"; import { getDateForRange } from "@/utils/dataUtils.js"; import type { IIncidentsRepository, IMonitorsRepository, IUsersRepository } from "@/repositories/index.js"; -import type { Incident } from "@/types/index.js"; +import type { Incident, IncidentSummary, User } from "@/types/index.js"; import type { MonitorActionDecision } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; import type { INotificationMessageBuilder } from "@/service/infrastructure/notificationMessageBuilder.js"; import type { ILogger } from "@/utils/logger.js"; -class IncidentService { +export interface IIncidentService { + handleIncident( + monitor: Monitor, + code: number, + decision: MonitorActionDecision, + monitorStatusResponse?: MonitorStatusResponse + ): Promise; + resolveIncident(incidentId: string, userId: string, teamId: string, comment?: string, userEmail?: string): Promise; + getIncidentsByTeam( + teamId: string, + sortOrder: string, + dateRange: string, + page: string, + rowsPerPage: string, + status: string, + monitorId: string, + resolutionType: string + ): Promise<{ incidents: Incident[]; count: number }>; + getIncidentSummary(teamId: string, limit?: string): Promise; + getIncidentById(incidentId: string, teamId: string): Promise<{ incident: Incident; monitor: Monitor; user: User | null }>; +} + +export class IncidentService implements IIncidentService { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; @@ -245,4 +267,3 @@ class IncidentService { }; } -export default IncidentService; diff --git a/server/src/service/business/inviteService.ts b/server/src/service/business/inviteService.ts index e4069d485..26b3b9dbe 100644 --- a/server/src/service/business/inviteService.ts +++ b/server/src/service/business/inviteService.ts @@ -5,7 +5,13 @@ import { AppError } from "@/utils/AppError.js"; const SERVICE_NAME = "inviteService"; -class InviteService { +export interface IInviteService { + getInviteToken(params: { invite: Partial; teamId: string; userRoles: UserRole[] }): Promise; + sendInviteEmail(params: { invite: Partial; firstName: string; userRoles: UserRole[] }): Promise; + verifyInviteToken(params: { inviteToken: string }): Promise; +} + +export class InviteService implements IInviteService { static SERVICE_NAME = SERVICE_NAME; private settingsService: any; @@ -89,4 +95,3 @@ class InviteService { }; } -export default InviteService; diff --git a/server/src/service/business/maintenanceWindowService.ts b/server/src/service/business/maintenanceWindowService.ts index 816bdca59..2257718f3 100644 --- a/server/src/service/business/maintenanceWindowService.ts +++ b/server/src/service/business/maintenanceWindowService.ts @@ -1,10 +1,23 @@ import { IMaintenanceWindowsRepository, IMonitorsRepository } from "@/repositories/index.js"; +import type { MaintenanceWindow } from "@/types/index.js"; import { ParseBoolean } from "@/utils/utils.js"; import { AppError } from "@/utils/AppError.js"; const SERVICE_NAME = "maintenanceWindowService"; -class MaintenanceWindowService { +export interface IMaintenanceWindowService { + createMaintenanceWindow(params: { teamId: string; body: Partial & { monitors: string[] } }): Promise; + getMaintenanceWindowById(params: { id: string; teamId: string }): Promise; + getMaintenanceWindowsByTeamId(params: { + teamId: string; + query: Record; + }): Promise<{ maintenanceWindows: MaintenanceWindow[]; maintenanceWindowCount: number }>; + getMaintenanceWindowsByMonitorId(params: { monitorId: string; teamId: string }): Promise; + deleteMaintenanceWindow(params: { id: string; teamId: string }): Promise; + editMaintenanceWindow(params: { id: string; teamId: string; body: Partial }): Promise; +} + +export class MaintenanceWindowService implements IMaintenanceWindowService { static SERVICE_NAME = SERVICE_NAME; private monitorsRepository: IMonitorsRepository; private maintenanceWindowsRepository: IMaintenanceWindowsRepository; @@ -84,4 +97,3 @@ class MaintenanceWindowService { }; } -export default MaintenanceWindowService; diff --git a/server/src/service/business/userService.ts b/server/src/service/business/userService.ts index d05f027b1..fde9d6292 100644 --- a/server/src/service/business/userService.ts +++ b/server/src/service/business/userService.ts @@ -11,10 +11,31 @@ import { canManageRole, type UserRole } from "@/types/user.js"; import bcrypt from "bcryptjs"; import { AppError } from "@/utils/AppError.js"; import { ISuperSimpleQueue } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; - +import { IEmailService } from "@/service/infrastructure/emailService.js"; +import { ISettingsService } from "@/service/system/settingsService.js"; +import { ILogger } from "@/utils/logger.js"; +import jwt from "jsonwebtoken"; +type JwtType = typeof jwt; const SERVICE_NAME = "userService"; -class UserService { +export interface IUserService { + issueToken(payload: Partial, appSettings: { jwtTTL?: string; jwtSecret?: string }): string; + registerUser(user: Partial, inviteToken: string, file: unknown): Promise<{ user: User; token: string }>; + createUser(userData: Partial, teamId: string, actorRoles: UserRole[], file: unknown): Promise; + loginUser(email: string, password: string): Promise<{ user: User; token: string }>; + editUser(updates: Partial, file: unknown, currentUser: User): Promise; + checkSuperadminExists(): Promise; + requestRecovery(email: string): Promise; + validateRecovery(recoveryToken: string): Promise; + resetPassword(password: string, recoveryToken: string): Promise<{ user: User; token: string }>; + deleteUser(user: User): Promise; + getAllUsers(): Promise; + getUserById(roles: UserRole[], userId: string): Promise; + editUserById(userId: string, patch: Partial): Promise; + setPasswordByUserId(userId: string, password: string): Promise; +} + +export class UserService implements IUserService { static SERVICE_NAME = SERVICE_NAME; private hashPassword = (password: string): string => { @@ -22,10 +43,10 @@ class UserService { return bcrypt.hashSync(password, salt); }; - private emailService: any; - private settingsService: any; - private logger: any; - private jwt: any; + private emailService: IEmailService; + private settingsService: ISettingsService; + private logger: ILogger; + private jwt: JwtType; private jobQueue: ISuperSimpleQueue; private crypto: any; private monitorsRepository: IMonitorsRepository; @@ -50,10 +71,10 @@ class UserService { teamsRepository, }: { crypto: any; - emailService: any; - settingsService: any; - logger: any; - jwt: any; + emailService: IEmailService; + settingsService: ISettingsService; + logger: ILogger; + jwt: JwtType; jobQueue: ISuperSimpleQueue; monitorsRepository: IMonitorsRepository; usersRepository: IUsersRepository; @@ -134,20 +155,23 @@ class UserService { const html = await this.emailService.buildEmail("welcomeEmailTemplate", { name: newUser.firstName, }); - this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error: any) => { + if (!html) { + throw new Error("Failed to build welcome email HTML"); + } + this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error: unknown) => { this.logger.warn({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "registerUser", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); }); - } catch (error: any) { + } catch (error: unknown) { this.logger.warn({ - message: error.message, + message: error instanceof Error ? error.message : "Unknown error", service: SERVICE_NAME, method: "registerUser", - stack: error.stack, + stack: error instanceof Error ? error.stack : undefined, }); } @@ -313,13 +337,13 @@ class UserService { if (roles.includes("superadmin")) { // 2. Remove all jobs, delete checks and alerts - res && - res?.length > 0 && - (await Promise.all( + if (res && res.length > 0) { + await Promise.all( res.map(async (monitor) => { await this.jobQueue.deleteJob(monitor.id); }) - )); + ); + } } // 6. Delete the user by id await this.usersRepository.deleteById(userId); @@ -348,4 +372,3 @@ class UserService { return updatedUser; }; } -export default UserService; diff --git a/server/src/service/index.ts b/server/src/service/index.ts index 7ec49288f..464866e09 100644 --- a/server/src/service/index.ts +++ b/server/src/service/index.ts @@ -1,14 +1,33 @@ +// Business services +export * from "@/service/business/checkService.js"; +export * from "@/service/business/diagnosticService.js"; +export * from "@/service/business/geoChecksService.js"; +export * from "@/service/business/incidentService.js"; +export * from "@/service/business/inviteService.js"; +export * from "@/service/business/maintenanceWindowService.js"; export * from "@/service/business/monitorService.js"; +export * from "@/service/business/statusPageService.js"; +export * from "@/service/business/userService.js"; + +// Infrastructure services +export * from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; +export * from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; +export * from "@/service/infrastructure/notificationMessageBuilder.js"; +export * from "@/service/infrastructure/bufferService.js"; +export * from "@/service/infrastructure/emailService.js"; +export * from "@/service/infrastructure/globalPingService.js"; export * from "@/service/infrastructure/networkService.js"; +export * from "@/service/infrastructure/notificationsService.js"; +export * from "@/service/infrastructure/statusService.js"; + +// Notification providers export * from "@/service/infrastructure/notificationProviders/discord.js"; export * from "@/service/infrastructure/notificationProviders/email.js"; export * from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; -export * from "@/service/infrastructure/notificationProviders/pagerduty.js"; export * from "@/service/infrastructure/notificationProviders/matrix.js"; +export * from "@/service/infrastructure/notificationProviders/pagerduty.js"; export * from "@/service/infrastructure/notificationProviders/slack.js"; export * from "@/service/infrastructure/notificationProviders/webhook.js"; -export * from "@/service/infrastructure/notificationsService.js"; -export * from "@/service/infrastructure/statusService.js"; -export * from "@/service/infrastructure/notificationProviders/webhook.js"; -export * from "@/service/infrastructure/notificationProviders/matrix.js"; -export * from "@/service/business/statusPageService.js"; + +// System services +export * from "@/service/system/settingsService.js"; diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.ts index 0eb645cb7..216a821b1 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.ts +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.ts @@ -53,7 +53,7 @@ export interface ISuperSimpleQueue { obliterate(): Promise; } -class SuperSimpleQueue implements ISuperSimpleQueue { +export class SuperSimpleQueue implements ISuperSimpleQueue { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; @@ -290,4 +290,3 @@ class SuperSimpleQueue implements ISuperSimpleQueue { }; } -export default SuperSimpleQueue; diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts index 3946f620a..11a44d1bd 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts @@ -1,10 +1,8 @@ const SERVICE_NAME = "JobQueueHelper"; import type { Monitor } from "@/types/monitor.js"; import { AppError } from "@/utils/AppError.js"; -import { INetworkService, INotificationsService, IStatusService } from "@/service/index.js"; +import { INetworkService, INotificationsService, IStatusService, IncidentService, type IGeoChecksService } from "@/service/index.js"; import type { StatusChangeResult } from "@/types/index.js"; -import IncidentService from "@/service/business/incidentService.js"; -import type { IGeoChecksService } from "@/service/business/geoChecksService.js"; import { IMaintenanceWindowsRepository, IMonitorsRepository, @@ -15,7 +13,7 @@ import { IGeoChecksRepository, } from "@/repositories/index.js"; import { ILogger } from "@/utils/logger.js"; -import { IBufferService } from "../bufferService.js"; +import { IBufferService } from "@/service/index.js"; export interface ISuperSimpleQueueHelper { readonly serviceName: string; @@ -39,7 +37,7 @@ export interface MonitorActionDecision { }; } -class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper { +export class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper { static SERVICE_NAME = SERVICE_NAME; private logger: any; @@ -415,4 +413,3 @@ class SuperSimpleQueueHelper implements ISuperSimpleQueueHelper { } } -export default SuperSimpleQueueHelper; diff --git a/server/src/service/infrastructure/bufferService.ts b/server/src/service/infrastructure/bufferService.ts index 318fd35cd..ae89a1c09 100755 --- a/server/src/service/infrastructure/bufferService.ts +++ b/server/src/service/infrastructure/bufferService.ts @@ -15,7 +15,7 @@ export interface IBufferService { flushGeoBuffer(): Promise; } -class BufferService implements IBufferService { +export class BufferService implements IBufferService { static SERVICE_NAME = SERVICE_NAME; private BUFFER_TIMEOUT: number; private logger: ILogger; @@ -178,4 +178,3 @@ class BufferService implements IBufferService { } } -export default BufferService; diff --git a/server/src/service/infrastructure/emailService.ts b/server/src/service/infrastructure/emailService.ts index 5246351c0..180a728f3 100755 --- a/server/src/service/infrastructure/emailService.ts +++ b/server/src/service/infrastructure/emailService.ts @@ -23,7 +23,7 @@ export interface IEmailService { sendEmail(to: string, subject: string, html: string, transportConfig?: EmailTransportConfig): Promise; } -class EmailService implements IEmailService { +export class EmailService implements IEmailService { static SERVICE_NAME = SERVICE_NAME; private settingsService: ISettingsService; @@ -179,4 +179,3 @@ class EmailService implements IEmailService { }; } -export default EmailService; diff --git a/server/src/service/infrastructure/globalPingService.ts b/server/src/service/infrastructure/globalPingService.ts index 883b9f76b..0a65412f4 100644 --- a/server/src/service/infrastructure/globalPingService.ts +++ b/server/src/service/infrastructure/globalPingService.ts @@ -54,7 +54,7 @@ export interface IGlobalPingService { pollForResults(measurementId: string, timeoutMs?: number): Promise; } -class GlobalPingService implements IGlobalPingService { +export class GlobalPingService implements IGlobalPingService { static SERVICE_NAME = SERVICE_NAME; private logger: ILogger; @@ -207,4 +207,3 @@ class GlobalPingService implements IGlobalPingService { } } -export default GlobalPingService; diff --git a/server/src/service/infrastructure/networkService.ts b/server/src/service/infrastructure/networkService.ts index f9c79ac29..a459c6480 100644 --- a/server/src/service/infrastructure/networkService.ts +++ b/server/src/service/infrastructure/networkService.ts @@ -23,7 +23,7 @@ export interface INetworkService { }>; } -class NetworkService implements INetworkService { +export class NetworkService implements INetworkService { static SERVICE_NAME = SERVICE_NAME; private axios: AxiosStatic; @@ -198,4 +198,3 @@ class NetworkService implements INetworkService { } } -export default NetworkService; diff --git a/server/src/service/system/settingsService.ts b/server/src/service/system/settingsService.ts index 58c896dc6..46959c554 100755 --- a/server/src/service/system/settingsService.ts +++ b/server/src/service/system/settingsService.ts @@ -21,7 +21,7 @@ export interface ISettingsService { getDBSettings(): Promise; } -class SettingsService implements ISettingsService { +export class SettingsService implements ISettingsService { static SERVICE_NAME = SERVICE_NAME; private settings: EnvConfig; private settingsRepository: ISettingsRepository; @@ -75,4 +75,3 @@ class SettingsService implements ISettingsService { }; } -export default SettingsService;