From 5e011448c4d3d6abae54e89f71c23484d19fc68d Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Tue, 18 Nov 2025 14:42:48 +0000 Subject: [PATCH 1/9] chore: update version constant to 1.5.0 --- app/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/constants.ts b/app/lib/constants.ts index 764d47b..55aa5cd 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -1,4 +1,4 @@ -export const VERSION = '1.4.0' +export const VERSION = '1.5.0' export const RequiredVersions = { controller: VERSION, From ac6b53be0cc0b95dc5d311200d445df429f4dabc Mon Sep 17 00:00:00 2001 From: Adam Laycock Date: Sun, 23 Nov 2025 21:00:13 +0000 Subject: [PATCH 2/9] feat: Actions Buttons (#21) * feat: basic action button features * fix: bad path in link * feat: button trigger * feat: about page info for buttons * feat: add button to screen api response * feat: add zone ton buttons for actions that require a zone --- app/lib/constants.ts | 3 +- app/locales/en.ts | 48 +++- app/root.tsx | 4 + app/routes/_index.tsx | 41 +++- app/routes/about.tsx | 56 +++++ app/routes/button-api.enroll.tsx | 24 ++ app/routes/button-api.get-config.tsx | 34 +++ app/routes/button-api.log.tsx | 34 +++ app/routes/button-api.ping.tsx | 37 +++ app/routes/button-api.trigger.tsx | 48 ++++ app/routes/buttons.$button._index.tsx | 141 +++++++++++ app/routes/buttons.$button.delete.tsx | 18 ++ app/routes/buttons.$button.edit.tsx | 220 ++++++++++++++++++ app/routes/buttons._index.tsx | 99 ++++++++ app/routes/buttons.add.tsx | 124 ++++++++++ app/routes/sounder-api.get-status.tsx | 13 ++ app/routes/status.tsx | 6 +- .../migration.sql | 8 + .../migration.sql | 19 ++ .../migration.sql | 20 ++ .../migration.sql | 8 + .../migration.sql | 21 ++ .../migration.sql | 22 ++ .../migration.sql | 24 ++ prisma/schema.prisma | 36 ++- 25 files changed, 1101 insertions(+), 7 deletions(-) create mode 100644 app/routes/button-api.enroll.tsx create mode 100644 app/routes/button-api.get-config.tsx create mode 100644 app/routes/button-api.log.tsx create mode 100644 app/routes/button-api.ping.tsx create mode 100644 app/routes/button-api.trigger.tsx create mode 100644 app/routes/buttons.$button._index.tsx create mode 100644 app/routes/buttons.$button.delete.tsx create mode 100644 app/routes/buttons.$button.edit.tsx create mode 100644 app/routes/buttons._index.tsx create mode 100644 app/routes/buttons.add.tsx create mode 100644 prisma/migrations/20251118144444_add_action_buttons/migration.sql create mode 100644 prisma/migrations/20251119092757_add_pin_and_duration_configuration_to_the_buttons/migration.sql create mode 100644 prisma/migrations/20251119115345_add_ip_to_button/migration.sql create mode 100644 prisma/migrations/20251119122451_add_action_button_log/migration.sql create mode 100644 prisma/migrations/20251119130829_add_enrolled_to_button/migration.sql create mode 100644 prisma/migrations/20251119204729_add_last_seen_to_action_button/migration.sql create mode 100644 prisma/migrations/20251123203946_add_zone_to_button/migration.sql diff --git a/app/lib/constants.ts b/app/lib/constants.ts index 55aa5cd..9f6a28e 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -4,7 +4,8 @@ export const RequiredVersions = { controller: VERSION, tts: '2.0.0', piper: '1.3.0', - sounder: '2.1.0' + sounder: '2.1.0', + button: '1.0.0' } export const DOCS_URL = `https://openschoolbell.co.uk` diff --git a/app/locales/en.ts b/app/locales/en.ts index 91a8c49..7af1c24 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -343,7 +343,53 @@ export const en = { 'zones.edit.pageTitle': 'Edit zone {{name}}', 'zones.detail.metaFallback': 'Zone', 'zones.detail.soundersTitle': 'Sounders', - 'zones.detail.editButton': 'Edit zone' + 'zones.detail.editButton': 'Edit zone', + 'buttons.titleWithCount': 'Buttons ({{count}})', + 'buttons.table.device': 'Button', + 'buttons.table.action': 'Action', + 'nav.buttons': 'Buttons', + 'buttons.addButton': 'Add Button', + 'buttons.metaTitle': 'Buttons', + 'buttons.add.pageTitle': 'Add Button', + 'buttons.form.name.label': 'Name', + 'buttons.form.name.helper': 'Descriptive name of the button.', + 'buttons.form.ip.label': 'IP address', + 'buttons.form.ip.helper': + 'IP address where the controller can reach the button.', + 'buttons.form.action.label': 'Action', + 'buttons.form.action.helper': 'The action to be triggered by this button', + 'buttons.form.zone.label': 'Zone', + 'buttons.form.zone.helper': 'The zone to use when triggering the action.', + 'buttons.form.ledPin.label': 'LED Pin', + 'buttons.form.ledPin.helper': 'The GPIO output pin connected to the LED.', + 'buttons.form.buttonPin.label': 'Button Pin', + 'buttons.form.buttonPin.helper': + 'The GPIO input pin connected to the Button.', + 'buttons.form.holdDuration.label': 'Hold Duration', + 'buttons.form.holdDuration.helper': + 'How long in seconds should the button be held to trigger the action.', + 'buttons.form.cancelDuration.label': 'Cancel Duration', + 'buttons.form.cancelDuration.helper': + 'How long in seconds does the user have to cancel the trigger.', + 'buttons.add.submit': 'Add Button', + 'buttons.edit.submit': 'Update Button', + 'buttons.deleteConfirmation': + 'Are you sure you want to delete the button {{name}}?', + 'buttons.detail.metaFallback': 'Button', + 'buttons.detail.infoTitle': 'About', + 'buttons.detail.ipLabel': 'IP', + 'buttons.detail.keyLabel': 'Key', + 'buttons.detail.logTitle': 'Log', + 'buttons.detail.editButton': 'Edit button', + 'buttons.detail.ledPinLabel': 'LED Pin', + 'buttons.detail.buttonPinLabel': 'Button Pin', + 'buttons.detail.holdLabel': 'Hold Duration', + 'buttons.detail.cancelLabel': 'Cancel Duration', + 'buttons.detail.actionLabel': 'Action', + 'buttons.detail.zoneLabel': 'Zone', + 'buttons.edit.metaTitle': 'Edit {{name}}', + 'buttons.edit.pageTitle': 'Edit button {{name}}', + 'dashboard.buttons': 'Buttons' } as const export type EnMessages = typeof en diff --git a/app/root.tsx b/app/root.tsx index d0f2b97..85d23d9 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -28,6 +28,7 @@ import LockClosedIcon from '@heroicons/react/24/outline/LockClosedIcon' import MusicIcon from '@heroicons/react/24/outline/MusicalNoteIcon' import CodeIcon from '@heroicons/react/24/outline/CodeBracketIcon' import LogIcon from '@heroicons/react/24/outline/ClipboardDocumentCheckIcon' +import ButtonIcon from '@heroicons/react/24/outline/ArrowDownOnSquareIcon' import './tailwind.css' @@ -112,6 +113,9 @@ const AppContent = () => { {t('nav.actions')} + + {t('nav.buttons')} + {t('nav.webhooks')} diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index d9aed0a..550e837 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -26,10 +26,11 @@ export const loader = async ({request}: LoaderFunctionArgs) => { const prisma = getPrisma() const sounders = await prisma.sounder.findMany({orderBy: {name: 'asc'}}) + const buttons = await prisma.actionButton.findMany({orderBy: {name: 'asc'}}) const lockdownMode = await getSetting('lockdownMode') - return {sounders, lockdownMode} + return {sounders, lockdownMode, buttons} } export const meta: MetaFunction = ({matches}) => { @@ -38,7 +39,7 @@ export const meta: MetaFunction = ({matches}) => { } export default function Index() { - const {sounders, lockdownMode} = useLoaderData() + const {sounders, lockdownMode, buttons} = useLoaderData() const {t, locale} = useTranslation() const dateLocale = locale === 'pl' ? pl : enUS @@ -119,6 +120,42 @@ export default function Index() { +
+

{t('dashboard.buttons')}

+ + + + + + + + + + {buttons.map(({id, name, lastCheckIn}) => { + return ( + + + + + + ) + })} + +
{t('dashboard.table.name')}{t('dashboard.table.status')}{t('dashboard.table.lastSeen')}
+ {name} + + {new Date().getTime() / 1000 - + lastCheckIn.getTime() / 1000 < + 65 + ? '🟢' + : '🔴'} + + {formatDistance(lastCheckIn, new Date(), { + addSuffix: true, + locale: dateLocale + })} +
+
) diff --git a/app/routes/about.tsx b/app/routes/about.tsx index 91728f4..e0a4632 100644 --- a/app/routes/about.tsx +++ b/app/routes/about.tsx @@ -112,6 +112,25 @@ export const loader = async ({request}: LoaderFunctionArgs) => { .catch(() => resolve('error')) }) + const buttonLatest = await new Promise(resolve => { + fetch( + 'https://api.github.com/repos/Open-School-Bell/action-button/releases?per_page=1', + { + headers: { + Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28' + } + } + ) + .then(response => { + response + .json() + .then(data => resolve(data[0].tag_name)) + .catch(() => resolve('error')) + }) + .catch(() => resolve('error')) + }) + const prisma = getPrisma() const redis = getRedis() @@ -128,6 +147,19 @@ export const loader = async ({request}: LoaderFunctionArgs) => { sounderVersions[id] = version ? version : '0.0.0' }) + const buttons = await prisma.actionButton.findMany({ + select: {id: true, name: true}, + orderBy: {name: 'asc'} + }) + + const buttonVersions: {[buttonId: string]: string} = {} + + await asyncForEach(buttons, async ({id}) => { + const version = await redis.get(`osb-button-version-${id}`) + + buttonVersions[id] = version ? version : '0.0.0' + }) + const license = ( await readFile(path.join(process.cwd(), 'LICENSE')) ).toString() @@ -137,6 +169,9 @@ export const loader = async ({request}: LoaderFunctionArgs) => { sounders, sounderVersions, sounderLatest, + buttons, + buttonVersions, + buttonLatest, ttsLatest, controllerLatest, license @@ -154,6 +189,9 @@ const About = () => { sounders, sounderVersions, sounderLatest, + buttons, + buttonVersions, + buttonLatest, ttsLatest, controllerLatest, license @@ -228,6 +266,24 @@ const About = () => { ) })} + {buttons.map(({id, name}) => { + return ( + + {`Button: ${name}`} + {buttonVersions[id]} + + {buttonLatest.replace('v', '')} + + + {RequiredVersions.button} + + + ) + })} diff --git a/app/routes/button-api.enroll.tsx b/app/routes/button-api.enroll.tsx new file mode 100644 index 0000000..0873df2 --- /dev/null +++ b/app/routes/button-api.enroll.tsx @@ -0,0 +1,24 @@ +import {type ActionFunctionArgs} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' + +export const action = async ({request}: ActionFunctionArgs) => { + const {key} = (await request.json()) as {key?: string} + + if (!key || typeof key !== 'string') { + return Response.json({error: 'missing key'}, {status: 400}) + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirstOrThrow({ + where: {key, enrolled: false} + }) + + await prisma.actionButton.update({ + where: {id: button.id}, + data: {enrolled: true} + }) + + return Response.json({id: button.id, name: button.name}) +} diff --git a/app/routes/button-api.get-config.tsx b/app/routes/button-api.get-config.tsx new file mode 100644 index 0000000..3804764 --- /dev/null +++ b/app/routes/button-api.get-config.tsx @@ -0,0 +1,34 @@ +import {type ActionFunctionArgs} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {getSettings} from '~/lib/settings.server' + +export const action = async ({request}: ActionFunctionArgs) => { + const {key} = (await request.json()) as {key?: string} + + if (!key || typeof key !== 'string') { + return Response.json({error: 'missing key'}, {status: 400}) + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirst({ + where: {key, enrolled: true} + }) + + if (!button) { + return Response.json({error: 'invalid key'}, {status: 403}) + } + + const {lockdownMode} = await getSettings(['lockdownMode']) + + return Response.json({ + name: button.name, + id: button.id, + ledPin: button.ledPin, + buttonPin: button.buttonPin, + holdDuration: button.holdDuration, + cancelDuration: button.cancelDuration, + lockdown: lockdownMode === '1' + }) +} diff --git a/app/routes/button-api.log.tsx b/app/routes/button-api.log.tsx new file mode 100644 index 0000000..20352db --- /dev/null +++ b/app/routes/button-api.log.tsx @@ -0,0 +1,34 @@ +import {type ActionFunctionArgs} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' + +export const action = async ({request}: ActionFunctionArgs) => { + const {key, message} = (await request.json()) as { + key?: string + message?: string + } + + if (!key || typeof key !== 'string') { + return Response.json({error: 'missing key'}, {status: 400}) + } + + if (!message || typeof message !== 'string') { + return Response.json({error: 'missing message'}, {status: 400}) + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirst({ + where: {key, enrolled: true} + }) + + if (!button) { + return Response.json({error: 'invalid key'}, {status: 403}) + } + + await prisma.actionButtonLog.create({ + data: {message, actionButtonId: button.id} + }) + + return Response.json({status: 'ok'}) +} diff --git a/app/routes/button-api.ping.tsx b/app/routes/button-api.ping.tsx new file mode 100644 index 0000000..4d244fb --- /dev/null +++ b/app/routes/button-api.ping.tsx @@ -0,0 +1,37 @@ +import {type ActionFunctionArgs} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {getRedis} from '~/lib/redis.server.mjs' + +export const action = async ({request}: ActionFunctionArgs) => { + const {key, version} = (await request.json()) as { + key?: string + version?: string + } + + if (!key || typeof key !== 'string') { + return Response.json({error: 'missing key'}, {status: 400}) + } + + const prisma = getPrisma() + const redis = getRedis() + + const button = await prisma.actionButton.findFirst({ + where: {key, enrolled: true} + }) + + if (!button) { + return Response.json({error: 'invalid key'}, {status: 403}) + } + + await prisma.actionButton.update({ + where: {id: button.id}, + data: {lastCheckIn: new Date()} + }) + + if (version) { + void redis.set(`osb-button-version-${button.id}`, version) + } + + return Response.json({ping: 'pong'}) +} diff --git a/app/routes/button-api.trigger.tsx b/app/routes/button-api.trigger.tsx new file mode 100644 index 0000000..745fea7 --- /dev/null +++ b/app/routes/button-api.trigger.tsx @@ -0,0 +1,48 @@ +import {type ActionFunctionArgs} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {broadcast} from '~/lib/broadcast.server' +import {toggleLockdown} from '~/lib/lockdown.server' + +export const action = async ({request}: ActionFunctionArgs) => { + const {key} = (await request.json()) as { + key?: string + } + + if (!key || typeof key !== 'string') { + return Response.json({error: 'missing key'}, {status: 400}) + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirst({ + where: {key, enrolled: true}, + include: {action: true} + }) + + if (!button) { + return Response.json({error: 'sounder not found'}, {status: 401}) + } + + const zone = button.zoneId + + switch (button.action.action) { + case 'broadcast': + if (!zone || typeof zone !== 'string' || zone.trim() === '') { + return Response.json({error: 'missing zone'}, {status: 400}) + } + + if (button.action.audioId) { + const zoneId = zone.trim() + await broadcast(zoneId, JSON.stringify([button.action.audioId])) + } + break + case 'lockdown': + await toggleLockdown() + break + default: + break + } + + return Response.json({ping: 'pong'}) +} diff --git a/app/routes/buttons.$button._index.tsx b/app/routes/buttons.$button._index.tsx new file mode 100644 index 0000000..de3c7c1 --- /dev/null +++ b/app/routes/buttons.$button._index.tsx @@ -0,0 +1,141 @@ +import { + type LoaderFunctionArgs, + type MetaFunction, + redirect +} from '@remix-run/node' +import {useLoaderData, useNavigate, Link} from '@remix-run/react' +import {format} from 'date-fns' + +import {getPrisma} from '~/lib/prisma.server' +import {checkSession} from '~/lib/session' +import {INPUT_CLASSES, pageTitle} from '~/lib/utils' +import {Page, Actions} from '~/lib/ui' +import {getSetting} from '~/lib/settings.server' +import {useTranslation} from '~/lib/i18n' +import {translate} from '~/lib/i18n.shared' +import {getRootI18n} from '~/lib/i18n.meta' + +export const meta: MetaFunction = ({data, matches}) => { + const {messages} = getRootI18n(matches) + const name = data + ? data.button.name + : translate(messages, 'buttons.detail.metaFallback') + + return [{title: pageTitle(translate(messages, 'buttons.metaTitle'), name)}] +} + +export const loader = async ({request, params}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirstOrThrow({ + where: {id: params.sounder}, + include: { + action: true, + zone: true, + logs: {orderBy: {time: 'desc'}, take: 10} + } + }) + + const enrollUrl = await getSetting('enrollUrl') + + return {button, enrollUrl} +} + +const Sounder = () => { + const {button, enrollUrl} = useLoaderData() + const navigate = useNavigate() + const {t} = useTranslation() + + return ( + +
+
+

{t('buttons.detail.infoTitle')}

+

+ {t('buttons.detail.ipLabel')}: {button.ip} +

+

+ {t('buttons.detail.ledPinLabel')}: {button.ledPin} +

+

+ {t('buttons.detail.buttonPinLabel')}: {button.buttonPin} +

+

+ {t('buttons.detail.holdLabel')}: {button.holdDuration} +

+

+ {t('buttons.detail.cancelLabel')}: {button.cancelDuration} +

+

+ {t('buttons.detail.actionLabel')}:{' '} + {button.action.name} +

+

+ {t('buttons.detail.zoneLabel')}:{' '} + + {button.zone ? button.zone.name : 'None'} + +

+ {button.enrolled ? ( +
+ +
+ ) : ( + <> +

+ {t('buttons.detail.keyLabel')}: {button.key} +

+
+
+                  button --enroll {button.key} --controller {enrollUrl}
+                
+
+ + )} +
+
+

{t('buttons.detail.logTitle')}

+ + + + + + + + + {button.logs.map(({id, message, time}) => { + return ( + + + + + ) + })} + +
{t('log.columns.time')}{t('log.columns.message')}
+ {format(time, 'dd/MM/yy HH:mm')} + {message}
+
+
+ navigate(`/buttons/${button.id}/edit`) + } + ]} + /> +
+ ) +} + +export default Sounder diff --git a/app/routes/buttons.$button.delete.tsx b/app/routes/buttons.$button.delete.tsx new file mode 100644 index 0000000..f760cbf --- /dev/null +++ b/app/routes/buttons.$button.delete.tsx @@ -0,0 +1,18 @@ +import {type ActionFunctionArgs, redirect} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {checkSession} from '~/lib/session' + +export const action = async ({request, params}: ActionFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + await prisma.actionButton.delete({where: {id: params.button}}) + + return redirect('/buttons') +} diff --git a/app/routes/buttons.$button.edit.tsx b/app/routes/buttons.$button.edit.tsx new file mode 100644 index 0000000..3b31b61 --- /dev/null +++ b/app/routes/buttons.$button.edit.tsx @@ -0,0 +1,220 @@ +import { + redirect, + type ActionFunctionArgs, + type LoaderFunctionArgs, + type MetaFunction +} from '@remix-run/node' +import {useLoaderData, useNavigate} from '@remix-run/react' +import {invariant} from '@arcath/utils' + +import {getPrisma} from '~/lib/prisma.server' +import {INPUT_CLASSES, pageTitle} from '~/lib/utils' +import {checkSession} from '~/lib/session' +import {Page, FormElement, Actions} from '~/lib/ui' +import {useTranslation} from '~/lib/i18n' +import {translate} from '~/lib/i18n.shared' +import {getRootI18n} from '~/lib/i18n.meta' + +export const meta: MetaFunction = ({data, matches}) => { + const {messages} = getRootI18n(matches) + return [ + { + title: pageTitle( + translate(messages, 'buttons.metaTitle'), + data + ? translate(messages, 'buttons.edit.metaTitle', { + name: data.button.name + }) + : translate(messages, 'buttons.edit.metaTitle', {name: ''}) + ) + } + ] +} + +export const loader = async ({request, params}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const actions = await prisma.action.findMany({orderBy: {name: 'asc'}}) + const zones = await prisma.zone.findMany({orderBy: {name: 'asc'}}) + const button = await prisma.actionButton.findFirstOrThrow({ + where: {id: params.button} + }) + + return {actions, button, zones} +} + +export const action = async ({request, params}: ActionFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const formData = await request.formData() + + const name = formData.get('name') as string | undefined + const ip = formData.get('ip') as string | undefined + const action = formData.get('action') as string | undefined + const zone = formData.get('zone') as string | undefined + const ledPin = formData.get('ledpin') as string | undefined + const buttonPin = formData.get('buttonpin') as string | undefined + const holdDuration = formData.get('holdduration') as string | undefined + const cancelDuration = formData.get('cancelduration') as string | undefined + + invariant(name) + invariant(ip) + invariant(action) + invariant(zone) + invariant(ledPin) + invariant(buttonPin) + invariant(holdDuration) + invariant(cancelDuration) + + const button = await prisma.actionButton.update({ + where: {id: params.button}, + data: { + name, + ip, + actionId: action, + zoneId: zone, + ledPin: parseInt(ledPin), + buttonPin: parseInt(buttonPin), + holdDuration: parseInt(holdDuration), + cancelDuration: parseInt(cancelDuration) + } + }) + + return redirect(`/buttons/${button.id}`) +} + +const EditButton = () => { + const {actions, button, zones} = useLoaderData() + const navigate = useNavigate() + const {t} = useTranslation() + + return ( + +
+ + + + + + + + + + + + + + + + + + + + + + + + + { + e.preventDefault() + navigate('/buttons') + } + }, + {label: t('buttons.edit.submit'), color: 'bg-green-300'} + ]} + /> + +
+ ) +} + +export default EditButton diff --git a/app/routes/buttons._index.tsx b/app/routes/buttons._index.tsx new file mode 100644 index 0000000..bb3296d --- /dev/null +++ b/app/routes/buttons._index.tsx @@ -0,0 +1,99 @@ +import { + type LoaderFunctionArgs, + type MetaFunction, + redirect +} from '@remix-run/node' +import {useLoaderData, Link, useNavigate} from '@remix-run/react' + +import {getPrisma} from '~/lib/prisma.server' +import {checkSession} from '~/lib/session' +import {pageTitle} from '~/lib/utils' +import {Page, Actions} from '~/lib/ui' +import {useTranslation} from '~/lib/i18n' +import {translate} from '~/lib/i18n.shared' +import {getRootI18n} from '~/lib/i18n.meta' + +export const meta: MetaFunction = ({matches}) => { + const {messages} = getRootI18n(matches) + return [{title: pageTitle(translate(messages, 'buttons.metaTitle'))}] +} + +export const loader = async ({request}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const buttons = await prisma.actionButton.findMany({ + orderBy: {name: 'asc'}, + include: {action: true} + }) + + return {buttons} +} + +const Sounders = () => { + const {buttons} = useLoaderData() + const navigate = useNavigate() + const {t} = useTranslation() + + return ( + +
+ + + + + + + + + + {buttons.map(({id, name, action}) => { + return ( + + + + + + ) + })} + +
{t('buttons.table.device')}{t('buttons.table.action')}
+ {name} + + {action.name} + +
{ + if (!confirm(t('buttons.deleteConfirmation', {name}))) { + e.preventDefault() + } + }} + > + +
+
+
+ navigate('/buttons/add') + } + ]} + /> +
+ ) +} + +export default Sounders diff --git a/app/routes/buttons.add.tsx b/app/routes/buttons.add.tsx new file mode 100644 index 0000000..51b719e --- /dev/null +++ b/app/routes/buttons.add.tsx @@ -0,0 +1,124 @@ +import { + redirect, + type ActionFunctionArgs, + type LoaderFunctionArgs, + type MetaFunction +} from '@remix-run/node' +import {useLoaderData, useNavigate} from '@remix-run/react' +import {invariant} from '@arcath/utils' + +import {getPrisma} from '~/lib/prisma.server' +import {makeKey, INPUT_CLASSES, pageTitle} from '~/lib/utils' +import {checkSession} from '~/lib/session' +import {Page, FormElement, Actions} from '~/lib/ui' +import {useTranslation} from '~/lib/i18n' +import {translate} from '~/lib/i18n.shared' +import {getRootI18n} from '~/lib/i18n.meta' + +export const meta: MetaFunction = ({matches}) => { + const {messages} = getRootI18n(matches) + return [ + { + title: pageTitle( + translate(messages, 'buttons.metaTitle'), + translate(messages, 'buttons.add.pageTitle') + ) + } + ] +} + +export const loader = async ({request}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const actions = await prisma.action.findMany({orderBy: {name: 'asc'}}) + + return {actions} +} + +export const action = async ({request}: ActionFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const formData = await request.formData() + + const name = formData.get('name') as string | undefined + const ip = formData.get('ip') as string | undefined + const action = formData.get('action') as string | undefined + + invariant(name) + invariant(ip) + invariant(action) + + const key = makeKey() + + const button = await prisma.actionButton.create({ + data: {name, key, ip, actionId: action} + }) + + return redirect(`/buttons/${button.id}`) +} + +const AddButton = () => { + const {actions} = useLoaderData() + const navigate = useNavigate() + const {t} = useTranslation() + + return ( + +
+ + + + + + + + + + { + e.preventDefault() + navigate('/buttons') + } + }, + {label: t('buttons.add.submit'), color: 'bg-green-300'} + ]} + /> + +
+ ) +} + +export default AddButton diff --git a/app/routes/sounder-api.get-status.tsx b/app/routes/sounder-api.get-status.tsx index fcbd5b2..2a67fc8 100644 --- a/app/routes/sounder-api.get-status.tsx +++ b/app/routes/sounder-api.get-status.tsx @@ -22,6 +22,7 @@ export const action = async ({request}: ActionFunctionArgs) => { } const sounders = await prisma.sounder.findMany({orderBy: {name: 'asc'}}) + const buttons = await prisma.actionButton.findMany({orderBy: {name: 'asc'}}) return Response.json({ system: 'ok', @@ -38,6 +39,18 @@ export const action = async ({request}: ActionFunctionArgs) => { ? '🟢' : '🔴' } + }), + buttons: buttons.map(button => { + return { + ...button, + lastSeen: formatDistance(new Date(button.lastCheckIn), new Date(), { + addSuffix: true + }), + status: + new Date().getTime() / 1000 - button.lastCheckIn.getTime() / 1000 < 65 + ? '🟢' + : '🔴' + } }) }) } diff --git a/app/routes/status.tsx b/app/routes/status.tsx index 97f56a8..e50467e 100644 --- a/app/routes/status.tsx +++ b/app/routes/status.tsx @@ -13,6 +13,9 @@ export const loader = async ({}: LoaderFunctionArgs) => { select: {id: true, name: true, lastCheckIn: true, enrolled: true, ip: true} }) const zones = await prisma.zone.findMany() + const buttons = await prisma.actionButton.findMany({ + select: {id: true, name: true, lastCheckIn: true, enrolled: true, ip: true} + }) const date = new Date() date.setHours(0, 0, 0, 0) @@ -27,6 +30,7 @@ export const loader = async ({}: LoaderFunctionArgs) => { lockdown: lockdownMode === '1', sounders, version: VERSION, - zones + zones, + buttons }) } diff --git a/prisma/migrations/20251118144444_add_action_buttons/migration.sql b/prisma/migrations/20251118144444_add_action_buttons/migration.sql new file mode 100644 index 0000000..f4f5072 --- /dev/null +++ b/prisma/migrations/20251118144444_add_action_buttons/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "actionId" TEXT NOT NULL, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); diff --git a/prisma/migrations/20251119092757_add_pin_and_duration_configuration_to_the_buttons/migration.sql b/prisma/migrations/20251119092757_add_pin_and_duration_configuration_to_the_buttons/migration.sql new file mode 100644 index 0000000..3d60718 --- /dev/null +++ b/prisma/migrations/20251119092757_add_pin_and_duration_configuration_to_the_buttons/migration.sql @@ -0,0 +1,19 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "ledPin" INTEGER NOT NULL DEFAULT 0, + "buttonPin" INTEGER NOT NULL DEFAULT 0, + "holdDuration" INTEGER NOT NULL DEFAULT 0, + "cancelDuration" INTEGER NOT NULL DEFAULT 0, + "actionId" TEXT NOT NULL, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ActionButton" ("actionId", "id", "key", "name") SELECT "actionId", "id", "key", "name" FROM "ActionButton"; +DROP TABLE "ActionButton"; +ALTER TABLE "new_ActionButton" RENAME TO "ActionButton"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/migrations/20251119115345_add_ip_to_button/migration.sql b/prisma/migrations/20251119115345_add_ip_to_button/migration.sql new file mode 100644 index 0000000..42908c1 --- /dev/null +++ b/prisma/migrations/20251119115345_add_ip_to_button/migration.sql @@ -0,0 +1,20 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "ip" TEXT NOT NULL DEFAULT '', + "ledPin" INTEGER NOT NULL DEFAULT 0, + "buttonPin" INTEGER NOT NULL DEFAULT 0, + "holdDuration" INTEGER NOT NULL DEFAULT 0, + "cancelDuration" INTEGER NOT NULL DEFAULT 0, + "actionId" TEXT NOT NULL, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ActionButton" ("actionId", "buttonPin", "cancelDuration", "holdDuration", "id", "key", "ledPin", "name") SELECT "actionId", "buttonPin", "cancelDuration", "holdDuration", "id", "key", "ledPin", "name" FROM "ActionButton"; +DROP TABLE "ActionButton"; +ALTER TABLE "new_ActionButton" RENAME TO "ActionButton"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/migrations/20251119122451_add_action_button_log/migration.sql b/prisma/migrations/20251119122451_add_action_button_log/migration.sql new file mode 100644 index 0000000..d3c727a --- /dev/null +++ b/prisma/migrations/20251119122451_add_action_button_log/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "ActionButtonLog" ( + "id" TEXT NOT NULL PRIMARY KEY, + "time" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "message" TEXT NOT NULL, + "actionButtonId" TEXT NOT NULL, + CONSTRAINT "ActionButtonLog_actionButtonId_fkey" FOREIGN KEY ("actionButtonId") REFERENCES "ActionButton" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); diff --git a/prisma/migrations/20251119130829_add_enrolled_to_button/migration.sql b/prisma/migrations/20251119130829_add_enrolled_to_button/migration.sql new file mode 100644 index 0000000..d5edd63 --- /dev/null +++ b/prisma/migrations/20251119130829_add_enrolled_to_button/migration.sql @@ -0,0 +1,21 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "ip" TEXT NOT NULL DEFAULT '', + "enrolled" BOOLEAN NOT NULL DEFAULT false, + "ledPin" INTEGER NOT NULL DEFAULT 0, + "buttonPin" INTEGER NOT NULL DEFAULT 0, + "holdDuration" INTEGER NOT NULL DEFAULT 0, + "cancelDuration" INTEGER NOT NULL DEFAULT 0, + "actionId" TEXT NOT NULL, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ActionButton" ("actionId", "buttonPin", "cancelDuration", "holdDuration", "id", "ip", "key", "ledPin", "name") SELECT "actionId", "buttonPin", "cancelDuration", "holdDuration", "id", "ip", "key", "ledPin", "name" FROM "ActionButton"; +DROP TABLE "ActionButton"; +ALTER TABLE "new_ActionButton" RENAME TO "ActionButton"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/migrations/20251119204729_add_last_seen_to_action_button/migration.sql b/prisma/migrations/20251119204729_add_last_seen_to_action_button/migration.sql new file mode 100644 index 0000000..64f2d49 --- /dev/null +++ b/prisma/migrations/20251119204729_add_last_seen_to_action_button/migration.sql @@ -0,0 +1,22 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "ip" TEXT NOT NULL DEFAULT '', + "enrolled" BOOLEAN NOT NULL DEFAULT false, + "lastCheckIn" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "ledPin" INTEGER NOT NULL DEFAULT 0, + "buttonPin" INTEGER NOT NULL DEFAULT 0, + "holdDuration" INTEGER NOT NULL DEFAULT 0, + "cancelDuration" INTEGER NOT NULL DEFAULT 0, + "actionId" TEXT NOT NULL, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_ActionButton" ("actionId", "buttonPin", "cancelDuration", "enrolled", "holdDuration", "id", "ip", "key", "ledPin", "name") SELECT "actionId", "buttonPin", "cancelDuration", "enrolled", "holdDuration", "id", "ip", "key", "ledPin", "name" FROM "ActionButton"; +DROP TABLE "ActionButton"; +ALTER TABLE "new_ActionButton" RENAME TO "ActionButton"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/migrations/20251123203946_add_zone_to_button/migration.sql b/prisma/migrations/20251123203946_add_zone_to_button/migration.sql new file mode 100644 index 0000000..f10b838 --- /dev/null +++ b/prisma/migrations/20251123203946_add_zone_to_button/migration.sql @@ -0,0 +1,24 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_ActionButton" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "key" TEXT NOT NULL, + "ip" TEXT NOT NULL DEFAULT '', + "enrolled" BOOLEAN NOT NULL DEFAULT false, + "lastCheckIn" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "ledPin" INTEGER NOT NULL DEFAULT 0, + "buttonPin" INTEGER NOT NULL DEFAULT 0, + "holdDuration" INTEGER NOT NULL DEFAULT 0, + "cancelDuration" INTEGER NOT NULL DEFAULT 0, + "actionId" TEXT NOT NULL, + "zoneId" TEXT, + CONSTRAINT "ActionButton_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "Action" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "ActionButton_zoneId_fkey" FOREIGN KEY ("zoneId") REFERENCES "Zone" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +INSERT INTO "new_ActionButton" ("actionId", "buttonPin", "cancelDuration", "enrolled", "holdDuration", "id", "ip", "key", "lastCheckIn", "ledPin", "name") SELECT "actionId", "buttonPin", "cancelDuration", "enrolled", "holdDuration", "id", "ip", "key", "lastCheckIn", "ledPin", "name" FROM "ActionButton"; +DROP TABLE "ActionButton"; +ALTER TABLE "new_ActionButton" RENAME TO "ActionButton"; +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5475afd..fcfdc5a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -40,8 +40,9 @@ model Zone { id String @id @default(uuid()) name String - sounders ZoneSounder[] - schedules Schedule[] + sounders ZoneSounder[] + schedules Schedule[] + ActionButton ActionButton[] } model ZoneSounder { @@ -110,6 +111,7 @@ model Action { audioId String? webhooks Webhook[] + buttons ActionButton[] } model Setting { @@ -146,3 +148,33 @@ model OutboundWebhook { event String key String } + +model ActionButton { + id String @id @default(uuid()) + name String + key String + ip String @default("") + enrolled Boolean @default(false) + lastCheckIn DateTime @default(now()) + + ledPin Int @default(0) + buttonPin Int @default(0) + holdDuration Int @default(0) + cancelDuration Int @default(0) + + action Action @relation(fields: [actionId], references: [id]) + actionId String + zone Zone? @relation(fields: [zoneId], references: [id]) + zoneId String? + + logs ActionButtonLog[] +} + +model ActionButtonLog { + id String @id @default(uuid()) + time DateTime @default(now()) + message String + + actionButton ActionButton @relation(fields: [actionButtonId], references: [id]) + actionButtonId String +} From d063e96e1ba371dd00f69ac91ba3101e3375381a Mon Sep 17 00:00:00 2001 From: Adam Laycock Date: Mon, 24 Nov 2025 20:31:52 +0000 Subject: [PATCH 3/9] fix: add missing reset for buttons --- app/routes/buttons.$button.reset.tsx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/routes/buttons.$button.reset.tsx diff --git a/app/routes/buttons.$button.reset.tsx b/app/routes/buttons.$button.reset.tsx new file mode 100644 index 0000000..dd8a97c --- /dev/null +++ b/app/routes/buttons.$button.reset.tsx @@ -0,0 +1,26 @@ +import {type ActionFunctionArgs, redirect} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {makeKey} from '~/lib/utils' +import {checkSession} from '~/lib/session' + +export const action = async ({request, params}: ActionFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirstOrThrow({ + where: {id: params.button} + }) + + await prisma.actionButton.update({ + where: {id: button.id}, + data: {enrolled: false, key: makeKey()} + }) + + return redirect(`/buttons/${button.id}`) +} From 212d802559334e9f38dd8d9295c0715b82f35070 Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Tue, 25 Nov 2025 12:53:57 +0000 Subject: [PATCH 4/9] chore: update deps --- package-lock.json | 22 ++++++++++++++++++++++ package.json | 34 +++++++++++++++++----------------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d213b6..b6caca9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -123,6 +123,7 @@ "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -1954,6 +1955,7 @@ "resolved": "https://registry.npmjs.org/@remix-run/react/-/react-2.17.0.tgz", "integrity": "sha512-muOLHqcimMCrIk6VOuqIn51P3buYjKpdYc6qpNy6zE5HlKfyaKEY00a5pzdutRmevYTQy7FiEF/LK4M8sxk70Q==", "license": "MIT", + "peer": true, "dependencies": { "@remix-run/router": "1.23.0", "@remix-run/server-runtime": "2.17.0", @@ -1989,6 +1991,7 @@ "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.17.0.tgz", "integrity": "sha512-eq0A7A89uqg+rQiGVHoUwb1NUawmPpjAY3RWn4KG4uCp4QwhqJausML63fIxnfdp7zu2OplXhfSXCNiTekQ0rw==", "license": "MIT", + "peer": true, "dependencies": { "@remix-run/express": "2.17.0", "@remix-run/node": "2.17.0", @@ -2898,6 +2901,7 @@ "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2973,6 +2977,7 @@ "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", @@ -3123,6 +3128,7 @@ "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.42.0", @@ -4074,6 +4080,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4632,6 +4639,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -5114,6 +5122,7 @@ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -5991,6 +6000,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6075,6 +6085,7 @@ "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -6246,6 +6257,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6280,6 +6292,7 @@ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", @@ -6884,6 +6897,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -8801,6 +8815,7 @@ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", "dev": true, "license": "MPL-2.0", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -11692,6 +11707,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11902,6 +11918,7 @@ "integrity": "sha512-E6RCgOt+kUVtjtZgLQDBJ6md2tDItLJNExwI0XJeBc1FKL+Vwb+ovxXxuok9r8oBgsOXBA33fGDuE/0qDdCWqQ==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "6.15.0", "@prisma/engines": "6.15.0" @@ -12136,6 +12153,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12148,6 +12166,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -13800,6 +13819,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14068,6 +14088,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15122,6 +15143,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index eb263ad..23cc6a9 100644 --- a/package.json +++ b/package.json @@ -20,47 +20,47 @@ "@arcath/utils": "^3.15.0", "@heroicons/react": "^2.2.0", "@prisma/client": "^6.15.0", - "@remix-run/node": "^2.17.0", - "@remix-run/react": "^2.17.0", - "@remix-run/serve": "^2.17.0", + "@remix-run/node": "^2.17.2", + "@remix-run/react": "^2.17.2", + "@remix-run/serve": "^2.17.2", "adm-zip": "^0.5.16", - "bullmq": "^5.58.5", + "bullmq": "^5.64.1", "date-fns": "^4.1.0", - "isbot": "^5.1.30", + "isbot": "^5.1.32", "jsonwebtoken": "^9.0.2", "mkdirp": "^3.0.1", - "music-metadata": "^11.9.0", + "music-metadata": "^11.10.3", "prisma": "^6.15.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "semver": "^7.7.2" + "semver": "^7.7.3" }, "devDependencies": { - "@remix-run/dev": "^2.17.0", - "@tailwindcss/postcss": "^4.1.13", + "@remix-run/dev": "^2.17.2", + "@tailwindcss/postcss": "^4.1.17", "@types/adm-zip": "^0.5.7", "@types/jsonwebtoken": "^9.0.10", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", "@types/semver": "^7.7.1", - "@typescript-eslint/eslint-plugin": "^8.37.0", - "@typescript-eslint/parser": "^8.37.0", - "autoprefixer": "^10.4.21", + "@typescript-eslint/eslint-plugin": "^8.47.0", + "@typescript-eslint/parser": "^8.47.0", + "autoprefixer": "^10.4.22", "env-cmd": "^11.0.0", - "eslint": "^9.34.0", + "eslint": "^9.39.1", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-import-x": "^4.16.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "globals": "^16.3.0", + "globals": "^16.5.0", "npm-run-all": "^4.1.5", "postcss": "^8.5.6", "prettier": "^3.6.2", - "tailwindcss": "^4.1.13", - "typescript": "5.9.2", - "typescript-eslint": "^8.42.0", + "tailwindcss": "^4.1.17", + "typescript": "5.9.3", + "typescript-eslint": "^8.47.0", "vite": "^6.3.5", "vite-tsconfig-paths": "^5.1.4" }, From d45eb5be80c909bcb8619d0cd27b954e7407fcbf Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Tue, 25 Nov 2025 12:59:46 +0000 Subject: [PATCH 5/9] fix: rollback eslint plugins --- package-lock.json | 950 +++++++++++++++++++--------------------------- package.json | 4 +- 2 files changed, 385 insertions(+), 569 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6caca9..cf244a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,24 @@ "@arcath/utils": "^3.15.0", "@heroicons/react": "^2.2.0", "@prisma/client": "^6.15.0", - "@remix-run/node": "^2.17.0", - "@remix-run/react": "^2.17.0", - "@remix-run/serve": "^2.17.0", + "@remix-run/node": "^2.17.2", + "@remix-run/react": "^2.17.2", + "@remix-run/serve": "^2.17.2", "adm-zip": "^0.5.16", - "bullmq": "^5.58.5", + "bullmq": "^5.64.1", "date-fns": "^4.1.0", - "isbot": "^5.1.30", + "isbot": "^5.1.32", "jsonwebtoken": "^9.0.2", "mkdirp": "^3.0.1", - "music-metadata": "^11.9.0", + "music-metadata": "^11.10.3", "prisma": "^6.15.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "semver": "^7.7.2" + "semver": "^7.7.3" }, "devDependencies": { - "@remix-run/dev": "^2.17.0", - "@tailwindcss/postcss": "^4.1.13", + "@remix-run/dev": "^2.17.2", + "@tailwindcss/postcss": "^4.1.17", "@types/adm-zip": "^0.5.7", "@types/jsonwebtoken": "^9.0.10", "@types/react": "^18.2.20", @@ -34,22 +34,22 @@ "@types/semver": "^7.7.1", "@typescript-eslint/eslint-plugin": "^8.37.0", "@typescript-eslint/parser": "^8.37.0", - "autoprefixer": "^10.4.21", + "autoprefixer": "^10.4.22", "env-cmd": "^11.0.0", - "eslint": "^9.34.0", + "eslint": "^9.39.1", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-import-x": "^4.16.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", - "globals": "^16.3.0", + "globals": "^16.5.0", "npm-run-all": "^4.1.5", "postcss": "^8.5.6", "prettier": "^3.6.2", - "tailwindcss": "^4.1.13", - "typescript": "5.9.2", - "typescript-eslint": "^8.42.0", + "tailwindcss": "^4.1.17", + "typescript": "5.9.3", + "typescript-eslint": "^8.47.0", "vite": "^6.3.5", "vite-tsconfig-paths": "^5.1.4" }, @@ -1071,9 +1071,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1100,13 +1100,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1139,19 +1139,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1223,9 +1226,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", - "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -1236,9 +1239,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1246,13 +1249,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1335,9 +1338,9 @@ } }, "node_modules/@ioredis/commands": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", - "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", "license": "MIT" }, "node_modules/@isaacs/cliui": { @@ -1387,19 +1390,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -1592,44 +1582,6 @@ "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@npmcli/fs": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", @@ -1796,9 +1748,9 @@ } }, "node_modules/@remix-run/dev": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/dev/-/dev-2.17.0.tgz", - "integrity": "sha512-L2W8PYH3jUvCKlJeUkFMGyOMzUsM0goZg4n0NU69O85TNlB1jgiqSbSMb69xhviphGpwzAoH+D/p3/cUnw4DbQ==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/dev/-/dev-2.17.2.tgz", + "integrity": "sha512-gfc4Hu2Sysr+GyU/F12d2uvEfQwUwWvsOjBtiemhdN1xGOn1+FyYzlLl/aJ7AL9oYko8sDqOPyJCiApWJJGCCw==", "dev": true, "license": "MIT", "dependencies": { @@ -1812,9 +1764,9 @@ "@babel/types": "^7.22.5", "@mdx-js/mdx": "^2.3.0", "@npmcli/package-json": "^4.0.1", - "@remix-run/node": "2.17.0", + "@remix-run/node": "2.17.2", "@remix-run/router": "1.23.0", - "@remix-run/server-runtime": "2.17.0", + "@remix-run/server-runtime": "2.17.2", "@types/mdx": "^2.0.5", "@vanilla-extract/integration": "^6.2.0", "arg": "^5.0.1", @@ -1904,12 +1856,12 @@ } }, "node_modules/@remix-run/express": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.17.0.tgz", - "integrity": "sha512-VUNpdrX3WSLPOkRBbsTQao5Vu/xdKcB8AY+44pAyC7iW5iIKHDb6EYlDvpbMLcMNh9ErYGhpPtshaBiBTMvjiw==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.17.2.tgz", + "integrity": "sha512-N3Rp4xuXWlUUboQxc8ATqvYiFX2DoX0cavWIsQ0pMKPyG1WOSO+MfuOFgHa7kf/ksRteSfDtzgJSgTH6Fv96AA==", "license": "MIT", "dependencies": { - "@remix-run/node": "2.17.0" + "@remix-run/node": "2.17.2" }, "engines": { "node": ">=18.0.0" @@ -1925,12 +1877,12 @@ } }, "node_modules/@remix-run/node": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.17.0.tgz", - "integrity": "sha512-ISy3N4peKB+Fo8ddh+mU6ki3HzQqLXwJxUrAtqxYxrBDM4Pwc7EvISrcQ4QasB6ORBknJeEZSBu69WDRhGzrjA==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.17.2.tgz", + "integrity": "sha512-NHBIQI1Fap3ZmyHMPVsMcma6mvi2oUunvTzOcuWHHkkx1LrvWRzQTlaWqEnqCp/ff5PfX5r0eBEPrSkC8zrHRQ==", "license": "MIT", "dependencies": { - "@remix-run/server-runtime": "2.17.0", + "@remix-run/server-runtime": "2.17.2", "@remix-run/web-fetch": "^4.4.2", "@web3-storage/multipart-parser": "^1.0.0", "cookie-signature": "^1.1.0", @@ -1951,14 +1903,14 @@ } }, "node_modules/@remix-run/react": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/react/-/react-2.17.0.tgz", - "integrity": "sha512-muOLHqcimMCrIk6VOuqIn51P3buYjKpdYc6qpNy6zE5HlKfyaKEY00a5pzdutRmevYTQy7FiEF/LK4M8sxk70Q==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/react/-/react-2.17.2.tgz", + "integrity": "sha512-/s/PYqDjTsQ/2bpsmY3sytdJYXuFHwPX3IRHKcM+UZXFRVGPKF5dgGuvCfb+KW/TmhVKNgpQv0ybCoGpCuF6aA==", "license": "MIT", "peer": true, "dependencies": { "@remix-run/router": "1.23.0", - "@remix-run/server-runtime": "2.17.0", + "@remix-run/server-runtime": "2.17.2", "react-router": "6.30.0", "react-router-dom": "6.30.0", "turbo-stream": "2.4.1" @@ -1987,19 +1939,19 @@ } }, "node_modules/@remix-run/serve": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.17.0.tgz", - "integrity": "sha512-eq0A7A89uqg+rQiGVHoUwb1NUawmPpjAY3RWn4KG4uCp4QwhqJausML63fIxnfdp7zu2OplXhfSXCNiTekQ0rw==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.17.2.tgz", + "integrity": "sha512-awbabibbFnfRMkW6UryRB5BF1G23pZvL8kT5ibWyI9rXnHwcOsKsHGm7ctdTKRDza7PSIH47uqMBCaCWPWNUsg==", "license": "MIT", "peer": true, "dependencies": { - "@remix-run/express": "2.17.0", - "@remix-run/node": "2.17.0", + "@remix-run/express": "2.17.2", + "@remix-run/node": "2.17.2", "chokidar": "^3.5.3", - "compression": "^1.7.4", + "compression": "^1.8.1", "express": "^4.20.0", "get-port": "5.1.1", - "morgan": "^1.10.0", + "morgan": "^1.10.1", "source-map-support": "^0.5.21" }, "bin": { @@ -2010,9 +1962,9 @@ } }, "node_modules/@remix-run/server-runtime": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.0.tgz", - "integrity": "sha512-X0zfGLgvukhuTIL0tdWKnlvHy4xUe7Z17iQ0KMQoITK0SkTZPSud/6cJCsKhPqC8kfdYT1GNFLJKRhHz7Aapmw==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.2.tgz", + "integrity": "sha512-dTrAG1SgOLgz1DFBDsLHN0V34YqLsHEReVHYOI4UV/p+ALbn/BRQMw1MaUuqGXmX21ZTuCzzPegtTLNEOc8ixA==", "license": "MIT", "dependencies": { "@remix-run/router": "1.23.0", @@ -2399,54 +2351,49 @@ "license": "MIT" }, "node_modules/@tailwindcss/node": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", - "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", - "jiti": "^2.5.1", - "lightningcss": "1.30.1", - "magic-string": "^0.30.18", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.17" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", - "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-arm64": "4.1.13", - "@tailwindcss/oxide-darwin-x64": "4.1.13", - "@tailwindcss/oxide-freebsd-x64": "4.1.13", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", - "@tailwindcss/oxide-linux-x64-musl": "4.1.13", - "@tailwindcss/oxide-wasm32-wasi": "4.1.13", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", - "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", "cpu": [ "arm64" ], @@ -2461,9 +2408,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", - "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", "cpu": [ "arm64" ], @@ -2478,9 +2425,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", - "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", "cpu": [ "x64" ], @@ -2495,9 +2442,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", - "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", "cpu": [ "x64" ], @@ -2512,9 +2459,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", - "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", "cpu": [ "arm" ], @@ -2529,9 +2476,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", - "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", "cpu": [ "arm64" ], @@ -2546,9 +2493,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", - "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", "cpu": [ "arm64" ], @@ -2563,9 +2510,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", - "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", "cpu": [ "x64" ], @@ -2580,9 +2527,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", - "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", "cpu": [ "x64" ], @@ -2597,9 +2544,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", - "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2615,21 +2562,21 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.5", - "@emnapi/runtime": "^1.4.5", - "@emnapi/wasi-threads": "^1.0.4", - "@napi-rs/wasm-runtime": "^0.2.12", - "@tybys/wasm-util": "^0.10.0", - "tslib": "^2.8.0" + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", - "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", "cpu": [ "arm64" ], @@ -2644,9 +2591,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", - "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", "cpu": [ "x64" ], @@ -2660,80 +2607,28 @@ "node": ">= 10" } }, - "node_modules/@tailwindcss/oxide/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz", - "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", + "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.13", - "@tailwindcss/oxide": "4.1.13", + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", - "tailwindcss": "4.1.13" + "tailwindcss": "4.1.17" } }, "node_modules/@tokenizer/inflate": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", - "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" + "debug": "^4.4.3", + "token-types": "^6.1.1" }, "engines": { "node": ">=18" @@ -2932,17 +2827,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", - "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", + "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/type-utils": "8.42.0", - "@typescript-eslint/utils": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/type-utils": "8.48.0", + "@typescript-eslint/utils": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2956,7 +2851,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.42.0", + "@typescript-eslint/parser": "^8.48.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -2972,17 +2867,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", - "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", + "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4" }, "engines": { @@ -2998,14 +2893,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", - "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", + "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.42.0", - "@typescript-eslint/types": "^8.42.0", + "@typescript-eslint/tsconfig-utils": "^8.48.0", + "@typescript-eslint/types": "^8.48.0", "debug": "^4.3.4" }, "engines": { @@ -3020,14 +2915,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", - "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", + "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0" + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3038,9 +2933,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", - "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", + "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", "dev": true, "license": "MIT", "engines": { @@ -3055,15 +2950,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", - "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.0.tgz", + "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3080,9 +2975,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", - "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz", + "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", "dev": true, "license": "MIT", "engines": { @@ -3094,21 +2989,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", - "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", + "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.42.0", - "@typescript-eslint/tsconfig-utils": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/project-service": "8.48.0", + "@typescript-eslint/tsconfig-utils": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -3123,17 +3017,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", - "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz", + "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0" + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3148,13 +3042,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", - "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", + "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/types": "8.48.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4395,9 +4289,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "version": "10.4.22", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.22.tgz", + "integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==", "dev": true, "funding": [ { @@ -4415,9 +4309,9 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", + "browserslist": "^4.27.0", + "caniuse-lite": "^1.0.30001754", + "fraction.js": "^5.3.4", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -4506,6 +4400,16 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.31", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", + "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -4620,9 +4524,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", "dev": true, "funding": [ { @@ -4641,10 +4545,11 @@ "license": "MIT", "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -4691,18 +4596,18 @@ "license": "MIT" }, "node_modules/bullmq": { - "version": "5.58.5", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.58.5.tgz", - "integrity": "sha512-0A6Qjxdn8j7aOcxfRZY798vO/aMuwvoZwfE6a9EOXHb1pzpBVAogsc/OfRWeUf+5wMBoYB5nthstnJo/zrQOeQ==", + "version": "5.64.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.64.1.tgz", + "integrity": "sha512-Tg4ORit8bQ1xLwcQrEfcDpG50pS30Onuz1ZA4rPLbL9QEsOaBbvFQMSEvXSgMnvRZRqDggJoSBGz9tck+1PixQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", - "ioredis": "^5.4.1", + "ioredis": "^5.8.2", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", - "uuid": "^9.0.0" + "uuid": "^11.1.0" } }, "node_modules/bytes": { @@ -4895,9 +4800,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001703", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz", - "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==", + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", "dev": true, "funding": [ { @@ -5723,9 +5628,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.109", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz", - "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==", + "version": "1.5.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", + "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", "dev": true, "license": "ISC" }, @@ -6080,26 +5985,25 @@ } }, "node_modules/eslint": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", - "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.34.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -7011,23 +6915,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7042,16 +6929,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fault": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", @@ -7066,12 +6943,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7086,14 +6957,14 @@ } }, "node_modules/file-type": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", - "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.1.tgz", + "integrity": "sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg==", "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.2.7", - "strtok3": "^10.2.2", - "token-types": "^6.0.0", + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" }, "engines": { @@ -7250,16 +7121,16 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -7531,9 +7402,9 @@ } }, "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", "engines": { @@ -7910,12 +7781,12 @@ } }, "node_modules/ioredis": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.0.tgz", - "integrity": "sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.2.tgz", + "integrity": "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==", "license": "MIT", "dependencies": { - "@ioredis/commands": "^1.1.1", + "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", @@ -8535,9 +8406,9 @@ "license": "MIT" }, "node_modules/isbot": { - "version": "5.1.30", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.30.tgz", - "integrity": "sha512-3wVJEonAns1OETX83uWsk5IAne2S5zfDcntD2hbtU23LelSqNXzXs9zKjMPOLMzroCgIjCfjYAEHrd2D6FOkiA==", + "version": "5.1.32", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.32.tgz", + "integrity": "sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ==", "license": "Unlicense", "engines": { "node": ">=18" @@ -8592,9 +8463,9 @@ "license": "MIT" }, "node_modules/jiti": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", - "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -8810,9 +8681,9 @@ } }, "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "dev": true, "license": "MPL-2.0", "peer": true, @@ -8827,22 +8698,44 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", "cpu": [ "arm64" ], @@ -8861,9 +8754,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", "cpu": [ "x64" ], @@ -8882,9 +8775,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", "cpu": [ "x64" ], @@ -8903,9 +8796,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", "cpu": [ "arm" ], @@ -8924,9 +8817,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", "cpu": [ "arm64" ], @@ -8945,9 +8838,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", "cpu": [ "arm64" ], @@ -8966,9 +8859,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", "cpu": [ "x64" ], @@ -8987,9 +8880,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", "cpu": [ "x64" ], @@ -9008,9 +8901,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", "cpu": [ "arm64" ], @@ -9029,9 +8922,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", "cpu": [ "x64" ], @@ -9284,9 +9177,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9563,16 +9456,6 @@ "dev": true, "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -10223,20 +10106,6 @@ ], "license": "MIT" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -10607,9 +10476,9 @@ } }, "node_modules/music-metadata": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.9.0.tgz", - "integrity": "sha512-J7VqD8FY6KRcm75Fzj86FPsckiD/EdvO5OS3P+JiMf/2krP3TcAseZYfkic6eFeJ0iBhhzcdxgfu8hLW95aXXw==", + "version": "11.10.3", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.10.3.tgz", + "integrity": "sha512-j0g/x4cNNZW6I5gdcPAY+GFkJY9WHTpkFDMBJKQLxJQyvSfQbXm57fTE3haGFFuOzCgtsTd4Plwc49Sn9RacDQ==", "funding": [ { "type": "github", @@ -10626,7 +10495,7 @@ "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.3", - "file-type": "^21.0.0", + "file-type": "^21.1.1", "media-typer": "^1.1.0", "strtok3": "^10.3.4", "token-types": "^6.1.1", @@ -10731,9 +10600,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, @@ -12093,27 +11962,6 @@ ], "license": "MIT" }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -12661,17 +12509,6 @@ "node": ">= 4" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rollup": { "version": "4.50.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz", @@ -12713,30 +12550,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -12840,9 +12653,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -13582,16 +13395,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", - "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -13782,14 +13595,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -13799,11 +13612,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -13814,9 +13630,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "peer": true, @@ -14083,9 +13899,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "peer": true, @@ -14098,16 +13914,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz", - "integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.0.tgz", + "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.42.0", - "@typescript-eslint/parser": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/utils": "8.42.0" + "@typescript-eslint/eslint-plugin": "8.48.0", + "@typescript-eslint/parser": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14402,9 +14218,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, "funding": [ { @@ -14472,16 +14288,16 @@ } }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/uvu": { diff --git a/package.json b/package.json index 23cc6a9..ccf561a 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", "@types/semver": "^7.7.1", - "@typescript-eslint/eslint-plugin": "^8.47.0", - "@typescript-eslint/parser": "^8.47.0", + "@typescript-eslint/eslint-plugin": "^8.37.0", + "@typescript-eslint/parser": "^8.37.0", "autoprefixer": "^10.4.22", "env-cmd": "^11.0.0", "eslint": "^9.39.1", From cb0d5296c7fedefb0d194b47106fba0af2e202a8 Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Tue, 25 Nov 2025 13:10:15 +0000 Subject: [PATCH 6/9] feat: log on dashboard and live page data for logs --- app/lib/hooks/use-live-data.ts | 32 ++++++++++++++++++ app/locales/en.ts | 1 + app/routes/_index.tsx | 43 +++++++++++++++++++++---- app/routes/buttons.$button._index.tsx | 2 ++ app/routes/log.tsx | 4 ++- app/routes/sounders.$sounder._index.tsx | 3 ++ 6 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 app/lib/hooks/use-live-data.ts diff --git a/app/lib/hooks/use-live-data.ts b/app/lib/hooks/use-live-data.ts new file mode 100644 index 0000000..680967d --- /dev/null +++ b/app/lib/hooks/use-live-data.ts @@ -0,0 +1,32 @@ +import {useRevalidator} from '@remix-run/react' +import {useEffect, useRef} from 'react' + +function useInterval(callback: () => void, delay: number) { + const savedCallback = useRef<() => void>() + + // Remember the latest callback. + useEffect(() => { + savedCallback.current = callback + }, [callback]) + + // Set up the interval. + useEffect(() => { + function tick() { + savedCallback.current!() + } + if (delay !== null) { + let id = setInterval(tick, delay) + return () => clearInterval(id) + } + }, [delay]) +} + +export const useLivePageData = (interval: number = 5000) => { + const revalidator = useRevalidator() + + useInterval(() => { + if (revalidator.state === 'idle') { + revalidator.revalidate() + } + }, interval) +} diff --git a/app/locales/en.ts b/app/locales/en.ts index 7af1c24..897d9b6 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -31,6 +31,7 @@ export const en = { 'Are you sure you want to disable lockdown?', 'dashboard.lockdown.button.enable': 'Enable', 'dashboard.lockdown.button.disable': 'Disable', + 'dashboard.log': 'Log', 'about.title': 'About', 'about.table.component': 'Component', 'about.table.version': 'Version', diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 550e837..9fcddde 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -4,7 +4,7 @@ import { redirect } from '@remix-run/node' import {Link, useLoaderData} from '@remix-run/react' -import {formatDistance} from 'date-fns' +import {formatDistance, format} from 'date-fns' import {enUS, pl} from 'date-fns/locale' import {getPrisma} from '~/lib/prisma.server' @@ -15,6 +15,8 @@ import {Page} from '~/lib/ui' import {useTranslation} from '~/lib/i18n' import {translate} from '~/lib/i18n.shared' import {getRootI18n} from '~/lib/i18n.meta' +import {useLivePageData} from '~/lib/hooks/use-live-data' +import {translateLogMessage} from './log' export const loader = async ({request}: LoaderFunctionArgs) => { const result = await checkSession(request) @@ -27,10 +29,11 @@ export const loader = async ({request}: LoaderFunctionArgs) => { const sounders = await prisma.sounder.findMany({orderBy: {name: 'asc'}}) const buttons = await prisma.actionButton.findMany({orderBy: {name: 'asc'}}) + const logs = await prisma.log.findMany({orderBy: {time: 'desc'}, take: 10}) const lockdownMode = await getSetting('lockdownMode') - return {sounders, lockdownMode, buttons} + return {sounders, lockdownMode, buttons, logs} } export const meta: MetaFunction = ({matches}) => { @@ -39,12 +42,13 @@ export const meta: MetaFunction = ({matches}) => { } export default function Index() { - const {sounders, lockdownMode, buttons} = useLoaderData() + const {sounders, lockdownMode, buttons, logs} = useLoaderData() const {t, locale} = useTranslation() const dateLocale = locale === 'pl' ? pl : enUS + useLivePageData() return ( - +

{t('dashboard.devices')}

@@ -83,9 +87,9 @@ export default function Index() {
-

+

{t('dashboard.lockdown.message', { status: t( lockdownMode === '0' @@ -111,7 +115,9 @@ export default function Index() { } }} > -

+
+

{t('dashboard.log')}

+ + + + + + + + + {logs.map(({id, message, time}) => { + return ( + + + + + ) + })} + +
{t('log.columns.time')}{t('log.columns.message')}
+ {format(time, 'dd/MM/yy HH:mm')} + {translateLogMessage(message, t)}
+
) diff --git a/app/routes/buttons.$button._index.tsx b/app/routes/buttons.$button._index.tsx index de3c7c1..1641e74 100644 --- a/app/routes/buttons.$button._index.tsx +++ b/app/routes/buttons.$button._index.tsx @@ -14,6 +14,7 @@ import {getSetting} from '~/lib/settings.server' import {useTranslation} from '~/lib/i18n' import {translate} from '~/lib/i18n.shared' import {getRootI18n} from '~/lib/i18n.meta' +import {useLivePageData} from '~/lib/hooks/use-live-data' export const meta: MetaFunction = ({data, matches}) => { const {messages} = getRootI18n(matches) @@ -51,6 +52,7 @@ const Sounder = () => { const {button, enrollUrl} = useLoaderData() const navigate = useNavigate() const {t} = useTranslation() + useLivePageData() return ( diff --git a/app/routes/log.tsx b/app/routes/log.tsx index 879333d..252a166 100644 --- a/app/routes/log.tsx +++ b/app/routes/log.tsx @@ -14,6 +14,7 @@ import {useTranslation} from '~/lib/i18n' import {translate} from '~/lib/i18n.shared' import {getRootI18n} from '~/lib/i18n.meta' import {MessageKey} from '~/locales' +import {useLivePageData} from '~/lib/hooks/use-live-data' export const meta: MetaFunction = ({matches}) => { const {messages} = getRootI18n(matches) @@ -37,6 +38,7 @@ export const loader = async ({request}: LoaderFunctionArgs) => { const Log = () => { const {logs} = useLoaderData() const {t} = useTranslation() + useLivePageData() return ( @@ -66,7 +68,7 @@ const Log = () => { export default Log -const translateLogMessage = ( +export const translateLogMessage = ( message: string, t: ReturnType['t'] ) => { diff --git a/app/routes/sounders.$sounder._index.tsx b/app/routes/sounders.$sounder._index.tsx index 9e76bad..527213f 100644 --- a/app/routes/sounders.$sounder._index.tsx +++ b/app/routes/sounders.$sounder._index.tsx @@ -14,6 +14,7 @@ import {getSetting} from '~/lib/settings.server' import {useTranslation} from '~/lib/i18n' import {translate} from '~/lib/i18n.shared' import {getRootI18n} from '~/lib/i18n.meta' +import {useLivePageData} from '~/lib/hooks/use-live-data' export const meta: MetaFunction = ({data, matches}) => { const {messages} = getRootI18n(matches) @@ -61,6 +62,8 @@ const Sounder = () => { ? t('sounders.detail.ringerPin.none') : String(sounder.ringerPin) + useLivePageData() + return (
From 0c7e14a4b262aae9c14a46f905e5fd86e2995ef1 Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Wed, 26 Nov 2025 13:02:40 +0000 Subject: [PATCH 7/9] feat: add full log button to sounders and buttons --- app/locales/en.ts | 1 + app/routes/buttons.$button._index.tsx | 7 ++++++ app/routes/buttons.$button.log.tsx | 29 +++++++++++++++++++++++++ app/routes/sounders.$sounder._index.tsx | 7 ++++++ app/routes/sounders.$sounder.log.tsx | 29 +++++++++++++++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 app/routes/buttons.$button.log.tsx create mode 100644 app/routes/sounders.$sounder.log.tsx diff --git a/app/locales/en.ts b/app/locales/en.ts index 897d9b6..cbca71d 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -382,6 +382,7 @@ export const en = { 'buttons.detail.keyLabel': 'Key', 'buttons.detail.logTitle': 'Log', 'buttons.detail.editButton': 'Edit button', + 'buttons.detail.logButton': 'View full log', 'buttons.detail.ledPinLabel': 'LED Pin', 'buttons.detail.buttonPinLabel': 'Button Pin', 'buttons.detail.holdLabel': 'Hold Duration', diff --git a/app/routes/buttons.$button._index.tsx b/app/routes/buttons.$button._index.tsx index 1641e74..7978954 100644 --- a/app/routes/buttons.$button._index.tsx +++ b/app/routes/buttons.$button._index.tsx @@ -129,6 +129,13 @@ const Sounder = () => {
{ + window.location.href = `/buttons/${button.id}/log` + } + }, { label: t('buttons.detail.editButton'), color: 'bg-blue-300', diff --git a/app/routes/buttons.$button.log.tsx b/app/routes/buttons.$button.log.tsx new file mode 100644 index 0000000..fa37a89 --- /dev/null +++ b/app/routes/buttons.$button.log.tsx @@ -0,0 +1,29 @@ +import {type LoaderFunctionArgs, redirect} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {checkSession} from '~/lib/session' + +export const loader = async ({request, params}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const button = await prisma.actionButton.findFirstOrThrow({ + where: {id: params.button}, + include: { + logs: {orderBy: {time: 'desc'}} + } + }) + + return new Response( + button.logs + .map(({time, message}) => { + return `${time}: ${message}` + }) + .join(`\r\n`) + ) +} diff --git a/app/routes/sounders.$sounder._index.tsx b/app/routes/sounders.$sounder._index.tsx index 527213f..bae7633 100644 --- a/app/routes/sounders.$sounder._index.tsx +++ b/app/routes/sounders.$sounder._index.tsx @@ -157,6 +157,13 @@ const Sounder = () => { { + window.location.href = `/sounders/${sounder.id}/log` + } + }, { label: t('sounders.detail.editButton'), color: 'bg-blue-300', diff --git a/app/routes/sounders.$sounder.log.tsx b/app/routes/sounders.$sounder.log.tsx new file mode 100644 index 0000000..82fe3a0 --- /dev/null +++ b/app/routes/sounders.$sounder.log.tsx @@ -0,0 +1,29 @@ +import {type LoaderFunctionArgs, redirect} from '@remix-run/node' + +import {getPrisma} from '~/lib/prisma.server' +import {checkSession} from '~/lib/session' + +export const loader = async ({request, params}: LoaderFunctionArgs) => { + const result = await checkSession(request) + + if (!result) { + return redirect('/login') + } + + const prisma = getPrisma() + + const sounder = await prisma.sounder.findFirstOrThrow({ + where: {id: params.sounder}, + include: { + logs: {orderBy: {time: 'desc'}} + } + }) + + return new Response( + sounder.logs + .map(({time, message}) => { + return `${time}: ${message}` + }) + .join(`\r\n`) + ) +} From 0cca1852df24863ceb07a1782bbec852a3ad93e8 Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Fri, 12 Dec 2025 10:51:31 +0000 Subject: [PATCH 8/9] chore: rollback remix --- package-lock.json | 125 +++++++++++++++++++++++++++++++++++++--------- package.json | 6 +-- 2 files changed, 105 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf244a5..effd8d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "@arcath/utils": "^3.15.0", "@heroicons/react": "^2.2.0", "@prisma/client": "^6.15.0", - "@remix-run/node": "^2.17.2", - "@remix-run/react": "^2.17.2", - "@remix-run/serve": "^2.17.2", + "@remix-run/node": "^2.17.0", + "@remix-run/react": "^2.17.0", + "@remix-run/serve": "^2.17.0", "adm-zip": "^0.5.16", "bullmq": "^5.64.1", "date-fns": "^4.1.0", @@ -1839,6 +1839,33 @@ } } }, + "node_modules/@remix-run/dev/node_modules/@remix-run/node": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.17.2.tgz", + "integrity": "sha512-NHBIQI1Fap3ZmyHMPVsMcma6mvi2oUunvTzOcuWHHkkx1LrvWRzQTlaWqEnqCp/ff5PfX5r0eBEPrSkC8zrHRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@remix-run/server-runtime": "2.17.2", + "@remix-run/web-fetch": "^4.4.2", + "@web3-storage/multipart-parser": "^1.0.0", + "cookie-signature": "^1.1.0", + "source-map-support": "^0.5.21", + "stream-slice": "^0.1.2", + "undici": "^6.21.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@remix-run/dev/node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -1856,12 +1883,12 @@ } }, "node_modules/@remix-run/express": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.17.2.tgz", - "integrity": "sha512-N3Rp4xuXWlUUboQxc8ATqvYiFX2DoX0cavWIsQ0pMKPyG1WOSO+MfuOFgHa7kf/ksRteSfDtzgJSgTH6Fv96AA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/express/-/express-2.17.0.tgz", + "integrity": "sha512-VUNpdrX3WSLPOkRBbsTQao5Vu/xdKcB8AY+44pAyC7iW5iIKHDb6EYlDvpbMLcMNh9ErYGhpPtshaBiBTMvjiw==", "license": "MIT", "dependencies": { - "@remix-run/node": "2.17.2" + "@remix-run/node": "2.17.0" }, "engines": { "node": ">=18.0.0" @@ -1877,12 +1904,12 @@ } }, "node_modules/@remix-run/node": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.17.2.tgz", - "integrity": "sha512-NHBIQI1Fap3ZmyHMPVsMcma6mvi2oUunvTzOcuWHHkkx1LrvWRzQTlaWqEnqCp/ff5PfX5r0eBEPrSkC8zrHRQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.17.0.tgz", + "integrity": "sha512-ISy3N4peKB+Fo8ddh+mU6ki3HzQqLXwJxUrAtqxYxrBDM4Pwc7EvISrcQ4QasB6ORBknJeEZSBu69WDRhGzrjA==", "license": "MIT", "dependencies": { - "@remix-run/server-runtime": "2.17.2", + "@remix-run/server-runtime": "2.17.0", "@remix-run/web-fetch": "^4.4.2", "@web3-storage/multipart-parser": "^1.0.0", "cookie-signature": "^1.1.0", @@ -1902,15 +1929,40 @@ } } }, + "node_modules/@remix-run/node/node_modules/@remix-run/server-runtime": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.0.tgz", + "integrity": "sha512-X0zfGLgvukhuTIL0tdWKnlvHy4xUe7Z17iQ0KMQoITK0SkTZPSud/6cJCsKhPqC8kfdYT1GNFLJKRhHz7Aapmw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "@types/cookie": "^0.6.0", + "@web3-storage/multipart-parser": "^1.0.0", + "cookie": "^0.7.2", + "set-cookie-parser": "^2.4.8", + "source-map": "^0.7.3", + "turbo-stream": "2.4.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@remix-run/react": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/@remix-run/react/-/react-2.17.2.tgz", - "integrity": "sha512-/s/PYqDjTsQ/2bpsmY3sytdJYXuFHwPX3IRHKcM+UZXFRVGPKF5dgGuvCfb+KW/TmhVKNgpQv0ybCoGpCuF6aA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/react/-/react-2.17.0.tgz", + "integrity": "sha512-muOLHqcimMCrIk6VOuqIn51P3buYjKpdYc6qpNy6zE5HlKfyaKEY00a5pzdutRmevYTQy7FiEF/LK4M8sxk70Q==", "license": "MIT", - "peer": true, "dependencies": { "@remix-run/router": "1.23.0", - "@remix-run/server-runtime": "2.17.2", + "@remix-run/server-runtime": "2.17.0", "react-router": "6.30.0", "react-router-dom": "6.30.0", "turbo-stream": "2.4.1" @@ -1929,6 +1981,32 @@ } } }, + "node_modules/@remix-run/react/node_modules/@remix-run/server-runtime": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.0.tgz", + "integrity": "sha512-X0zfGLgvukhuTIL0tdWKnlvHy4xUe7Z17iQ0KMQoITK0SkTZPSud/6cJCsKhPqC8kfdYT1GNFLJKRhHz7Aapmw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "@types/cookie": "^0.6.0", + "@web3-storage/multipart-parser": "^1.0.0", + "cookie": "^0.7.2", + "set-cookie-parser": "^2.4.8", + "source-map": "^0.7.3", + "turbo-stream": "2.4.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "typescript": "^5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", @@ -1939,19 +2017,19 @@ } }, "node_modules/@remix-run/serve": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.17.2.tgz", - "integrity": "sha512-awbabibbFnfRMkW6UryRB5BF1G23pZvL8kT5ibWyI9rXnHwcOsKsHGm7ctdTKRDza7PSIH47uqMBCaCWPWNUsg==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/serve/-/serve-2.17.0.tgz", + "integrity": "sha512-eq0A7A89uqg+rQiGVHoUwb1NUawmPpjAY3RWn4KG4uCp4QwhqJausML63fIxnfdp7zu2OplXhfSXCNiTekQ0rw==", "license": "MIT", "peer": true, "dependencies": { - "@remix-run/express": "2.17.2", - "@remix-run/node": "2.17.2", + "@remix-run/express": "2.17.0", + "@remix-run/node": "2.17.0", "chokidar": "^3.5.3", - "compression": "^1.8.1", + "compression": "^1.7.4", "express": "^4.20.0", "get-port": "5.1.1", - "morgan": "^1.10.1", + "morgan": "^1.10.0", "source-map-support": "^0.5.21" }, "bin": { @@ -1965,6 +2043,7 @@ "version": "2.17.2", "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.17.2.tgz", "integrity": "sha512-dTrAG1SgOLgz1DFBDsLHN0V34YqLsHEReVHYOI4UV/p+ALbn/BRQMw1MaUuqGXmX21ZTuCzzPegtTLNEOc8ixA==", + "dev": true, "license": "MIT", "dependencies": { "@remix-run/router": "1.23.0", diff --git a/package.json b/package.json index ccf561a..91ffa10 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "@arcath/utils": "^3.15.0", "@heroicons/react": "^2.2.0", "@prisma/client": "^6.15.0", - "@remix-run/node": "^2.17.2", - "@remix-run/react": "^2.17.2", - "@remix-run/serve": "^2.17.2", + "@remix-run/node": "^2.17.0", + "@remix-run/react": "^2.17.0", + "@remix-run/serve": "^2.17.0", "adm-zip": "^0.5.16", "bullmq": "^5.64.1", "date-fns": "^4.1.0", From a7b7613f6ae98203817bb4e1bdab04f1241b6f5a Mon Sep 17 00:00:00 2001 From: AML - A Laycock Date: Fri, 12 Dec 2025 10:51:43 +0000 Subject: [PATCH 9/9] feat: allow WAV files as well --- app/routes/sounds.add.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/sounds.add.tsx b/app/routes/sounds.add.tsx index fd6619a..8a6743e 100644 --- a/app/routes/sounds.add.tsx +++ b/app/routes/sounds.add.tsx @@ -142,7 +142,7 @@ const AddSound = () => {