diff --git a/apps/client/src/features/app-settings/AppSettings.tsx b/apps/client/src/features/app-settings/AppSettings.tsx index 28c0098f13..beefc05f7a 100644 --- a/apps/client/src/features/app-settings/AppSettings.tsx +++ b/apps/client/src/features/app-settings/AppSettings.tsx @@ -3,6 +3,7 @@ import { ErrorBoundary } from '@sentry/react'; import { useKeyDown } from '../../common/hooks/useKeyDown'; import AboutPanel from './panel/about-panel/AboutPanel'; +import AutomationsPanel from './panel/automations-panel/AutomationsPanel'; import ClientControlPanel from './panel/client-control-panel/ClientControlPanel'; import FeatureSettingsPanel from './panel/feature-settings-panel/FeatureSettingsPanel'; import GeneralPanel from './panel/general-panel/GeneralPanel'; @@ -31,6 +32,7 @@ export default function AppSettings() { {panel === 'feature_settings' && } {panel === 'sources' && } {panel === 'integrations' && } + {panel === 'automations' && } {panel === 'client_control' && } {panel === 'about' && } {panel === 'network' && } diff --git a/apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx b/apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx index 4020d73d61..69be8a6e55 100644 --- a/apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx +++ b/apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx @@ -44,16 +44,15 @@ export function Card({ children, className, ...props }: { children: ReactNode } } export function Table({ className, children }: { className?: string; children: ReactNode }) { - const classes = cx([style.table, className]); return (
- {children}
+ {children}
); } -export function ListGroup({ children }: { children: ReactNode }) { - return
    {children}
; +export function ListGroup({ className, children }: { className?: string; children: ReactNode }) { + return
    {children}
; } export function ListItem({ children }: { children: ReactNode }) { diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.module.scss b/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.module.scss new file mode 100644 index 0000000000..566552bbf6 --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.module.scss @@ -0,0 +1,20 @@ +.cardCollapsed { + display: flex; + gap: 1rem; +} + +.append { + position: absolute; + display: flex; + align-items: center; + gap: 1rem; + top: -0.75rem; + background-color: $gray-900; + left: 0; + left: 50%; + height: 2rem; // match button height + transform: translateX(-50%); + padding-inline: 1rem; + border-radius: 99px; + font-size: calc(1rem - 2px); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.tsx b/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.tsx new file mode 100644 index 0000000000..441feddd0e --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationCard.tsx @@ -0,0 +1,22 @@ +import { PropsWithChildren, ReactNode } from 'react'; + +import { cx } from '../../../../common/utils/styleUtils'; +import * as Panel from '../../panel-utils/PanelUtils'; + +import style from './AutomationCard.module.scss'; + +interface AutomationCardProps { + append: ReactNode; + className?: string; +} + +export default function AutomationCard(props: PropsWithChildren) { + const { append, className, children } = props; + + return ( + + {children} +
{append}
+
+ ); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.module.scss b/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.module.scss new file mode 100644 index 0000000000..b45c3c4f7e --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.module.scss @@ -0,0 +1,34 @@ +.cardItems { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.fullWidth { + flex: 1; + align-items: center; +} + +.cardCollapsed { + background-color: $gray-1350; + display: flex; + justify-content: space-between; +} + +.cardExpanded { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.threeCols { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.filterRow{ + display: grid; + grid-template-columns: 1fr 1fr 1fr auto; + gap: 1rem; +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.tsx b/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.tsx new file mode 100644 index 0000000000..9cd168671a --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.tsx @@ -0,0 +1,133 @@ +import { PropsWithChildren, useState } from 'react'; +import { Button, ButtonGroup, IconButton, Input, Select } from '@chakra-ui/react'; +import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; +import { IoChevronDown } from '@react-icons/all-files/io5/IoChevronDown'; +import { IoTrash } from '@react-icons/all-files/io5/IoTrash'; + +import AutomationCard from './AutomationCard'; +import { cycles } from './automationUtils'; + +import style from './AutomationItem.module.scss'; + +interface AutomationCardProps { + title: string; + trigger: string; +} + +export default function AutomationItem(props: AutomationCardProps) { + const { title, trigger } = props; + const [expanded, setExpanded] = useState(false); + + return ( +
  • + + + } + aria-label='Edit entry' + onClick={() => setExpanded((prev) => !prev)} + /> + } + color='#FA5656' + aria-label='Delete entry' + onClick={() => setExpanded((prev) => !prev)} + /> + + {expanded && } +
  • + ); +} + +function AutomationCardExpanded(props: PropsWithChildren) { + const { title, trigger } = props; + const [triggerRole, setTriggerRole] = useState('all'); + + return ( + <> + + + + + + If + + + + + match + + } + > +
    + + + + } + aria-label='Delete entry' + onClick={() => undefined} + /> +
    + } + aria-label='Add entry' + onClick={() => undefined} + /> +
    + + + + + + } + aria-label='Add entry' + onClick={() => undefined} + /> + + + ); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.module.scss b/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.module.scss new file mode 100644 index 0000000000..2f9e33d1b7 --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.module.scss @@ -0,0 +1,5 @@ +.list { + display: flex; + flex-direction: column; + gap: 3rem; +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.tsx b/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.tsx new file mode 100644 index 0000000000..7d528e1aae --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationManagement.tsx @@ -0,0 +1,69 @@ +import { Button } from '@chakra-ui/react'; +import { TimerLifeCycle } from 'ontime-types'; + +import * as Panel from '../../panel-utils/PanelUtils'; + +import AutomationItem from './AutomationItem'; + +import style from './AutomationManagement.module.scss'; + +const data = [ + { + id: '1', + title: 'Automation 1', + trigger: TimerLifeCycle.onClock, + filterRule: 'all', + filter: [], + output: [], + }, + { + id: '2', + title: 'Automation 1', + trigger: TimerLifeCycle.onClock, + filterRule: 'all', + filter: [], + output: [], + }, + { + id: '3', + title: 'Automation 1', + trigger: TimerLifeCycle.onClock, + filterRule: 'all', + filter: [], + output: [], + }, +]; + +export default function AutomationManagement() { + return ( + + + Manage automations +
    + + +
    +
    + + + + undefined} + onKeyDown={() => console.log('prevent escapee')} + > + +
      + {data.map((automation) => { + return ; + })} +
    +
    +
    + ); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationSettings.tsx b/apps/client/src/features/app-settings/panel/automations-panel/AutomationSettings.tsx new file mode 100644 index 0000000000..e4aace642b --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationSettings.tsx @@ -0,0 +1,114 @@ +import { Controller, useForm } from 'react-hook-form'; +import { Button, Input, Switch } from '@chakra-ui/react'; + +import { isOnlyNumbers } from '../../../../common/utils/regex'; +import * as Panel from '../../panel-utils/PanelUtils'; + +interface AutomationSettingsOptions { + enabledIn: boolean; + portIn: number; +} + +const automationSettingsPlaceholder = { + enabledIn: false, + portIn: 8888, +}; + +const style = { + flex: 'flex', +}; +export default function AutomationSettings() { + const { + control, + handleSubmit, + reset, + register, + setError, + formState: { errors, isSubmitting, isDirty, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues: automationSettingsPlaceholder, + values: automationSettingsPlaceholder, + resetOptions: { + keepDirtyValues: true, + }, + }); + + return ( + + + Automation settings +
    + + +
    +
    + + + + undefined} + onKeyDown={() => console.log('prevent escapee')} + > + + OSC Input + {errors?.root && {errors.root.message}} + + + + ( + + )} + /> + + + + + + + Automation + {errors?.root && {errors.root.message}} + + + + ( + + )} + /> + + + +
    + ); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/AutomationsPanel.tsx b/apps/client/src/features/app-settings/panel/automations-panel/AutomationsPanel.tsx new file mode 100644 index 0000000000..f038628fb8 --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/AutomationsPanel.tsx @@ -0,0 +1,42 @@ +import { Alert, AlertDescription, AlertIcon } from '@chakra-ui/react'; + +import ExternalLink from '../../../../common/components/external-link/ExternalLink'; +import useScrollIntoView from '../../../../common/hooks/useScrollIntoView'; +import type { PanelBaseProps } from '../../panel-list/PanelList'; +import * as Panel from '../../panel-utils/PanelUtils'; + +import AutomationManagement from './AutomationManagement'; +import AutomationSettings from './AutomationSettings'; + +const integrationDocsUrl = 'https://docs.getontime.no/api/integrations/'; + +export default function IntegrationsPanel({ location }: PanelBaseProps) { + const oscRef = useScrollIntoView('osc', location); + const httpRef = useScrollIntoView('http', location); + + return ( + <> + Automation + + + + + Integrations allow Ontime to receive commands or send its data to other systems in your workflow.
    +
    + Currently supported protocols are OSC (Open Sound Control), HTTP and Websockets.
    + WebSockets are used for Ontime and cannot be configured independently.
    + See the docs +
    +
    +
    + +
    + +
    +
    + +
    +
    + + ); +} diff --git a/apps/client/src/features/app-settings/panel/automations-panel/automationUtils.ts b/apps/client/src/features/app-settings/panel/automations-panel/automationUtils.ts new file mode 100644 index 0000000000..95a0b6f62b --- /dev/null +++ b/apps/client/src/features/app-settings/panel/automations-panel/automationUtils.ts @@ -0,0 +1,29 @@ +import { TimerLifeCycle } from 'ontime-types'; + +type CycleLabel = { + id: number; + label: string; + value: keyof typeof TimerLifeCycle; +}; + +export const cycles: CycleLabel[] = [ + { id: 1, label: 'On Load', value: 'onLoad' }, + { id: 2, label: 'On Start', value: 'onStart' }, + { id: 3, label: 'On Pause', value: 'onPause' }, + { id: 4, label: 'On Stop', value: 'onStop' }, + { id: 5, label: 'Every second', value: 'onClock' }, + { id: 5, label: 'On Timer Update', value: 'onUpdate' }, + { id: 6, label: 'On Finish', value: 'onFinish' }, + { id: 7, label: 'On Warning', value: 'onWarning' }, + { id: 8, label: 'On Danger', value: 'onDanger' }, +]; + +export const field = [ + { id: 1, label: 'Cue', value: 'cue' }, + { id: 2, label: 'Title', value: 'title' }, + { id: 3, label: 'Note', value: 'note' }, + { id: 4, label: 'Custom', value: 'cue' }, + { id: 5, label: 'Cue', value: 'cue' }, + { id: 6, label: 'Cue', value: 'cue' }, + { id: 7, label: 'Cue', value: 'cue' }, +]; diff --git a/apps/client/src/features/app-settings/useAppSettingsMenu.tsx b/apps/client/src/features/app-settings/useAppSettingsMenu.tsx index 7f87b4d2db..a277ace683 100644 --- a/apps/client/src/features/app-settings/useAppSettingsMenu.tsx +++ b/apps/client/src/features/app-settings/useAppSettingsMenu.tsx @@ -54,6 +54,14 @@ const staticOptions = [ { id: 'integrations__http', label: 'HTTP settings' }, ], }, + { + id: 'automations', + label: 'Automations', + secondary: [ + { id: 'automations__settings', label: 'Automation settings' }, + { id: 'automations__manage', label: 'Manage automations' }, + ], + }, { id: 'network', label: 'Network', diff --git a/apps/server/package.json b/apps/server/package.json index 9b5a8a7e23..d9ea798361 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -8,19 +8,19 @@ "@googleapis/sheets": "^5.0.5", "cors": "^2.8.5", "dotenv": "^16.0.1", - "express": "^4.18.2", - "express-static-gzip": "^2.1.7", - "express-validator": "^7.0.1", + "express": "^4.21.1", + "express-static-gzip": "^2.2.0", + "express-validator": "^7.2.0", "fast-equals": "^5.0.1", "google-auth-library": "^9.4.2", - "got": "^14.0.0", + "got": "^14.4.5", "lowdb": "^7.0.1", "multer": "^1.4.5-lts.1", - "node-osc": "^9.0.2", + "node-osc": "^9.1.4", "ontime-utils": "workspace:*", "sanitize-filename": "^1.6.3", "steno": "^4.0.2", - "ws": "^8.13.0", + "ws": "^8.18.0", "xlsx": "^0.18.5" }, "devDependencies": { @@ -28,7 +28,7 @@ "@types/express": "^4.17.17", "@types/multer": "^1.4.11", "@types/node": "catalog:", - "@types/node-osc": "^6.0.2", + "@types/node-osc": "^6.0.3", "@types/websocket": "^1.0.5", "@types/ws": "^8.5.10", "@typescript-eslint/eslint-plugin": "catalog:", @@ -40,10 +40,10 @@ "prettier": "catalog:", "server-timing": "^3.3.3", "shx": "^0.3.4", - "ts-essentials": "^9.4.1", - "tsx": "^4.16.2", + "ts-essentials": "^10.0.3", + "tsx": "^4.19.2", "typescript": "catalog:", - "vitest": "^1.6.0" + "vitest": "^2.1.5" }, "scripts": { "addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('../../package.json').version) + ';'\" > src/ONTIME_VERSION.js", diff --git a/apps/server/src/api-data/automation/__tests__/automation.service.test.ts b/apps/server/src/api-data/automation/__tests__/automation.service.test.ts new file mode 100644 index 0000000000..94ac375684 --- /dev/null +++ b/apps/server/src/api-data/automation/__tests__/automation.service.test.ts @@ -0,0 +1,240 @@ +import { TimerLifeCycle } from 'ontime-types'; + +import { + clearAutomations, + Automation, + addAutomations, + getAutomations, + deleteAutomation, + editAutomation, + OSCOutput, + HTTPOutput, + CompanionOutput, + triggerAction, +} from '../automation.service.js'; + +import * as oscClient from '../clients/osc.client.js'; +import * as httpClient from '../clients/http.client.js'; +import * as companionClient from '../clients/companion.client.js'; + +// mock event store +vi.mock('../../../stores/EventStore.js', () => ({ + eventStore: { + poll: vi.fn(), + }, +})); + +describe('addAutomations()', () => { + beforeEach(() => { + clearAutomations(); + }); + + it('should accept a list of valid automations', () => { + const testData: Automation[] = [ + { + id: Math.random().toString(), + title: 'test', + trigger: TimerLifeCycle.onLoad, + filterRule: 'all', + filter: [], + output: [makeOSCAction(), makeHTTPAction(), makeCompanionAction()], + }, + ]; + + const result = addAutomations(testData); + expect(result).toMatchObject(testData); + }); +}); + +describe('removeAutomation()', () => { + beforeEach(() => { + clearAutomations(); + addAutomations([ + { + id: 'test-osc', + title: 'test-osc', + trigger: TimerLifeCycle.onLoad, + filterRule: 'all', + filter: [], + output: [], + }, + { + id: 'test-http', + title: 'test-http', + trigger: TimerLifeCycle.onFinish, + filterRule: 'all', + filter: [], + output: [], + }, + { + id: 'test-companion', + title: 'test-companion', + trigger: TimerLifeCycle.onStop, + filterRule: 'all', + filter: [], + output: [], + }, + ]); + }); + + it('should remove an automation from the list', () => { + const automations = getAutomations(); + expect(automations.length).toEqual(3); + expect(automations[0].id).toEqual('test-osc'); + expect(automations[0].title).toEqual('test-osc'); + + const removed = deleteAutomation('test-osc'); + expect(removed.length).toEqual(2); + expect(removed[0].id).not.toEqual('test-osc'); + expect(removed[0].title).not.toEqual('test-osc'); + }); +}); + +describe('editAutomation()', () => { + beforeEach(() => { + clearAutomations(); + addAutomations([ + { + id: 'test-osc', + title: 'test-osc', + trigger: TimerLifeCycle.onLoad, + filterRule: 'any', + filter: [], + output: [], + }, + { + id: 'test-http', + title: 'test-http', + trigger: TimerLifeCycle.onFinish, + filterRule: 'any', + filter: [], + output: [], + }, + { + id: 'test-companion', + title: 'test-companion', + trigger: TimerLifeCycle.onStop, + filterRule: 'any', + filter: [], + output: [], + }, + ]); + }); + + it('should edit the contents of an automation', () => { + const automations = getAutomations(); + expect(automations[0].title).toEqual('test-osc'); + + const editedOSC = editAutomation('test-osc', { + id: 'test-osc', + title: 'edited-title', + trigger: TimerLifeCycle.onDanger, + filterRule: 'any', + filter: [], + output: [], + }); + + expect(editedOSC[0]).toMatchObject({ + id: 'test-osc', + title: 'edited-title', + trigger: TimerLifeCycle.onDanger, + }); + }); +}); + +describe('triggerAction()', () => { + beforeEach(() => { + clearAutomations(); + addAutomations([ + { + id: 'test-osc', + title: 'test-osc', + trigger: TimerLifeCycle.onLoad, + filterRule: 'any', + filter: [], + output: [makeOSCAction()], + }, + { + id: 'test-http', + title: 'test-http', + trigger: TimerLifeCycle.onFinish, + filterRule: 'any', + filter: [], + output: [makeOSCAction(), makeHTTPAction()], + }, + { + id: 'test-companion', + title: 'test-companion', + trigger: TimerLifeCycle.onStop, + filterRule: 'any', + filter: [], + output: [makeCompanionAction(), makeCompanionAction()], + }, + ]); + }); + it('should trigger automations for a given action', () => { + const oscSpy = vi.spyOn(oscClient, 'emitOSC'); + const httpSpy = vi.spyOn(httpClient, 'emitHTTP'); + const companionSpy = vi.spyOn(companionClient, 'emitCompanion'); + + triggerAction(TimerLifeCycle.onLoad); + expect(oscSpy).toHaveBeenCalledTimes(1); + expect(httpSpy).not.toBeCalled(); + expect(companionSpy).not.toBeCalled(); + oscSpy.mockReset(); + httpSpy.mockReset(); + companionSpy.mockReset(); + + triggerAction(TimerLifeCycle.onStart); + expect(oscSpy).not.toBeCalled(); + expect(httpSpy).not.toBeCalled(); + expect(companionSpy).not.toBeCalled(); + oscSpy.mockReset(); + httpSpy.mockReset(); + companionSpy.mockReset(); + + triggerAction(TimerLifeCycle.onFinish); + expect(oscSpy).toHaveBeenCalledTimes(1); + expect(httpSpy).toHaveBeenCalledTimes(1); + expect(companionSpy).not.toBeCalled(); + oscSpy.mockReset(); + httpSpy.mockReset(); + companionSpy.mockReset(); + + triggerAction(TimerLifeCycle.onStop); + expect(oscSpy).not.toBeCalled(); + expect(httpSpy).not.toBeCalled(); + expect(companionSpy).toHaveBeenCalledTimes(2); + }); +}); + +function makeOSCAction(action?: Partial): OSCOutput { + return { + type: 'osc', + targetIP: 'localhost', + targetPort: 3000, + address: 'test', + args: 'message', + ...action, + }; +} + +function makeHTTPAction(action?: Partial): HTTPOutput { + return { + type: 'http', + targetIP: 'localhost', + address: 'test', + ...action, + }; +} + +function makeCompanionAction(action?: Partial): CompanionOutput { + return { + type: 'companion', + targetIP: 'localhost', + address: 'test', + page: 1, + bank: 1, + ...action, + }; +} diff --git a/apps/server/src/api-data/automation/automation.controller.ts b/apps/server/src/api-data/automation/automation.controller.ts new file mode 100644 index 0000000000..226b54de8e --- /dev/null +++ b/apps/server/src/api-data/automation/automation.controller.ts @@ -0,0 +1,55 @@ +import { getErrorMessage } from 'ontime-utils'; +import { ErrorResponse } from 'ontime-types'; + +import type { Request, Response } from 'express'; + +import * as automationService from './automation.service.js'; +import { Automation, AutomationOutput } from './automation.service.js'; + +export function getAutomations(_req: Request, res: Response) { + res.json(automationService.getAutomations()); +} + +export function postAutomation(req: Request, res: Response) { + try { + const automation = req.body as Automation; + const allAutomations = automationService.addAutomations([automation]); + res.status(201).send(allAutomations); + } catch (error) { + const message = getErrorMessage(error); + res.status(400).send({ message }); + } +} + +export function putAutomation(req: Request, res: Response) { + try { + const automation = req.body as Automation; + const allAutomations = automationService.addAutomations([automation]); + res.status(201).send(allAutomations); + } catch (error) { + const message = getErrorMessage(error); + res.status(400).send({ message }); + } +} + +export function deleteAutomation(req: Request, res: Response) { + try { + const id = req.params.id; + const allAutomations = automationService.deleteAutomation(id); + res.status(204).send(allAutomations); + } catch (error) { + const message = getErrorMessage(error); + res.status(400).send({ message }); + } +} + +export function testAutomation(req: Request, res: Response) { + try { + const payload = req.body as AutomationOutput; + automationService.testOutput(payload); + res.status(200).send(); + } catch (error) { + const message = getErrorMessage(error); + res.status(400).send({ message }); + } +} diff --git a/apps/server/src/api-data/automation/automation.router.ts b/apps/server/src/api-data/automation/automation.router.ts new file mode 100644 index 0000000000..9d6c2faf31 --- /dev/null +++ b/apps/server/src/api-data/automation/automation.router.ts @@ -0,0 +1,21 @@ +import express from 'express'; + +import { + deleteAutomation, + getAutomations, + postAutomation, + putAutomation, + testAutomation, +} from './automation.controller.js'; +import { paramContainsAutomationId, validateAutomation, validateTestPayload } from './automation.validation.js'; + +export const router = express.Router(); + +router.get('/', getAutomations); +router.get('/test', validateTestPayload, testAutomation); + +router.post('/', validateAutomation, postAutomation); + +router.put('/:automationId', paramContainsAutomationId, putAutomation); + +router.delete('/:automationId', paramContainsAutomationId, deleteAutomation); diff --git a/apps/server/src/api-data/automation/automation.service.ts b/apps/server/src/api-data/automation/automation.service.ts new file mode 100644 index 0000000000..6ef41dedbe --- /dev/null +++ b/apps/server/src/api-data/automation/automation.service.ts @@ -0,0 +1,179 @@ +import { RuntimeStore, TimerLifeCycle } from 'ontime-types'; + +import { eventStore } from '../../stores/EventStore.js'; + +import { emitOSC } from './clients/osc.client.js'; +import { emitHTTP } from './clients/http.client.js'; +import { emitCompanion } from './clients/companion.client.js'; + +export type FilterRule = 'all' | 'any'; +export type Automation = { + id: string; + title: string; + filterRule: FilterRule; + trigger: TimerLifeCycle; + filter: AutomationFilter[]; + output: AutomationOutput[]; +}; + +export type AutomationFilter = { + field: string; // this should be a key of a OntimeEvent + custom fields + operator: 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains'; + value: string; // we use string but would coerce to the field value +}; + +export type AutomationOutput = OSCOutput | HTTPOutput | CompanionOutput; + +export type OSCOutput = { + type: 'osc'; + targetIP: string; + targetPort: number; + address: string; + args: number | string; +}; + +export type HTTPOutput = { + type: 'http'; + targetIP: string; + address: string; +}; + +export type CompanionOutput = { + type: 'companion'; + targetIP: string; + address: string; + page: number; + bank: number; +}; + +let automations: Automation[] = []; + +export function clearAutomations(): Automation[] { + automations = []; + return automations; +} + +export function getAutomations(): Automation[] { + return automations; +} + +/** + * Receives a list of automation, which is parsed, validated and normalised + * @param automations + */ +export function addAutomations(newAutomations: Automation[]) { + automations.push(...newAutomations); + return automations; +} + +/** + * Exposes a method for triggering actions based on a TimerLifeCycle event + */ +export function deleteAutomation(id: string) { + automations = automations.filter((automation) => automation.id !== id); + return automations; +} + +/** + * Exposes a method for triggering actions based on a TimerLifeCycle event + */ +export function editAutomation(id: string, newAutomation: Automation) { + for (let i = 0; i < automations.length; i++) { + const automation = automations[i]; + if (automation.id === id) { + automations[i] = newAutomation; + break; + } + } + return automations; +} + +/** + * Exposes a method for triggering actions based on a TimerLifeCycle event + */ +export function triggerAction(event: TimerLifeCycle) { + const triggerAutomations = automations.filter((automation) => automation.trigger === event); + if (triggerAutomations.length === 0) { + return; + } + + const state = eventStore.poll(); + triggerAutomations.forEach((automation) => { + if (automation.output.length === 0) { + return; + } + const shouldSend = testConditions(automation.filter, automation.filterRule, state); + if (shouldSend) { + send(automation.output, state); + } + }); +} + +export function testOutput(payload: AutomationOutput) { + const state = eventStore.poll(); + const success = send([payload], state); + if (!success) { + throw new Error('Failed to send output'); + } +} + +/** + * Checks whether the automation conditions are met + */ +export function testConditions( + filters: AutomationFilter[], + filterRule: FilterRule, + state: Partial, +): boolean { + if (filters.length === 0) { + return true; + } + + if (filterRule === 'all') { + return filters.every((filter) => evaluateCondition(filter)); + } + + return filters.some((filter) => evaluateCondition(filter)); + + function evaluateCondition(filter: AutomationFilter): boolean { + const { field, operator, value } = filter; + const fieldValue = state[field]; + + switch (operator) { + case 'equals': + return fieldValue === value; + case 'not_equals': + return fieldValue !== value; + case 'greater_than': + return fieldValue > value; + case 'less_than': + return fieldValue < value; + case 'contains': + return typeof fieldValue === 'string' && fieldValue.includes(value); + default: + return false; + } + } +} + +/** + * Handles preparing and sending of the data + */ +function send(output: AutomationOutput[], _state: Partial): boolean { + output.forEach((payload) => { + if (payload.type === 'osc') { + emitOSC(); + return true; + } + if (payload.type === 'http') { + emitHTTP(); + return true; + } + if (payload.type === 'companion') { + emitCompanion(); + return true; + } + return false; + }); + return true; +} diff --git a/apps/server/src/api-data/automation/automation.utils.ts b/apps/server/src/api-data/automation/automation.utils.ts new file mode 100644 index 0000000000..ee042d8dd9 --- /dev/null +++ b/apps/server/src/api-data/automation/automation.utils.ts @@ -0,0 +1,9 @@ +type FilterOperator = 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains'; + +export function isFilterOperator(value: string): value is FilterOperator { + return ['equals', 'not_equals', 'greater_than', 'less_than', 'contains'].includes(value); +} + +export function isFilterRule(value: string): value is 'all' | 'any' { + return value === 'all' || value === 'any'; +} diff --git a/apps/server/src/api-data/automation/automation.validation.ts b/apps/server/src/api-data/automation/automation.validation.ts new file mode 100644 index 0000000000..98b7d43fa3 --- /dev/null +++ b/apps/server/src/api-data/automation/automation.validation.ts @@ -0,0 +1,126 @@ +import { Request, Response, NextFunction } from 'express'; +import { body, param, validationResult } from 'express-validator'; + +import * as assert from '../../utils/assert.js'; + +import { isFilterOperator, isFilterRule } from './automation.utils.js'; +import type { Automation, AutomationFilter, AutomationOutput } from './automation.service.js'; + +export const validateTestPayload = [ + body('type').exists().isIn(['osc', 'http', 'companion']), + + // validation for OSC message + body('targetIP').if(body('type').equals('osc')).isIP(), + body('targetPort').if(body('type').equals('osc')).isPort(), + body('address').if(body('type').equals('osc')).isString().trim(), + body('args').if(body('type').equals('osc')).isString().trim(), + + // validation for HTTP message + body('targetIP').if(body('type').equals('http')).isIP(), + body('address').if(body('type').equals('http')).isString().trim(), + + // validation for OSC message + body('targetIP').if(body('type').equals('companion')).isIP(), + body('address').if(body('type').equals('companion')).isString().trim(), + body('page').if(body('type').equals('companion')).isInt({ min: 0 }), + body('bank').if(body('type').equals('companion')).isInt({ min: 0 }), + + (req: Request, res: Response, next: NextFunction) => { + const errors = validationResult(req); + if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); + next(); + }, +]; + +export const validateAutomation = [ + body().custom(parseAutomation), + + (_req: Request, _res: Response, next: NextFunction) => { + next(); + }, +]; + +export const paramContainsAutomationId = [ + param('automationId').exists(), + + (req: Request, res: Response, next: NextFunction) => { + const errors = validationResult(req); + if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); + next(); + }, +]; + +/** + * Parses and validates a potential automation entry + */ +export function parseAutomation(maybeAutomation: unknown): Automation { + assert.isObject(maybeAutomation); + assert.hasKeys(maybeAutomation, ['title', 'filterRule', 'filter', 'output']); + + const { title, filterRule, filter, output } = maybeAutomation; + assert.isString(title); + assert.isString(filterRule); + if (!isFilterRule(filterRule)) { + throw new Error('Invalid automation'); + } + assert.isArray(filter); + validateFilter(filter); + + assert.isArray(output); + validateOutput(output); + + return maybeAutomation as Automation; +} + +function validateFilter(filter: Array): filter is AutomationFilter[] { + filter.forEach((condition) => { + assert.isObject(condition); + + assert.hasKeys(condition, ['field', 'operator', 'value']); + const { field, operator, value } = condition; + assert.isString(field); + assert.isString(operator); + assert.isString(value); + !isFilterOperator(operator); + + if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') { + throw new Error('Invalid automation'); + } + }); + return true; +} + +function validateOutput(output: Array): output is AutomationOutput[] { + output.forEach((payload) => { + assert.isObject(payload); + assert.hasKeys(payload, ['type']); + const { type } = payload; + assert.isString(type); + + if (type === 'osc') { + assert.hasKeys(payload, ['targetIP', 'targetPort', 'address', 'args']); + const { targetIP, targetPort, address, args } = payload; + assert.isString(targetIP); + assert.isNumber(targetPort); + assert.isString(address); + if (typeof args !== 'string' && typeof args !== 'number') { + throw new Error('Invalid automation'); + } + } else if (type === 'http') { + assert.hasKeys(payload, ['targetIP', 'address']); + const { targetIP, address } = payload; + assert.isString(targetIP); + assert.isString(address); + } else if (type === 'companion') { + assert.hasKeys(payload, ['targetIP', 'address', 'page', 'bank']); + const { targetIP, address, page, bank } = payload; + assert.isString(targetIP); + assert.isString(address); + assert.isNumber(page); + assert.isNumber(bank); + } else { + throw new Error('Invalid automation'); + } + }); + return true; +} diff --git a/apps/server/src/api-data/automation/clients/companion.client.ts b/apps/server/src/api-data/automation/clients/companion.client.ts new file mode 100644 index 0000000000..cb1e03cd5f --- /dev/null +++ b/apps/server/src/api-data/automation/clients/companion.client.ts @@ -0,0 +1,18 @@ +/** + * Expose possibility to trigger a companion button over the HTTP protocol + */ +export function emitCompanion() { + console.log('companion emit not implemented'); + const payload = preparePayload(); + emit(payload); +} + +/** Parses the state and prepares payload to be emitted */ +function preparePayload() { + return; +} + +/** Emits message over transport */ +function emit(_payload) { + return; +} diff --git a/apps/server/src/api-data/automation/clients/http.client.ts b/apps/server/src/api-data/automation/clients/http.client.ts new file mode 100644 index 0000000000..6028f8bf31 --- /dev/null +++ b/apps/server/src/api-data/automation/clients/http.client.ts @@ -0,0 +1,18 @@ +/** + * Expose possibility to send a message using HTTP protocol + */ +export function emitHTTP() { + console.log('HTTP emit not implemented'); + const payload = preparePayload(); + emit(payload); +} + +/** Parses the state and prepares payload to be emitted */ +function preparePayload() { + return; +} + +/** Emits message over transport */ +function emit(_payload) { + return; +} diff --git a/apps/server/src/api-data/automation/clients/osc.client.ts b/apps/server/src/api-data/automation/clients/osc.client.ts new file mode 100644 index 0000000000..b455b7d9e7 --- /dev/null +++ b/apps/server/src/api-data/automation/clients/osc.client.ts @@ -0,0 +1,18 @@ +/** + * Expose possibility to send a message using OSC protocol + */ +export function emitOSC() { + console.log('OSC emit not implemented'); + const payload = preparePayload(); + emit(payload); +} + +/** Parses the state and prepares payload to be emitted */ +function preparePayload() { + return; +} + +/** Emits message over transport */ +function emit(_payload) { + return; +} diff --git a/apps/server/src/utils/assert.ts b/apps/server/src/utils/assert.ts index 47e68aa55e..1a9b9fb2e6 100644 --- a/apps/server/src/utils/assert.ts +++ b/apps/server/src/utils/assert.ts @@ -21,3 +21,20 @@ export function isObject(value: unknown): asserts value is object { throw new Error(`Unexpected payload type: ${String(value)}`); } } + +export function isArray(value: unknown): asserts value is unknown[] { + if (!Array.isArray(value)) { + throw new Error(`Unexpected payload type: ${String(value)}`); + } +} + +export function hasKeys( + value: T, + keys: K[], +): asserts value is T & Record { + for (const key of keys) { + if (!(key in value)) { + throw new Error(`Key not found: ${String(key)}`); + } + } +} diff --git a/apps/spec/automations.md b/apps/spec/automations.md new file mode 100644 index 0000000000..b1db7fcdf2 --- /dev/null +++ b/apps/spec/automations.md @@ -0,0 +1,49 @@ +# Automations + +The automation feature's purpose is to integrate ontime into users' workflows. +Ontime has a large amount of production information, which users need considerable effort to maintain. We want to allow tools so that: + +- allow distribution of Ontime's and other production data +- allow surfacing Ontime events +- allow synchronizing with other tools + +## Previous +Previous iterations imposed limitations on the number of integrations and target devices to evaluate performance concerns. + +The feature was not as used as we had hoped, and users often escalated the integration to tools like Companion. +I believe this to be in part from a lack of clarity on the feature and the limitations imposed +- Lack of explicit filtering logic made the process hard to reason +- Users met limitations on target devices earlier than expected. We would want users to meet these limitations only when the project grew over a size where a show controller would be needed +- Building the template strings was complex and poorly documented + +## Overview +- The user should be able to create as many automations as they want +- Automations are triggered by lifecycle events +- Each automation should go through a user-defined filtering process +- Each automation should be able to target multiple devices and multiple protocols +- We leverage HTTP / OSC as the main protocols and add support for triggering Companion button presses +- To allow easier debugging and "learn" workflows, users should be able to test a message before saving the automation +- We should have inline documentation for the template strings + +## Implementation details +- To simplify the usage of template strings, we will generate a list of template strings at runtime from the user project file +- To allow easier implementation and extensions, we want to keep the automations separate from triggers + +```ts +type Automation = { + id: AutomationId; + name: string; + filter: Filter[]; + output: Output[]; +}; + +type Trigger = { + id: string; + event: TimerLifecycle; + automations: AutomationId[]; +}; + +``` + +### Extension +- Users have expressed a desire to have automation triggered by the lifecycle of a specific event. By keeping the trigger separate from the automation, we will allow this to be implemented in the future. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b2cff25ff..842e393dda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -292,14 +292,14 @@ importers: specifier: ^16.0.1 version: 16.0.3 express: - specifier: ^4.18.2 - version: 4.18.2 + specifier: ^4.21.1 + version: 4.21.1 express-static-gzip: - specifier: ^2.1.7 - version: 2.1.7 + specifier: ^2.2.0 + version: 2.2.0 express-validator: - specifier: ^7.0.1 - version: 7.0.1 + specifier: ^7.2.0 + version: 7.2.0 fast-equals: specifier: ^5.0.1 version: 5.0.1 @@ -307,8 +307,8 @@ importers: specifier: ^9.4.2 version: 9.4.2 got: - specifier: ^14.0.0 - version: 14.0.0 + specifier: ^14.4.5 + version: 14.4.5 lowdb: specifier: ^7.0.1 version: 7.0.1 @@ -316,8 +316,8 @@ importers: specifier: ^1.4.5-lts.1 version: 1.4.5-lts.1 node-osc: - specifier: ^9.0.2 - version: 9.0.2 + specifier: ^9.1.4 + version: 9.1.4 ontime-utils: specifier: workspace:* version: link:../../packages/utils @@ -328,8 +328,8 @@ importers: specifier: ^4.0.2 version: 4.0.2 ws: - specifier: ^8.13.0 - version: 8.13.0 + specifier: ^8.18.0 + version: 8.18.0 xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -347,8 +347,8 @@ importers: specifier: 'catalog:' version: 20.14.10 '@types/node-osc': - specifier: ^6.0.2 - version: 6.0.2 + specifier: ^6.0.3 + version: 6.0.3 '@types/websocket': specifier: ^1.0.5 version: 1.0.5 @@ -383,17 +383,17 @@ importers: specifier: ^0.3.4 version: 0.3.4 ts-essentials: - specifier: ^9.4.1 - version: 9.4.1(typescript@5.5.3) + specifier: ^10.0.3 + version: 10.0.3(typescript@5.5.3) tsx: - specifier: ^4.16.2 - version: 4.16.2 + specifier: ^4.19.2 + version: 4.19.2 typescript: specifier: 'catalog:' version: 5.5.3 vitest: - specifier: ^1.6.0 - version: 1.6.0(@types/node@20.14.10)(jsdom@21.1.0)(sass@1.57.1) + specifier: ^2.1.5 + version: 2.1.5(@types/node@20.14.10)(jsdom@21.1.0)(sass@1.57.1) packages/types: devDependencies: @@ -1192,9 +1192,9 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1210,9 +1210,9 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1228,9 +1228,9 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1246,9 +1246,9 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1264,9 +1264,9 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1282,9 +1282,9 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1300,9 +1300,9 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1318,9 +1318,9 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1336,9 +1336,9 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1354,9 +1354,9 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1372,9 +1372,9 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1390,9 +1390,9 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1408,9 +1408,9 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1426,9 +1426,9 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1444,9 +1444,9 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1462,9 +1462,9 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1480,9 +1480,9 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1498,12 +1498,18 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.19.10': resolution: {integrity: sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg==} engines: {node: '>=12'} @@ -1516,9 +1522,9 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1534,9 +1540,9 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1552,9 +1558,9 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1570,9 +1576,9 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1588,9 +1594,9 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1665,6 +1671,9 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.20': resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} @@ -1803,6 +1812,9 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@sentry-internal/browser-utils@8.19.0': resolution: {integrity: sha512-kM/2KlikKuBR63nFi2q7MGS3V9K9hakjvUknhr/jHZqDVfEuBKmp1ZlHFAdJtglKHHJy07gPj/XqDH7BbYh5yg==} engines: {node: '>=14.18'} @@ -1915,9 +1927,9 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@sindresorhus/is@6.1.0': - resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==} - engines: {node: '>=16'} + '@sindresorhus/is@7.0.1': + resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==} + engines: {node: '>=18'} '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} @@ -2147,8 +2159,8 @@ packages: '@types/multer@1.4.11': resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} - '@types/node-osc@6.0.2': - resolution: {integrity: sha512-/TxCH+NlDoI3hFA6b2O91dpnPAqBDkLb2HEIv5hMVdKnCiWTdJJv2sVnQdX38sBgnals8TjCQEGii+OcMVf2fg==} + '@types/node-osc@6.0.3': + resolution: {integrity: sha512-f0JUTDVAlk/mV9RH6jE6g/8Y7l+Mvi3f1x7xv0RG3VI1PktaXEd60mR+4plpMS9VbRovFoPsS7qaaIDhFXdaEw==} '@types/node@20.14.10': resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} @@ -2361,18 +2373,47 @@ packages: '@vitest/expect@1.6.0': resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + '@vitest/expect@2.1.5': + resolution: {integrity: sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==} + + '@vitest/mocker@2.1.5': + resolution: {integrity: sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.5': + resolution: {integrity: sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==} + '@vitest/runner@1.6.0': resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + '@vitest/runner@2.1.5': + resolution: {integrity: sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==} + '@vitest/snapshot@1.6.0': resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + '@vitest/snapshot@2.1.5': + resolution: {integrity: sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==} + '@vitest/spy@1.6.0': resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + '@vitest/spy@2.1.5': + resolution: {integrity: sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==} + '@vitest/utils@1.6.0': resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + '@vitest/utils@2.1.5': + resolution: {integrity: sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==} + '@xmldom/xmldom@0.8.10': resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} @@ -2519,6 +2560,10 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2582,8 +2627,8 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} boolean@3.2.0: @@ -2647,9 +2692,9 @@ packages: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + cacheable-request@12.0.1: + resolution: {integrity: sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==} + engines: {node: '>=18'} cacheable-request@7.0.4: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} @@ -2658,6 +2703,10 @@ packages: call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2677,6 +2726,10 @@ packages: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + engines: {node: '>=12'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2692,6 +2745,10 @@ packages: check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -2786,10 +2843,6 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-type@1.0.4: - resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} - engines: {node: '>= 0.6'} - content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -2803,8 +2856,8 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} copy-to-clipboard@3.3.3: @@ -2904,6 +2957,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -2915,6 +2977,10 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} @@ -2933,6 +2999,10 @@ packages: resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + define-properties@1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} engines: {node: '>= 0.4'} @@ -3057,6 +3127,10 @@ packages: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -3078,9 +3152,20 @@ packages: resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} engines: {node: '>= 0.4'} + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} @@ -3105,9 +3190,9 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} hasBin: true escalade@3.1.1: @@ -3270,19 +3355,23 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + expect@29.3.1: resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-static-gzip@2.1.7: - resolution: {integrity: sha512-QOCZUC+lhPPCjIJKpQGu1Oa61Axg9Mq09Qvit8Of7kzpMuwDeMSqjjQteQS3OVw/GkENBoSBheuQDWPlngImvw==} + express-static-gzip@2.2.0: + resolution: {integrity: sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==} - express-validator@7.0.1: - resolution: {integrity: sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==} + express-validator@7.2.0: + resolution: {integrity: sha512-I2ByKD8panjtr8Y05l21Wph9xk7kk64UMyvJCl/fFM/3CTJq8isXYPLeKW/aZBCdb/LYNv63PwhY8khw8VWocA==} engines: {node: '>= 8.0.0'} - express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + express@4.21.1: + resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} engines: {node: '>= 0.10.0'} extend@3.0.2: @@ -3337,8 +3426,8 @@ packages: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} - finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} find-root@1.1.0: @@ -3481,6 +3570,10 @@ packages: get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -3489,14 +3582,14 @@ packages: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -3563,8 +3656,8 @@ packages: resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} engines: {node: '>=10.19.0'} - got@14.0.0: - resolution: {integrity: sha512-X01vTgaX9SwaMq5DfImvS+3GMQFFs5HtrrlS9CuzUSzkxAf/tWGEyynuI+Qy7BjciMczZGjyVSmawYbP4eYhYA==} + got@14.4.5: + resolution: {integrity: sha512-sq+uET8TnNKRNnjEOPJzMcxeI0irT8BBNmf+GtZcJpmhYsQM1DSKmCROUjPWKsXZ5HzwD5Cf5/RV+QD9BSTxJg==} engines: {node: '>=20'} graceful-fs@4.2.11: @@ -3591,6 +3684,9 @@ packages: has-property-descriptors@1.0.1: resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} @@ -3800,6 +3896,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -3992,6 +4092,9 @@ packages: loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@3.1.2: + resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + lowdb@7.0.1: resolution: {integrity: sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==} engines: {node: '>=18'} @@ -4026,6 +4129,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.30.13: + resolution: {integrity: sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==} + magic-string@0.30.5: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} @@ -4042,8 +4148,8 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4200,9 +4306,9 @@ packages: encoding: optional: true - node-osc@9.0.2: - resolution: {integrity: sha512-q+VQL7DMWRL5+yvzRlWVig8BD9raotLs6onHU4e8MaFgxmYuIwcXhsvQeyUZFiKP6y/qGUXU6K0T99gVmISwmA==} - engines: {node: ^18.17.0 || >=20.5.0} + node-osc@9.1.4: + resolution: {integrity: sha512-ChkdOHmy2Ay6egrGsL9C7u0RqBQ+VIHjw+lk6yd59TuyzmqbaosCo53E1lvjh1xM97c+ByBhyH6i2dHOiBa2bw==} + engines: {node: ^18.17.0 || ^20.5.0 || >=22.0.0} node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} @@ -4215,8 +4321,8 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - normalize-url@8.0.0: - resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} engines: {node: '>=14.16'} npm-run-path@5.1.0: @@ -4233,6 +4339,10 @@ packages: object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + object-inspect@1.13.3: + resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + engines: {node: '>= 0.4'} + object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} @@ -4345,8 +4455,8 @@ packages: resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} engines: {node: '>=16 || 14 >=14.17'} - path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -4355,9 +4465,16 @@ packages: pathe@1.1.1: resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -4459,6 +4576,10 @@ packages: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -4476,8 +4597,8 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} react-clientside-effect@1.2.6: @@ -4735,21 +4856,25 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} server-timing@3.3.3: resolution: {integrity: sha512-TP0xWAca4oM8H/PSdeaGgp2qm+HrZ2cWCRcMXS2t500a7Wum/hSojlpTW43VZsIUSVNlKPFGDknH34IqF+mbBg==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -4774,6 +4899,10 @@ packages: side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -4843,6 +4972,9 @@ packages: std-env@3.6.0: resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + steno@4.0.2: resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==} engines: {node: '>=18'} @@ -4945,14 +5077,32 @@ packages: tinybench@2.5.1: resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.1: + resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + tinyspy@2.2.0: resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} engines: {node: '>=14.0.0'} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} @@ -4995,10 +5145,10 @@ packages: peerDependencies: typescript: '>=4.2.0' - ts-essentials@9.4.1: - resolution: {integrity: sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==} + ts-essentials@10.0.3: + resolution: {integrity: sha512-/FrVAZ76JLTWxJOERk04fm8hYENDo0PWSP3YLQKxevLwWtxemGcl5JJEzN4iqfDlRve0ckyfFaOBu4xbNH/wZw==} peerDependencies: - typescript: '>=4.1.0' + typescript: '>=4.5.0' peerDependenciesMeta: typescript: optional: true @@ -5031,8 +5181,8 @@ packages: peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - tsx@4.16.2: - resolution: {integrity: sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==} + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'} hasBin: true @@ -5090,6 +5240,10 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@4.27.1: + resolution: {integrity: sha512-3Ta7CyV6daqpwuGJMJKABaUChZZejpzysZkQg1//bLRg2wKQ4duwsg3MMIsHuElq58iDqizg4DBUmK8H8wExJg==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -5209,6 +5363,11 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-node@2.1.5: + resolution: {integrity: sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + vite-plugin-compression2@0.12.0: resolution: {integrity: sha512-9zdEF9xKVezETSF1l1bHoOk8LNoKIHB+DZVgSIGuGWaYupwFmsAGh0uwRcmK6rVHacxQRBECVYdtfc65DPDRfg==} @@ -5278,6 +5437,31 @@ packages: jsdom: optional: true + vitest@2.1.5: + resolution: {integrity: sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.5 + '@vitest/ui': 2.1.5 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} @@ -5339,6 +5523,11 @@ packages: engines: {node: '>=8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wmf@1.0.2: resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} engines: {node: '>=0.8'} @@ -5370,6 +5559,18 @@ packages: utf-8-validate: optional: true + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xlsx@0.18.5: resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} engines: {node: '>=0.8'} @@ -6501,7 +6702,7 @@ snapshots: '@esbuild/aix-ppc64@0.20.2': optional: true - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.23.1': optional: true '@esbuild/android-arm64@0.19.10': @@ -6510,7 +6711,7 @@ snapshots: '@esbuild/android-arm64@0.20.2': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.23.1': optional: true '@esbuild/android-arm@0.19.10': @@ -6519,7 +6720,7 @@ snapshots: '@esbuild/android-arm@0.20.2': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.23.1': optional: true '@esbuild/android-x64@0.19.10': @@ -6528,7 +6729,7 @@ snapshots: '@esbuild/android-x64@0.20.2': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.23.1': optional: true '@esbuild/darwin-arm64@0.19.10': @@ -6537,7 +6738,7 @@ snapshots: '@esbuild/darwin-arm64@0.20.2': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.23.1': optional: true '@esbuild/darwin-x64@0.19.10': @@ -6546,7 +6747,7 @@ snapshots: '@esbuild/darwin-x64@0.20.2': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.23.1': optional: true '@esbuild/freebsd-arm64@0.19.10': @@ -6555,7 +6756,7 @@ snapshots: '@esbuild/freebsd-arm64@0.20.2': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.23.1': optional: true '@esbuild/freebsd-x64@0.19.10': @@ -6564,7 +6765,7 @@ snapshots: '@esbuild/freebsd-x64@0.20.2': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.23.1': optional: true '@esbuild/linux-arm64@0.19.10': @@ -6573,7 +6774,7 @@ snapshots: '@esbuild/linux-arm64@0.20.2': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.23.1': optional: true '@esbuild/linux-arm@0.19.10': @@ -6582,7 +6783,7 @@ snapshots: '@esbuild/linux-arm@0.20.2': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.23.1': optional: true '@esbuild/linux-ia32@0.19.10': @@ -6591,7 +6792,7 @@ snapshots: '@esbuild/linux-ia32@0.20.2': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.23.1': optional: true '@esbuild/linux-loong64@0.19.10': @@ -6600,7 +6801,7 @@ snapshots: '@esbuild/linux-loong64@0.20.2': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.23.1': optional: true '@esbuild/linux-mips64el@0.19.10': @@ -6609,7 +6810,7 @@ snapshots: '@esbuild/linux-mips64el@0.20.2': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.23.1': optional: true '@esbuild/linux-ppc64@0.19.10': @@ -6618,7 +6819,7 @@ snapshots: '@esbuild/linux-ppc64@0.20.2': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.23.1': optional: true '@esbuild/linux-riscv64@0.19.10': @@ -6627,7 +6828,7 @@ snapshots: '@esbuild/linux-riscv64@0.20.2': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.23.1': optional: true '@esbuild/linux-s390x@0.19.10': @@ -6636,7 +6837,7 @@ snapshots: '@esbuild/linux-s390x@0.20.2': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.23.1': optional: true '@esbuild/linux-x64@0.19.10': @@ -6645,7 +6846,7 @@ snapshots: '@esbuild/linux-x64@0.20.2': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.23.1': optional: true '@esbuild/netbsd-x64@0.19.10': @@ -6654,7 +6855,10 @@ snapshots: '@esbuild/netbsd-x64@0.20.2': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': optional: true '@esbuild/openbsd-x64@0.19.10': @@ -6663,7 +6867,7 @@ snapshots: '@esbuild/openbsd-x64@0.20.2': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.23.1': optional: true '@esbuild/sunos-x64@0.19.10': @@ -6672,7 +6876,7 @@ snapshots: '@esbuild/sunos-x64@0.20.2': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.23.1': optional: true '@esbuild/win32-arm64@0.19.10': @@ -6681,7 +6885,7 @@ snapshots: '@esbuild/win32-arm64@0.20.2': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.23.1': optional: true '@esbuild/win32-ia32@0.19.10': @@ -6690,7 +6894,7 @@ snapshots: '@esbuild/win32-ia32@0.20.2': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.23.1': optional: true '@esbuild/win32-x64@0.19.10': @@ -6699,7 +6903,7 @@ snapshots: '@esbuild/win32-x64@0.20.2': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.23.1': optional: true '@eslint-community/eslint-utils@4.4.0(eslint@8.56.0)': @@ -6781,6 +6985,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.20': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -6885,6 +7091,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.17.2': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@sentry-internal/browser-utils@8.19.0': dependencies: '@sentry/core': 8.19.0 @@ -7017,7 +7225,7 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@sindresorhus/is@6.1.0': {} + '@sindresorhus/is@7.0.1': {} '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.23.6)': dependencies: @@ -7291,7 +7499,7 @@ snapshots: dependencies: '@types/express': 4.17.17 - '@types/node-osc@6.0.2': + '@types/node-osc@6.0.3': dependencies: '@types/node': 20.14.10 @@ -7587,22 +7795,56 @@ snapshots: '@vitest/utils': 1.6.0 chai: 4.3.10 + '@vitest/expect@2.1.5': + dependencies: + '@vitest/spy': 2.1.5 + '@vitest/utils': 2.1.5 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.5(vite@5.2.11(@types/node@20.14.10)(sass@1.57.1))': + dependencies: + '@vitest/spy': 2.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.13 + optionalDependencies: + vite: 5.2.11(@types/node@20.14.10)(sass@1.57.1) + + '@vitest/pretty-format@2.1.5': + dependencies: + tinyrainbow: 1.2.0 + '@vitest/runner@1.6.0': dependencies: '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.1 + '@vitest/runner@2.1.5': + dependencies: + '@vitest/utils': 2.1.5 + pathe: 1.1.2 + '@vitest/snapshot@1.6.0': dependencies: magic-string: 0.30.5 pathe: 1.1.1 pretty-format: 29.7.0 + '@vitest/snapshot@2.1.5': + dependencies: + '@vitest/pretty-format': 2.1.5 + magic-string: 0.30.13 + pathe: 1.1.2 + '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.0 + '@vitest/spy@2.1.5': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@1.6.0': dependencies: diff-sequences: 29.6.3 @@ -7610,6 +7852,12 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 + '@vitest/utils@2.1.5': + dependencies: + '@vitest/pretty-format': 2.1.5 + loupe: 3.1.2 + tinyrainbow: 1.2.0 + '@xmldom/xmldom@0.8.10': {} '@zag-js/element-size@0.3.2': {} @@ -7817,6 +8065,8 @@ snapshots: assertion-error@1.1.0: {} + assertion-error@2.0.1: {} + astral-regex@2.0.0: optional: true @@ -7878,7 +8128,7 @@ snapshots: bluebird@3.7.2: {} - body-parser@1.20.1: + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -7888,8 +8138,8 @@ snapshots: http-errors: 2.0.0 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.1 + qs: 6.13.0 + raw-body: 2.5.2 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: @@ -7971,14 +8221,14 @@ snapshots: cacheable-lookup@7.0.0: {} - cacheable-request@10.2.14: + cacheable-request@12.0.1: dependencies: '@types/http-cache-semantics': 4.0.4 - get-stream: 6.0.1 + get-stream: 9.0.1 http-cache-semantics: 4.1.1 keyv: 4.5.4 mimic-response: 4.0.0 - normalize-url: 8.0.0 + normalize-url: 8.0.1 responselike: 3.0.0 cacheable-request@7.0.4: @@ -7996,6 +8246,14 @@ snapshots: function-bind: 1.1.2 get-intrinsic: 1.2.2 + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + callsites@3.1.0: {} camelcase@6.3.0: {} @@ -8017,6 +8275,14 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -8037,6 +8303,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + check-error@2.1.1: {} + chokidar@3.5.3: dependencies: anymatch: 3.1.3 @@ -8141,8 +8409,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-type@1.0.4: {} - content-type@1.0.5: {} convert-source-map@1.9.0: {} @@ -8151,7 +8417,7 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.5.0: {} + cookie@0.7.1: {} copy-to-clipboard@3.3.3: dependencies: @@ -8245,6 +8511,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} decompress-response@6.0.0: @@ -8255,6 +8525,8 @@ snapshots: dependencies: type-detect: 4.0.8 + deep-eql@5.0.2: {} + deep-equal@2.2.0: dependencies: call-bind: 1.0.2 @@ -8287,6 +8559,12 @@ snapshots: gopd: 1.0.1 has-property-descriptors: 1.0.1 + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + define-properties@1.1.4: dependencies: has-property-descriptors: 1.0.1 @@ -8440,6 +8718,8 @@ snapshots: encodeurl@1.0.2: {} + encodeurl@2.0.0: {} + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -8490,6 +8770,12 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.9 + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + es-get-iterator@1.1.3: dependencies: call-bind: 1.0.2 @@ -8502,6 +8788,8 @@ snapshots: isarray: 2.0.5 stop-iteration-iterator: 1.0.0 + es-module-lexer@1.5.4: {} + es-set-tostringtag@2.0.1: dependencies: get-intrinsic: 1.2.2 @@ -8573,31 +8861,32 @@ snapshots: '@esbuild/win32-ia32': 0.20.2 '@esbuild/win32-x64': 0.20.2 - esbuild@0.21.5: + esbuild@0.23.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 escalade@3.1.1: {} @@ -8787,6 +9076,8 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + expect-type@1.1.0: {} + expect@29.3.1: dependencies: '@jest/expect-utils': 29.3.1 @@ -8795,45 +9086,46 @@ snapshots: jest-message-util: 29.3.1 jest-util: 29.3.1 - express-static-gzip@2.1.7: + express-static-gzip@2.2.0: dependencies: - serve-static: 1.15.0 + parseurl: 1.3.3 + serve-static: 1.16.2 transitivePeerDependencies: - supports-color - express-validator@7.0.1: + express-validator@7.2.0: dependencies: lodash: 4.17.21 validator: 13.12.0 - express@4.18.2: + express@4.21.1: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.1 + body-parser: 1.20.3 content-disposition: 0.5.4 - content-type: 1.0.4 - cookie: 0.5.0 + content-type: 1.0.5 + cookie: 0.7.1 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.3.1 fresh: 0.5.2 http-errors: 2.0.0 - merge-descriptors: 1.0.1 + merge-descriptors: 1.0.3 methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.7 + path-to-regexp: 0.1.10 proxy-addr: 2.0.7 - qs: 6.11.0 + qs: 6.13.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.19.0 + serve-static: 1.16.2 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -8897,10 +9189,10 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.2.0: + finalhandler@1.3.1: dependencies: debug: 2.6.9 - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 @@ -9043,16 +9335,27 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.0 + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + get-nonce@1.0.1: {} get-stream@5.2.0: dependencies: pump: 3.0.0 - get-stream@6.0.1: {} - get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.0.0: dependencies: call-bind: 1.0.2 @@ -9167,19 +9470,19 @@ snapshots: p-cancelable: 2.1.1 responselike: 2.0.1 - got@14.0.0: + got@14.4.5: dependencies: - '@sindresorhus/is': 6.1.0 + '@sindresorhus/is': 7.0.1 '@szmarczak/http-timer': 5.0.1 cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 + cacheable-request: 12.0.1 decompress-response: 6.0.0 form-data-encoder: 4.0.2 - get-stream: 8.0.1 http2-wrapper: 2.2.1 lowercase-keys: 3.0.0 p-cancelable: 4.0.1 responselike: 3.0.0 + type-fest: 4.27.1 graceful-fs@4.2.11: {} @@ -9203,6 +9506,10 @@ snapshots: dependencies: get-intrinsic: 1.2.2 + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + has-proto@1.0.1: {} has-symbols@1.0.3: {} @@ -9404,6 +9711,8 @@ snapshots: is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.0 @@ -9636,6 +9945,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + loupe@3.1.2: {} + lowdb@7.0.1: dependencies: steno: 4.0.2 @@ -9662,6 +9973,10 @@ snapshots: lz-string@1.5.0: {} + magic-string@0.30.13: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.5: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -9677,7 +9992,7 @@ snapshots: media-typer@0.3.0: {} - merge-descriptors@1.0.1: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -9798,7 +10113,7 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-osc@9.0.2: + node-osc@9.1.4: dependencies: osc-min: 1.1.2 @@ -9808,7 +10123,7 @@ snapshots: normalize-url@6.1.0: {} - normalize-url@8.0.0: {} + normalize-url@8.0.1: {} npm-run-path@5.1.0: dependencies: @@ -9820,6 +10135,8 @@ snapshots: object-inspect@1.12.3: {} + object-inspect@1.13.3: {} + object-is@1.1.5: dependencies: call-bind: 1.0.2 @@ -9941,14 +10258,18 @@ snapshots: lru-cache: 10.1.0 minipass: 7.0.4 - path-to-regexp@0.1.7: {} + path-to-regexp@0.1.10: {} path-type@4.0.0: {} pathe@1.1.1: {} + pathe@1.1.2: {} + pathval@1.1.1: {} + pathval@2.0.0: {} + pend@1.2.0: {} picocolors@1.0.0: {} @@ -10048,6 +10369,10 @@ snapshots: dependencies: side-channel: 1.0.4 + qs@6.13.0: + dependencies: + side-channel: 1.0.6 + querystringify@2.2.0: {} queue-microtask@1.2.3: {} @@ -10058,7 +10383,7 @@ snapshots: range-parser@1.2.1: {} - raw-body@2.5.1: + raw-body@2.5.2: dependencies: bytes: 3.1.2 http-errors: 2.0.0 @@ -10344,7 +10669,7 @@ snapshots: semver@7.6.2: {} - send@0.18.0: + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -10367,12 +10692,12 @@ snapshots: type-fest: 0.13.1 optional: true - serve-static@1.15.0: + serve-static@1.16.2: dependencies: - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color @@ -10381,6 +10706,15 @@ snapshots: minimist: 1.2.8 on-headers: 1.0.2 + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + setprototypeof@1.2.0: {} shebang-command@2.0.0: @@ -10406,6 +10740,13 @@ snapshots: get-intrinsic: 1.2.2 object-inspect: 1.12.3 + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.3 + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -10465,6 +10806,8 @@ snapshots: std-env@3.6.0: {} + std-env@3.8.0: {} + steno@4.0.2: {} stop-iteration-iterator@1.0.0: @@ -10588,10 +10931,20 @@ snapshots: tinybench@2.5.1: {} + tinybench@2.9.0: {} + + tinyexec@0.3.1: {} + tinypool@0.8.4: {} + tinypool@1.0.2: {} + + tinyrainbow@1.2.0: {} + tinyspy@2.2.0: {} + tinyspy@3.0.2: {} + tmp-promise@3.0.3: dependencies: tmp: 0.2.1 @@ -10631,7 +10984,7 @@ snapshots: dependencies: typescript: 5.5.3 - ts-essentials@9.4.1(typescript@5.5.3): + ts-essentials@10.0.3(typescript@5.5.3): optionalDependencies: typescript: 5.5.3 @@ -10652,9 +11005,9 @@ snapshots: tslib: 1.14.1 typescript: 5.5.3 - tsx@4.16.2: + tsx@4.19.2: dependencies: - esbuild: 0.21.5 + esbuild: 0.23.1 get-tsconfig: 4.7.5 optionalDependencies: fsevents: 2.3.3 @@ -10701,6 +11054,8 @@ snapshots: type-fest@0.20.2: {} + type-fest@4.27.1: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -10816,6 +11171,23 @@ snapshots: - supports-color - terser + vite-node@2.1.5(@types/node@20.14.10)(sass@1.57.1): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + es-module-lexer: 1.5.4 + pathe: 1.1.2 + vite: 5.2.11(@types/node@20.14.10)(sass@1.57.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vite-plugin-compression2@0.12.0(rollup@4.17.2): dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.17.2) @@ -10889,6 +11261,41 @@ snapshots: - supports-color - terser + vitest@2.1.5(@types/node@20.14.10)(jsdom@21.1.0)(sass@1.57.1): + dependencies: + '@vitest/expect': 2.1.5 + '@vitest/mocker': 2.1.5(vite@5.2.11(@types/node@20.14.10)(sass@1.57.1)) + '@vitest/pretty-format': 2.1.5 + '@vitest/runner': 2.1.5 + '@vitest/snapshot': 2.1.5 + '@vitest/spy': 2.1.5 + '@vitest/utils': 2.1.5 + chai: 5.1.2 + debug: 4.3.7 + expect-type: 1.1.0 + magic-string: 0.30.13 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.1 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.2.11(@types/node@20.14.10)(sass@1.57.1) + vite-node: 2.1.5(@types/node@20.14.10)(sass@1.57.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.10 + jsdom: 21.1.0 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - stylus + - sugarss + - supports-color + - terser + w3c-xmlserializer@4.0.0: dependencies: xml-name-validator: 4.0.0 @@ -10962,6 +11369,11 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wmf@1.0.2: {} word-wrap@1.2.3: {} @@ -10978,6 +11390,8 @@ snapshots: ws@8.13.0: {} + ws@8.18.0: {} + xlsx@0.18.5: dependencies: adler-32: 1.3.1