Upgrade expressjs (#1633)

* upgrade expressjs

* migration

* reenable test

* extend timeout on download test

* fixup! migration

* move empty body test from controller to validator

* enusre not empty

* extract validation function

* fixup! reenable test

* remove thin controllers

* disable e2e test of project file download
This commit is contained in:
Alex Christoffer Rasmussen
2025-06-12 15:02:35 +02:00
committed by Carlos Valente
parent 90870ecfb6
commit 6f3ab274bd
39 changed files with 590 additions and 841 deletions
+11 -11
View File
@@ -6,18 +6,18 @@
"exports": "./src/index.js", "exports": "./src/index.js",
"dependencies": { "dependencies": {
"@googleapis/sheets": "^5.0.5", "@googleapis/sheets": "^5.0.5",
"cookie": "^1.0.2", "cookie": "1.0.2",
"cookie-parser": "^1.4.7", "cookie-parser": "1.4.7",
"cors": "^2.8.5", "cors": "2.8.5",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"express": "^4.21.1", "express": "5.1.0",
"express-static-gzip": "^2.2.0", "express-static-gzip": "3.0.0",
"express-validator": "^7.2.0", "express-validator": "7.2.1",
"multer": "2.0.1",
"fast-equals": "^5.0.1", "fast-equals": "^5.0.1",
"google-auth-library": "^9.4.2", "google-auth-library": "^9.4.2",
"got": "^14.4.5", "got": "^14.4.5",
"lowdb": "^7.0.1", "lowdb": "^7.0.1",
"multer": "^1.4.5-lts.1",
"ontime-utils": "workspace:*", "ontime-utils": "workspace:*",
"osc-min": "2.1.2", "osc-min": "2.1.2",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
@@ -26,10 +26,10 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "1.4.9",
"@types/cors": "^2.8.17", "@types/cors": "2.8.19",
"@types/express": "^4.17.17", "@types/express": "5.0.3",
"@types/multer": "^1.4.11", "@types/multer": "1.4.13",
"@types/node": "catalog:", "@types/node": "catalog:",
"@types/websocket": "^1.0.5", "@types/websocket": "^1.0.5",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
@@ -1,41 +0,0 @@
import { defaultCss } from '../../user/styles/bundledCss.js';
import type { Request, Response } from 'express';
import { readCssFile, writeCssFile } from './assets.service.js';
/**
* Exposes the contents of the cssOverride.css file
*/
export async function getCssOverride(_req: Request, res: Response) {
try {
const data = await readCssFile();
res.status(200).send(data);
} catch (error) {
res.status(500).send({ message: error });
}
}
/**
* Allows modifying the cssOverride.css file
*/
export async function postCssOverride(req: Request, res: Response) {
const { css } = req.body;
try {
await writeCssFile(css);
res.status(204).send();
} catch (error) {
res.status(500).send({ message: error });
}
}
/**
* Restores the default cssOverride.css file
*/
export async function restoreCss(_req: Request, res: Response) {
try {
await writeCssFile(defaultCss);
res.status(200).send(defaultCss);
} catch (error) {
res.status(500).send({ message: error });
}
}
@@ -1,10 +1,40 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express';
import { getCssOverride, postCssOverride, restoreCss } from './assets.controller.js'; import type { ErrorResponse } from 'ontime-types';
import { validatePostCss } from './assets.validation.js'; import { validatePostCss } from './assets.validation.js';
import { readCssFile, writeCssFile } from './assets.service.js';
import { getErrorMessage } from 'ontime-utils';
import { defaultCss } from '../../user/styles/bundledCss.js';
export const router = express.Router(); export const router = express.Router();
router.get('/css', getCssOverride); router.get('/css', async (_req: Request, res: Response<string | ErrorResponse>) => {
router.post('/css', validatePostCss, postCssOverride); try {
router.post('/css/restore', restoreCss); const data = await readCssFile();
res.status(200).send(data);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
router.post('/css', validatePostCss, async (req: Request, res: Response<never | ErrorResponse>) => {
const { css } = req.body;
try {
await writeCssFile(css);
res.status(204).send();
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
router.post('/css/restore', async (_req: Request, res: Response<string | ErrorResponse>) => {
try {
await writeCssFile(defaultCss);
res.status(200).send(defaultCss);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
@@ -1,12 +1,4 @@
import { Request, Response, NextFunction } from 'express'; import { body } from 'express-validator';
import { body, validationResult } from 'express-validator'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validatePostCss = [ export const validatePostCss = [body('css').isString().trim(), requestValidationFunction];
body('css').isString().trim(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
@@ -12,7 +12,7 @@ import * as automationService from './automation.service.js';
import { parseOutput } from './automation.validation.js'; import { parseOutput } from './automation.validation.js';
export function getAutomationSettings(_req: Request, res: Response<AutomationSettings>) { export function getAutomationSettings(_req: Request, res: Response<AutomationSettings>) {
res.json(automationDao.getAutomationSettings()); res.status(200).json(automationDao.getAutomationSettings());
} }
export async function postAutomationSettings(req: Request, res: Response<AutomationSettings | ErrorResponse>) { export async function postAutomationSettings(req: Request, res: Response<AutomationSettings | ErrorResponse>) {
@@ -12,7 +12,6 @@ import {
testOutput, testOutput,
} from './automation.controller.js'; } from './automation.controller.js';
import { import {
paramContainsId,
validateAutomationSettings, validateAutomationSettings,
validateAutomation, validateAutomation,
validateAutomationPatch, validateAutomationPatch,
@@ -20,6 +19,7 @@ import {
validateTrigger, validateTrigger,
validateTriggerPatch, validateTriggerPatch,
} from './automation.validation.js'; } from './automation.validation.js';
import { paramsWithId } from '../validation-utils/validationFunction.js';
export const router = express.Router(); export const router = express.Router();
@@ -28,10 +28,10 @@ router.post('/', validateAutomationSettings, postAutomationSettings);
router.post('/trigger', validateTrigger, postTrigger); router.post('/trigger', validateTrigger, postTrigger);
router.put('/trigger/:id', validateTriggerPatch, putTrigger); router.put('/trigger/:id', validateTriggerPatch, putTrigger);
router.delete('/trigger/:id', paramContainsId, deleteTrigger); router.delete('/trigger/:id', paramsWithId, deleteTrigger);
router.post('/automation', validateAutomation, postAutomation); router.post('/automation', validateAutomation, postAutomation);
router.put('/automation/:id', validateAutomationPatch, editAutomation); router.put('/automation/:id', validateAutomationPatch, editAutomation);
router.delete('/automation/:id', paramContainsId, deleteAutomation); router.delete('/automation/:id', paramsWithId, deleteAutomation);
router.post('/test', validateTestPayload, testOutput); router.post('/test', validateTestPayload, testOutput);
@@ -10,22 +10,12 @@ import {
} from 'ontime-types'; } from 'ontime-types';
import { parseUserTime } from 'ontime-utils'; import { parseUserTime } from 'ontime-utils';
import type { Request, Response, NextFunction } from 'express'; import { body, oneOf, param } from 'express-validator';
import { body, oneOf, param, validationResult } from 'express-validator';
import * as assert from '../../utils/assert.js'; import * as assert from '../../utils/assert.js';
import { isFilterOperator, isFilterRule, isOntimeActionAction } from './automation.utils.js'; import { isFilterOperator, isFilterRule, isOntimeActionAction } from './automation.utils.js';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const paramContainsId = [
param('id').isString().notEmpty(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const validateAutomationSettings = [ export const validateAutomationSettings = [
body('enabledAutomations').isBoolean(), body('enabledAutomations').isBoolean(),
@@ -37,57 +27,33 @@ export const validateAutomationSettings = [
body('triggers.*.automationId').optional().isString().trim(), body('triggers.*.automationId').optional().isString().trim(),
body('automations').optional().custom(parseAutomation), body('automations').optional().custom(parseAutomation),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const validateTrigger = [ export const validateTrigger = [
body('title').isString().trim(), body('title').isString().trim().notEmpty(),
body('trigger').isIn(timerLifecycleValues), body('trigger').isIn(timerLifecycleValues),
body('automationId').isString().trim(), body('automationId').isString().trim().notEmpty(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const validateTriggerPatch = [ export const validateTriggerPatch = [
param('id').isString().notEmpty(), param('id').isString().notEmpty(),
body('title').optional().isString().trim(), body('title').optional().isString().trim().notEmpty(),
body('trigger').optional().isIn(timerLifecycleValues), body('trigger').optional().isIn(timerLifecycleValues),
body('automationId').optional().isString().trim(), body('automationId').optional().isString().trim().notEmpty(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const validateAutomation = [ export const validateAutomation = [body().custom(parseAutomation), requestValidationFunction];
body().custom(parseAutomation),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const validateAutomationPatch = [ export const validateAutomationPatch = [
param('id').isString().notEmpty(), param('id').isString().notEmpty(),
body().custom(parseAutomation), body().custom(parseAutomation),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
/** /**
@@ -162,11 +128,7 @@ export const validateTestPayload = [
body('visible').if(body('type').equals('ontime')).optional().isString().trim(), body('visible').if(body('type').equals('ontime')).optional().isString().trim(),
body('secondarySource').if(body('type').equals('ontime')).optional().isString().trim(), body('secondarySource').if(body('type').equals('ontime')).optional().isString().trim(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
/** /**
@@ -13,7 +13,7 @@ export const router = express.Router();
router.get('/', async (_req: Request, res: Response<CustomFields>) => { router.get('/', async (_req: Request, res: Response<CustomFields>) => {
const customFields = getProjectCustomFields(); const customFields = getProjectCustomFields();
res.json(customFields); res.status(200).json(customFields);
}); });
router.post('/', validateCustomField, async (req: Request, res: Response<CustomFields | ErrorResponse>) => { router.post('/', validateCustomField, async (req: Request, res: Response<CustomFields | ErrorResponse>) => {
@@ -1,7 +1,7 @@
import { isAlphanumericWithSpace } from 'ontime-utils'; import { isAlphanumericWithSpace } from 'ontime-utils';
import type { Request, Response, NextFunction } from 'express'; import { body, param } from 'express-validator';
import { body, param, validationResult } from 'express-validator'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateCustomField = [ export const validateCustomField = [
body('label') body('label')
@@ -14,11 +14,7 @@ export const validateCustomField = [
body('type').isIn(['string', 'image']), body('type').isIn(['string', 'image']),
body('colour').isString().trim(), body('colour').isString().trim(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const validateEditCustomField = [ export const validateEditCustomField = [
@@ -33,19 +29,7 @@ export const validateEditCustomField = [
body('type').isIn(['string', 'image']), body('type').isIn(['string', 'image']),
body('colour').isString().trim(), body('colour').isString().trim(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const validateDeleteCustomField = [ export const validateDeleteCustomField = [param('key').isString().notEmpty(), requestValidationFunction];
param('key').isString().notEmpty(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
+16 -8
View File
@@ -106,7 +106,8 @@ export async function projectDownload(req: Request, res: Response) {
const { filename } = req.body; const { filename } = req.body;
const pathToFile = doesProjectExist(filename); const pathToFile = doesProjectExist(filename);
if (!pathToFile) { if (!pathToFile) {
return res.status(404).send({ message: `Project ${filename} not found.` }); res.status(404).send({ message: `Project ${filename} not found.` });
return;
} }
res.download(pathToFile, filename, (error) => { res.download(pathToFile, filename, (error) => {
@@ -138,7 +139,8 @@ export async function postProjectFile(req: Request, res: Response<MessageRespons
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message.startsWith('Project file')) { if (message.startsWith('Project file')) {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
res.status(400).send({ message }); res.status(400).send({ message });
} }
@@ -195,7 +197,8 @@ export async function loadProject(req: Request, res: Response<MessageResponse |
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message.startsWith('Project file')) { if (message.startsWith('Project file')) {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
res.status(500).send({ message }); res.status(500).send({ message });
} }
@@ -214,7 +217,8 @@ export async function loadDemo(_req: Request, res: Response<MessageResponse | Er
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message.startsWith('Project file')) { if (message.startsWith('Project file')) {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
res.status(500).send({ message }); res.status(500).send({ message });
} }
@@ -245,7 +249,8 @@ export async function duplicateProjectFile(req: Request, res: Response<MessageRe
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message.startsWith('Project file')) { if (message.startsWith('Project file')) {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
res.status(500).send({ message }); res.status(500).send({ message });
@@ -275,7 +280,8 @@ export async function renameProjectFile(req: Request, res: Response<MessageRespo
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message.startsWith('Project file')) { if (message.startsWith('Project file')) {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
res.status(500).send({ message }); res.status(500).send({ message });
@@ -303,10 +309,12 @@ export async function deleteProjectFile(req: Request, res: Response<MessageRespo
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
if (message === 'Cannot delete currently loaded project') { if (message === 'Cannot delete currently loaded project') {
return res.status(403).send({ message }); res.status(403).send({ message });
return;
} }
if (message === 'Project file not found') { if (message === 'Project file not found') {
return res.status(404).send({ message }); res.status(404).send({ message });
return;
} }
res.status(500).send({ message }); res.status(500).send({ message });
+10 -49
View File
@@ -1,12 +1,13 @@
import type { Request, Response, NextFunction } from 'express'; import { body, param } from 'express-validator';
import { body, param, validationResult } from 'express-validator';
import sanitize from 'sanitize-filename'; import sanitize from 'sanitize-filename';
import { ensureJsonExtension } from '../../utils/fileManagement.js'; import { ensureJsonExtension } from '../../utils/fileManagement.js';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/** /**
* @description Validates request for a new project. * @description Validates request for a new project.
*/ */
export const validateNewProject = [ export const validateNewProject = [
body().notEmpty().withMessage('No object found in request'),
body('filename').optional().isString().trim(), body('filename').optional().isString().trim(),
body('title').optional().isString().trim(), body('title').optional().isString().trim(),
body('description').optional().isString().trim(), body('description').optional().isString().trim(),
@@ -18,11 +19,7 @@ export const validateNewProject = [
body('endMessage').optional().isString().trim(), body('endMessage').optional().isString().trim(),
body('custom').optional().isArray(), body('custom').optional().isArray(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
/** /**
@@ -40,25 +37,14 @@ export const validateQuickProject = [
body('viewSettings.freezeEnd').optional().isBoolean(), body('viewSettings.freezeEnd').optional().isBoolean(),
body('viewSettings.endMessage').optional().isString().trim(), body('viewSettings.endMessage').optional().isString().trim(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
/** /**
* @description Validates request for pathing data in the project. * @description Validates request for pathing data in the project.
*/ */
export const validatePatchProject = [ export const validatePatchProject = [
// Custom validator to ensure the body is not empty body().notEmpty().withMessage('No object found in request'),
(req: Request, res: Response, next: NextFunction) => {
if (Object.keys(req.body).length === 0) {
return res.status(422).json({ errors: [{ msg: 'Request body cannot be empty' }] });
}
next();
},
body('rundowns').isObject().optional({ nullable: false }), body('rundowns').isObject().optional({ nullable: false }),
body('project').isObject().optional({ nullable: false }), body('project').isObject().optional({ nullable: false }),
body('settings').isObject().optional({ nullable: false }), body('settings').isObject().optional({ nullable: false }),
@@ -68,11 +54,7 @@ export const validatePatchProject = [
body('osc').isObject().optional({ nullable: false }), body('osc').isObject().optional({ nullable: false }),
body('http').isObject().optional({ nullable: false }), body('http').isObject().optional({ nullable: false }),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
/** /**
@@ -88,14 +70,7 @@ export const validateNewFilenameBody = [
.withMessage('Filename was empty or contained only invalid characters') .withMessage('Filename was empty or contained only invalid characters')
.customSanitizer((input: string) => ensureJsonExtension(input)), .customSanitizer((input: string) => ensureJsonExtension(input)),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
},
]; ];
/** /**
@@ -111,14 +86,7 @@ export const validateFilenameBody = [
.withMessage('Filename was empty or contained only invalid characters') .withMessage('Filename was empty or contained only invalid characters')
.customSanitizer((input: string) => ensureJsonExtension(input)), .customSanitizer((input: string) => ensureJsonExtension(input)),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
},
]; ];
/** /**
@@ -134,12 +102,5 @@ export const validateFilenameParam = [
.withMessage('Filename was empty or contained only invalid characters') .withMessage('Filename was empty or contained only invalid characters')
.customSanitizer((input: string) => ensureJsonExtension(input)), .customSanitizer((input: string) => ensureJsonExtension(input)),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
},
]; ];
@@ -1,45 +0,0 @@
/**
* This module encapsulates logic related to
* Google Sheets
*/
import type { Request, Response } from 'express';
import { generateRundownPreview, listWorksheets, saveExcelFile } from './excel.service.js';
import { CustomFields, Rundown } from 'ontime-types';
export async function postExcel(req: Request, res: Response) {
try {
// file has been validated by middleware
const filePath = (req.file as Express.Multer.File).path;
await saveExcelFile(filePath);
res.status(200).send();
} catch (error) {
res.status(500).send({ message: String(error) });
}
}
export async function getWorksheets(req: Request, res: Response) {
try {
const names = listWorksheets();
res.status(200).send(names);
} catch (error) {
res.status(500).send({ message: String(error) });
}
}
/**
* parses an Excel spreadsheet
* @returns parsed result
*/
export async function previewExcel(
req: Request,
res: Response<{ rundown: Rundown; customFields: CustomFields } | { message: string }>,
) {
try {
const { options } = req.body;
const data = generateRundownPreview(options);
res.status(200).send(data);
} catch (error) {
res.status(500).send({ message: String(error) });
}
}
+36 -8
View File
@@ -1,14 +1,42 @@
/**
* This is a feature specific router for integration with Excel
*/
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express';
import { uploadExcel } from './excel.middleware.js'; import { uploadExcel } from './excel.middleware.js';
import { getWorksheets, postExcel, previewExcel } from './excel.controller.js';
import { validateFileExists, validateImportMapOptions } from './excel.validation.js'; import { validateFileExists, validateImportMapOptions } from './excel.validation.js';
import { CustomFields, ErrorResponse, Rundown } from 'ontime-types';
import { generateRundownPreview, listWorksheets, saveExcelFile } from './excel.service.js';
export const router = express.Router(); export const router = express.Router();
router.post('/upload', uploadExcel, validateFileExists, postExcel); router.post('/upload', uploadExcel, validateFileExists, async (req: Request, res: Response<never | ErrorResponse>) => {
router.get('/worksheets', getWorksheets); try {
router.post('/preview', validateImportMapOptions, previewExcel); // file has been validated by middleware
const filePath = (req.file as Express.Multer.File).path;
await saveExcelFile(filePath);
res.status(201).send();
} catch (error) {
res.status(500).send({ message: String(error) });
}
});
router.get('/worksheets', (_req: Request, res: Response<string[] | ErrorResponse>) => {
try {
const names = listWorksheets();
res.status(200).send(names);
} catch (error) {
res.status(500).send({ message: String(error) });
}
});
router.post(
'/preview',
validateImportMapOptions,
(req: Request, res: Response<{ rundown: Rundown; customFields: CustomFields } | ErrorResponse>) => {
try {
const { options } = req.body;
const data = generateRundownPreview(options);
res.status(200).send(data);
} catch (error) {
res.status(500).send({ message: String(error) });
}
},
);
@@ -1,16 +1,12 @@
import { isImportMap } from 'ontime-utils'; import { isImportMap } from 'ontime-utils';
import { body, validationResult } from 'express-validator'; import { body } from 'express-validator';
import type { NextFunction, Request, Response } from 'express'; import {
requestValidationFunction,
requestValidationFunctionWithFile,
} from '../validation-utils/validationFunction.js';
export const validateFileExists = [ export const validateFileExists = [requestValidationFunctionWithFile];
(req: Request, res: Response, next: NextFunction) => {
if (!req.file) {
return res.status(422).json({ errors: 'File not found' });
}
next();
},
];
export const validateImportMapOptions = [ export const validateImportMapOptions = [
body('options') body('options')
@@ -19,9 +15,5 @@ export const validateImportMapOptions = [
return isImportMap(content); return isImportMap(content);
}), }),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
+2 -2
View File
@@ -31,6 +31,6 @@ appRouter.use('/report', reportRouter);
appRouter.use('/assets', assetsRouter); appRouter.use('/assets', assetsRouter);
//we don't want to redirect to react index when using api routes //we don't want to redirect to react index when using api routes
appRouter.all('/*', (_req, res) => { appRouter.all('/*splat', (_req, res) => {
res.status(404).send(); res.status(404).send('data path not found');
}); });
@@ -1,40 +0,0 @@
import { ErrorResponse, ProjectData } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import type { Request, Response } from 'express';
import { removeUndefined } from '../../utils/parserUtils.js';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { editCurrentProjectData } from '../../services/project-service/ProjectService.js';
import * as projectDao from './projectData.dao.js';
export function getProjectData(_req: Request, res: Response<ProjectData>) {
res.json(projectDao.getProjectData());
}
export async function postProjectData(req: Request, res: Response<ProjectData | ErrorResponse>) {
if (failEmptyObjects(req.body, res)) {
return;
}
try {
const newData: Partial<ProjectData> = removeUndefined({
title: req.body?.title,
description: req.body?.description,
publicUrl: req.body?.publicUrl,
publicInfo: req.body?.publicInfo,
backstageUrl: req.body?.backstageUrl,
backstageInfo: req.body?.backstageInfo,
endMessage: req.body?.endMessage,
projectLogo: req.body?.projectLogo,
custom: req.body?.custom,
});
const updatedData = await editCurrentProjectData(newData);
res.status(200).send(updatedData);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).send({ message });
}
}
@@ -1,12 +1,42 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express';
import type { ErrorResponse, ProjectData } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { getProjectData, postProjectData } from './projectData.controller.js';
import { projectSanitiser } from './projectData.validation.js'; import { projectSanitiser } from './projectData.validation.js';
import { uploadImageFile } from '../db/db.middleware.js'; import { uploadImageFile } from '../db/db.middleware.js';
import { postProjectLogo } from '../db/db.controller.js'; import { postProjectLogo } from '../db/db.controller.js';
import * as projectDao from './projectData.dao.js';
import { removeUndefined } from '../../utils/parserUtils.js';
import { editCurrentProjectData } from '../../services/project-service/ProjectService.js';
export const router = express.Router(); export const router = express.Router();
router.get('/', getProjectData); router.get('/', (_req: Request, res: Response<ProjectData>) => {
router.post('/', projectSanitiser, postProjectData); res.status(200).json(projectDao.getProjectData());
});
router.post('/', projectSanitiser, async (req: Request, res: Response<ProjectData | ErrorResponse>) => {
try {
const newData: Partial<ProjectData> = removeUndefined({
title: req.body?.title,
description: req.body?.description,
publicUrl: req.body?.publicUrl,
publicInfo: req.body?.publicInfo,
backstageUrl: req.body?.backstageUrl,
backstageInfo: req.body?.backstageInfo,
endMessage: req.body?.endMessage,
projectLogo: req.body?.projectLogo,
custom: req.body?.custom,
});
const updatedData = await editCurrentProjectData(newData);
res.status(200).send(updatedData);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).send({ message });
}
});
router.post('/upload', uploadImageFile, postProjectLogo); router.post('/upload', uploadImageFile, postProjectLogo);
@@ -1,7 +1,8 @@
import { Request, Response, NextFunction } from 'express'; import { body } from 'express-validator';
import { body, validationResult } from 'express-validator'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const projectSanitiser = [ export const projectSanitiser = [
body().notEmpty().withMessage('No object found in request'),
body('title').optional().isString().trim(), body('title').optional().isString().trim(),
body('description').optional().isString().trim(), body('description').optional().isString().trim(),
body('publicUrl').optional().isString().trim(), body('publicUrl').optional().isString().trim(),
@@ -9,14 +10,10 @@ export const projectSanitiser = [
body('backstageUrl').optional().isString().trim(), body('backstageUrl').optional().isString().trim(),
body('backstageInfo').optional().isString().trim(), body('backstageInfo').optional().isString().trim(),
body('endMessage').optional().isString().trim(), body('endMessage').optional().isString().trim(),
body('projectLogo').optional({ nullable: true }).isString().trim(), body('projectLogo').optional({ nullable: true }).isString().trim().isBase64(),
body('custom').optional().isArray(), body('custom').optional().isArray(),
body('custom.*.title').optional().isString().trim().notEmpty(), body('custom.*.title').optional().isString().trim().notEmpty(),
body('custom.*.value').optional().isString().trim().notEmpty(), body('custom.*.value').optional().isString().trim().notEmpty(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -1,18 +0,0 @@
import type { Request, Response } from 'express';
import type { OntimeReport } from 'ontime-types';
import * as report from './report.service.js';
export function getAll(_req: Request, res: Response<OntimeReport>) {
res.json(report.generate());
}
export function deleteAll(_req: Request, res: Response<OntimeReport>) {
report.clear();
res.status(200).send();
}
export function deleteWithId(req: Request, res: Response<OntimeReport>) {
const { eventId } = req.params;
report.clear(eventId);
res.status(200).send();
}
@@ -1,10 +1,21 @@
import express from 'express'; import express from 'express';
import { getAll, deleteWithId, deleteAll } from './report.controller.js'; import type { Request, Response } from 'express';
import { paramsMustHaveEntryId } from '../rundown/rundown.validation.js'; import { paramsWithId } from '../validation-utils/validationFunction.js';
import * as report from './report.service.js';
export const router = express.Router(); export const router = express.Router();
router.get('/', getAll); router.get('/', (_req: Request, res: Response) => {
res.status(200).json(report.generate());
});
router.delete('/all', deleteAll); router.delete('/all', (_req: Request, res: Response) => {
router.delete('/:eventId', paramsMustHaveEntryId, deleteWithId); report.clear();
res.status(204).send();
});
router.delete('/:id', paramsWithId, (req: Request, res: Response) => {
const { id } = req.params;
report.clear(id);
res.status(204).send();
});
@@ -19,7 +19,6 @@ import {
ungroupEntries, ungroupEntries,
} from './rundown.service.js'; } from './rundown.service.js';
import { import {
paramsMustHaveEntryId,
rundownArrayOfIds, rundownArrayOfIds,
rundownBatchPutValidator, rundownBatchPutValidator,
rundownPostValidator, rundownPostValidator,
@@ -27,6 +26,7 @@ import {
rundownReorderValidator, rundownReorderValidator,
rundownSwapValidator, rundownSwapValidator,
} from './rundown.validation.js'; } from './rundown.validation.js';
import { paramsWithId } from '../validation-utils/validationFunction.js';
export const router = express.Router(); export const router = express.Router();
@@ -100,11 +100,11 @@ router.patch('/swap', rundownSwapValidator, async (req: Request, res: Response<R
}); });
router.patch( router.patch(
'/applydelay/:entryId', '/applydelay/:id',
paramsMustHaveEntryId, paramsWithId,
async (req: Request, res: Response<Rundown | ErrorResponse>) => { async (req: Request, res: Response<Rundown | ErrorResponse>) => {
try { try {
const newRundown = await applyDelay(req.params.entryId); const newRundown = await applyDelay(req.params.id);
res.status(200).send(newRundown); res.status(200).send(newRundown);
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
@@ -113,9 +113,9 @@ router.patch(
}, },
); );
router.post('/clone/:entryId', paramsMustHaveEntryId, async (req: Request, res: Response<Rundown | ErrorResponse>) => { router.post('/clone/:id', paramsWithId, async (req: Request, res: Response<Rundown | ErrorResponse>) => {
try { try {
const newRundown = await cloneEntry(req.params.entryId); const newRundown = await cloneEntry(req.params.id);
res.status(200).send(newRundown); res.status(200).send(newRundown);
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
@@ -134,11 +134,11 @@ router.post('/group', rundownArrayOfIds, async (req: Request, res: Response<Rund
}); });
router.post( router.post(
'/ungroup/:entryId', '/ungroup/:id',
paramsMustHaveEntryId, paramsWithId,
async (req: Request, res: Response<Rundown | ErrorResponse>) => { async (req: Request, res: Response<Rundown | ErrorResponse>) => {
try { try {
const newRundown = await ungroupEntries(req.params.entryId); const newRundown = await ungroupEntries(req.params.id);
res.status(200).send(newRundown); res.status(200).send(newRundown);
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
@@ -1,38 +1,22 @@
import { body, param, validationResult } from 'express-validator'; import { body, param } from 'express-validator';
import type { Request, Response, NextFunction } from 'express'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const rundownPostValidator = [ export const rundownPostValidator = [
body('type').isString().isIn(['event', 'delay', 'block']), body('type').isString().isIn(['event', 'delay', 'block']),
body('after').optional().isString(), body('after').optional().isString(),
body('before').optional().isString(), body('before').optional().isString(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const rundownPutValidator = [ export const rundownPutValidator = [body('id').isString().notEmpty(), requestValidationFunction];
body('id').isString().notEmpty(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const rundownBatchPutValidator = [ export const rundownBatchPutValidator = [
body('data').isObject(), body('data').isObject(),
body('ids').isArray().notEmpty(), body('ids').isArray().notEmpty(),
body('ids.*').isString(), body('ids.*').isString(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const rundownReorderValidator = [ export const rundownReorderValidator = [
@@ -40,41 +24,21 @@ export const rundownReorderValidator = [
body('destinationId').isString().notEmpty(), body('destinationId').isString().notEmpty(),
body('order').isIn(['before', 'after', 'insert']), body('order').isIn(['before', 'after', 'insert']),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const rundownSwapValidator = [ export const rundownSwapValidator = [
body('from').isString().notEmpty(), body('from').isString().notEmpty(),
body('to').isString().notEmpty(), body('to').isString().notEmpty(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
export const paramsMustHaveEntryId = [ export const paramsMustHaveEntryId = [param('entryId').isString().notEmpty(), requestValidationFunction];
param('entryId').isString().notEmpty(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const rundownArrayOfIds = [ export const rundownArrayOfIds = [
body('ids').isArray().notEmpty(), body('ids').isArray().notEmpty(),
body('ids.*').isString(), body('ids.*').isString(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -1,41 +0,0 @@
import { getErrorMessage } from 'ontime-utils';
import { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types';
import type { Request, Response } from 'express';
import * as sessionService from './session.service.js';
export async function getSessionStats(_req: Request, res: Response<SessionStats | ErrorResponse>) {
try {
const stats = await sessionService.getSessionStats();
res.status(200).send(stats);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
}
export async function getInfo(_req: Request, res: Response<GetInfo | ErrorResponse>) {
try {
const info = await sessionService.getInfo();
res.status(200).send(info);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
}
export async function generateUrl(req: Request, res: Response<GetUrl | ErrorResponse>) {
try {
const url = sessionService.generateAuthenticatedUrl(
req.body.baseUrl,
req.body.path,
req.body.lock,
req.body.authenticate,
);
res.status(200).send({ url: url.toString() });
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
}
@@ -1,10 +1,43 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express';
import { getInfo, getSessionStats, generateUrl } from './session.controller.js'; import { getErrorMessage } from 'ontime-utils';
import type { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types';
import { validateGenerateUrl } from './session.validation.js'; import { validateGenerateUrl } from './session.validation.js';
import * as sessionService from './session.service.js';
export const router = express.Router(); export const router = express.Router();
router.get('/', getSessionStats); router.get('/', async (_req: Request, res: Response<SessionStats | ErrorResponse>) => {
router.get('/info', getInfo); try {
router.post('/url', validateGenerateUrl, generateUrl); const stats = await sessionService.getSessionStats();
res.status(200).send(stats);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
router.get('/info', async (_req: Request, res: Response<GetInfo | ErrorResponse>) => {
try {
const info = await sessionService.getInfo();
res.status(200).send(info);
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
router.post('/url', validateGenerateUrl, (req: Request, res: Response<GetUrl | ErrorResponse>) => {
try {
const url = sessionService.generateAuthenticatedUrl(
req.body.baseUrl,
req.body.path,
req.body.lock,
req.body.authenticate,
);
res.status(200).send({ url: url.toString() });
} catch (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
}
});
@@ -1,5 +1,6 @@
import type { Request, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator'; import { body } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateGenerateUrl = [ export const validateGenerateUrl = [
body('baseUrl').isString().trim().notEmpty(), body('baseUrl').isString().trim().notEmpty(),
@@ -7,9 +8,5 @@ export const validateGenerateUrl = [
body('lock').isBoolean(), body('lock').isBoolean(),
body('authenticate').isBoolean(), body('authenticate').isBoolean(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -4,7 +4,6 @@ import { getErrorMessage, obfuscate } from 'ontime-utils';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import { isDocker } from '../../setup/environment.js'; import { isDocker } from '../../setup/environment.js';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as appState from '../../services/app-state-service/AppStateService.js'; import * as appState from '../../services/app-state-service/AppStateService.js';
@@ -25,9 +24,6 @@ export async function getSettings(_req: Request, res: Response<Settings>) {
} }
export async function postSettings(req: Request, res: Response<Settings | ErrorResponse>) { export async function postSettings(req: Request, res: Response<Settings | ErrorResponse>) {
if (failEmptyObjects(req.body, res)) {
return;
}
try { try {
const settings = getDataProvider().getSettings(); const settings = getDataProvider().getSettings();
const editorKey = extractPin(req.body?.editorKey, settings.editorKey); const editorKey = extractPin(req.body?.editorKey, settings.editorKey);
@@ -35,13 +31,15 @@ export async function postSettings(req: Request, res: Response<Settings | ErrorR
const serverPort = Number(req.body?.serverPort); const serverPort = Number(req.body?.serverPort);
//TODO: should this not be part of the validator? //TODO: should this not be part of the validator?
if (isNaN(serverPort)) { if (isNaN(serverPort)) {
return res.status(400).send({ message: `Invalid value found for server port: ${req.body?.serverPort}` }); res.status(400).send({ message: `Invalid value found for server port: ${req.body?.serverPort}` });
return;
} }
const hasChangedPort = settings.serverPort !== serverPort; const hasChangedPort = settings.serverPort !== serverPort;
if (isDocker && hasChangedPort) { if (isDocker && hasChangedPort) {
return res.status(403).json({ message: 'Can`t change port when running inside docker' }); res.status(403).json({ message: 'Can`t change port when running inside docker' });
return;
} }
let timeFormat = settings.timeFormat; let timeFormat = settings.timeFormat;
@@ -1,31 +1,21 @@
import { body, validationResult } from 'express-validator'; import { body } from 'express-validator';
import type { Request, Response, NextFunction } from 'express'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/** /**
* @description Validates object for POST /ontime/settings/welcomedialog * @description Validates object for POST /ontime/settings/welcomedialog
*/ */
export const validateWelcomeDialog = [ export const validateWelcomeDialog = [body('show').isBoolean(), requestValidationFunction];
body('show').isBoolean(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
/** /**
* @description Validates object for POST /ontime/settings * @description Validates object for POST /ontime/settings
*/ */
export const validateSettings = [ export const validateSettings = [
body().notEmpty().withMessage('No object found in request'),
body('editorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }), body('editorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }),
body('operatorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }), body('operatorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }),
body('timeFormat').isString().isIn(['12', '24']), body('timeFormat').isString().isIn(['12', '24']),
body('language').isString(), body('language').isString(),
body('serverPort').isPort().optional(), body('serverPort').isPort().optional(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -2,6 +2,7 @@ import { isImportMap } from 'ontime-utils';
import { body, param, validationResult } from 'express-validator'; import { body, param, validationResult } from 'express-validator';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateRequestConnection = [ export const validateRequestConnection = [
param('sheetId') param('sheetId')
@@ -14,27 +15,23 @@ export const validateRequestConnection = [
(req: Request, res: Response, next: NextFunction) => { (req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); if (!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() });
return;
}
// check that the file exists // check that the file exists
if (!req.file) { if (!req.file) {
return res.status(422).json({ errors: 'File not found' }); res.status(422).json({ errors: 'File not found' });
return;
} }
next(); next();
}, },
]; ];
export const validateSheetId = [ export const validateSheetId = [param('sheetId').isString().trim().notEmpty(), requestValidationFunction];
param('sheetId').isString(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const validateSheetOptions = [ export const validateSheetOptions = [
param('sheetId').isString(), param('sheetId').isString().trim().notEmpty(),
body('options') body('options')
.isObject() .isObject()
.custom((content) => { .custom((content) => {
@@ -42,9 +39,5 @@ export const validateSheetOptions = [
return isValid; return isValid;
}), }),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -1,30 +0,0 @@
import type { ErrorResponse, URLPreset } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import type { Request, Response } from 'express';
import { failIsNotArray } from '../../utils/routerUtils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
export async function getUrlPresets(_req: Request, res: Response<URLPreset[]>) {
const presets = getDataProvider().getUrlPresets();
res.status(200).send(presets as URLPreset[]);
}
export async function postUrlPresets(req: Request, res: Response<URLPreset[] | ErrorResponse>) {
if (failIsNotArray(req.body, res)) {
return;
}
try {
const newPresets: URLPreset[] = req.body.map((preset: URLPreset) => ({
enabled: preset.enabled,
alias: preset.alias,
pathAndParams: preset.pathAndParams,
}));
await getDataProvider().setUrlPresets(newPresets);
res.status(200).send(newPresets);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).send({ message });
}
}
@@ -1,8 +1,28 @@
import express from 'express'; import express from 'express';
import { getUrlPresets, postUrlPresets } from './urlPresets.controller.js'; import type { Request, Response } from 'express';
import type { ErrorResponse, URLPreset } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { validateUrlPresets } from './urlPresets.validation.js'; import { validateUrlPresets } from './urlPresets.validation.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
export const router = express.Router(); export const router = express.Router();
router.get('/', getUrlPresets); router.get('/', (_req: Request, res: Response<URLPreset[]>) => {
router.post('/', validateUrlPresets, postUrlPresets); const presets = getDataProvider().getUrlPresets();
res.status(200).send(presets as URLPreset[]);
});
router.post('/', validateUrlPresets, async (req: Request, res: Response<URLPreset[] | ErrorResponse>) => {
try {
const newPresets: URLPreset[] = req.body.map((preset: URLPreset) => ({
enabled: preset.enabled,
alias: preset.alias,
pathAndParams: preset.pathAndParams,
}));
await getDataProvider().setUrlPresets(newPresets);
res.status(200).send(newPresets);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).send({ message });
}
});
@@ -1,18 +1,14 @@
import { body, validationResult } from 'express-validator'; import { body } from 'express-validator';
import { Request, Response, NextFunction } from 'express'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/** /**
* validate array of URL preset objects * validate array of URL preset objects
*/ */
export const validateUrlPresets = [ export const validateUrlPresets = [
body().isArray(), body().isArray().withMessage('No array found in request'),
body('*.enabled').isBoolean(), body('*.enabled').isBoolean(),
body('*.alias').isString().trim(), body('*.alias').isString().trim().notEmpty(),
body('*.pathAndParams').isString().trim(), body('*.pathAndParams').isString().trim().notEmpty(),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -0,0 +1,34 @@
import type { Request, Response, NextFunction } from 'express';
import { param, validationResult } from 'express-validator';
/**
* Runs validation and any error are sent with status 422
*/
export function requestValidationFunction(req: Request, res: Response, next: NextFunction) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() });
return;
}
next();
}
/**
* Runs validation and any error are sent with status 422
* Also checks for the presses of a `file` in the body
*/
export function requestValidationFunctionWithFile(req: Request, res: Response, next: NextFunction) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() });
return;
}
// check that the file exists
if (!req.file) {
res.status(422).json({ errors: 'File not found' });
return;
}
next();
}
export const paramsWithId = [param('id').isString().trim().notEmpty(), requestValidationFunction];
@@ -1,20 +1,16 @@
import { check, validationResult } from 'express-validator'; import { body } from 'express-validator';
import { Request, Response, NextFunction } from 'express'; import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/** /**
* @description Validates object for POST /ontime/views * @description Validates object for POST /ontime/views
*/ */
export const validateViewSettings = [ export const validateViewSettings = [
check('dangerColor').isString().trim().withMessage('dangerColor value must be string'), body('dangerColor').isString().trim().withMessage('dangerColor value must be string'),
check('endMessage').isString().trim().withMessage('endMessage value must be string'), body('endMessage').isString().trim().withMessage('endMessage value must be string'),
check('freezeEnd').isBoolean().withMessage('freezeEnd value must be boolean'), body('freezeEnd').isBoolean().withMessage('freezeEnd value must be boolean'),
check('normalColor').isString().trim().withMessage('normalColor value must be string'), body('normalColor').isString().trim().withMessage('normalColor value must be string'),
check('overrideStyles').isBoolean().withMessage('overrideStyles value must be boolean'), body('overrideStyles').isBoolean().withMessage('overrideStyles value must be boolean'),
check('warningColor').isString().trim().withMessage('warningColor value must be string'), body('warningColor').isString().trim().withMessage('warningColor value must be string'),
(req: Request, res: Response, next: NextFunction) => { requestValidationFunction,
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
]; ];
@@ -5,7 +5,7 @@
* *
*/ */
import { LogOrigin } from 'ontime-types'; import { ErrorResponse, LogOrigin } from 'ontime-types';
import express, { type Request, type Response } from 'express'; import express, { type Request, type Response } from 'express';
@@ -27,10 +27,11 @@ integrationRouter.get('/', (_req: Request, res: Response<{ message: string }>) =
/** /**
* All calls are sent to the dispatcher * All calls are sent to the dispatcher
*/ */
integrationRouter.get('/*', (req: Request, res: Response) => { integrationRouter.get('/*splat', (req: Request, res: Response<ErrorResponse | { payload: unknown }>) => {
let action = req.path.substring(1); let action = req.path.substring(1);
if (!action) { if (!action) {
return res.status(400).json({ error: 'No action found' }); res.status(400).json({ message: 'No action found' });
return;
} }
try { try {
+2 -2
View File
@@ -76,7 +76,7 @@ app.disable('x-powered-by');
// Implement middleware // Implement middleware
app.use(cors()); // setup cors for all routes app.use(cors()); // setup cors for all routes
app.options('*', cors()); // enable pre-flight cors app.options('*splat', cors()); // enable pre-flight cors
app.use(bodyParser); app.use(bodyParser);
app.use(cookieParser()); app.use(cookieParser());
@@ -97,7 +97,7 @@ app.use(`${prefix}/user`, express.static(publicDir.userDir));
// Base route for static files // Base route for static files
app.use(`${prefix}`, authenticateAndRedirect, compressedStatic); app.use(`${prefix}`, authenticateAndRedirect, compressedStatic);
app.use(`${prefix}/*`, authenticateAndRedirect, compressedStatic); app.use(`${prefix}/*splat`, authenticateAndRedirect, compressedStatic);
// Implement catch all // Implement catch all
app.use((_error, response) => { app.use((_error, response) => {
-39
View File
@@ -1,39 +0,0 @@
import type { Response } from 'express';
import { isEmptyObject } from './parserUtils.js';
/**
* @description initial checks for an empty of malformed request object
* @param obj
* @param res
*/
export const failEmptyObjects = (obj: object, res: Response): boolean => {
try {
if (isEmptyObject(obj)) {
res.status(400).send('No object found in request');
return true;
}
} catch (error) {
res.status(400).send(error);
return true;
}
return false;
};
/**
* @description initial checks for an empty of malformed request object
* @param obj
* @param res
*/
export const failIsNotArray = (obj: object, res: Response): boolean => {
try {
if (!Array.isArray(obj)) {
res.status(400).send('No array found in request');
return true;
}
} catch (error) {
res.status(400).send(error);
return true;
}
return false;
};
+13 -12
View File
@@ -1,9 +1,10 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import { randomUUID } from 'crypto';
import { readFile } from 'fs/promises'; import { readFile, unlink } from 'fs/promises';
const fileToUpload = 'e2e/tests/fixtures/e2e-test-db.json'; const fileToUpload = 'e2e/tests/fixtures/e2e-test-db.json';
const fileToDownload = 'e2e/tests/fixtures/tmp/e2e-test-db.json'; const fileToDownload = 'e2e/tests/fixtures/tmp/';
test('project file upload', async ({ page }) => { test('project file upload', async ({ page }) => {
await page.goto('http://localhost:4001/editor'); await page.goto('http://localhost:4001/editor');
@@ -35,30 +36,30 @@ test('project file upload', async ({ page }) => {
await expect(thirdTitle).toHaveValue('Lithuania'); await expect(thirdTitle).toHaveValue('Lithuania');
}); });
//TODO: this works when testing locally, but not in github actions
test.fixme('project file download', async ({ page }) => { test.fixme('project file download', async ({ page }) => {
await page.goto('http://localhost:4001/editor'); await page.goto('http://localhost:4001/editor/?settings=project__manage');
await page.getByRole('button', { name: 'toggle settings' }).click();
await page.getByRole('button', { name: 'Manage projects' }).click();
// workaround to download
// https://playwright.dev/docs/api/class-download
const downloadPromise = page.waitForEvent('download');
await page await page
.getByRole('row', { name: /.*currently loaded/i }) .getByRole('row', { name: /.*currently loaded/i })
.getByLabel('Options') .getByLabel('Options')
.click(); .click();
// workaround to download
// https://playwright.dev/docs/api/class-download
const downloadPromise = page.waitForEvent('download', { timeout: 10_000 });
await page.getByRole('menuitem', { name: 'Download' }).click(); await page.getByRole('menuitem', { name: 'Download' }).click();
const download = await downloadPromise; const download = await downloadPromise;
// Wait for the download process to complete and save the downloaded file somewhere. // Wait for the download process to complete and save the downloaded file somewhere.
await download.saveAs(fileToDownload); const uniqFileToDownload = fileToDownload + randomUUID() + '.json';
await download.saveAs(uniqFileToDownload);
expect(download.failure()).toMatchObject({}); expect(download.failure()).toMatchObject({});
const original = JSON.parse(await readFile(fileToUpload, { encoding: 'utf-8' })); const original = JSON.parse(await readFile(fileToUpload, { encoding: 'utf-8' }));
const fromServer = JSON.parse(await readFile(fileToDownload, { encoding: 'utf-8' })); const fromServer = JSON.parse(await readFile(uniqFileToDownload, { encoding: 'utf-8' }));
await unlink(uniqFileToDownload);
// when a file is parsed, the server will write the version number to the project file // when a file is parsed, the server will write the version number to the project file
original.settings.version = 'not-important'; original.settings.version = 'not-important';
+22 -3
View File
@@ -21,6 +21,24 @@
"bab4a", "bab4a",
"d3eb1" "d3eb1"
], ],
"flatOrder": [
"32d31",
"21cd2",
"0b371",
"3cd28",
"e457f",
"01e85",
"1c420",
"b7737",
"d3a80",
"8276c",
"2340b",
"cb90b",
"503c4",
"5e965",
"bab4a",
"d3eb1"
],
"entries": { "entries": {
"32d31": { "32d31": {
"type": "event", "type": "event",
@@ -469,7 +487,8 @@
"publicInfo": "Rehearsal Schedule - Turin 2022", "publicInfo": "Rehearsal Schedule - Turin 2022",
"backstageUrl": "www.github.com/cpvalente/ontime", "backstageUrl": "www.github.com/cpvalente/ontime",
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal", "backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
"projectLogo": null "projectLogo": null,
"custom": []
}, },
"settings": { "settings": {
"app": "ontime", "app": "ontime",
@@ -489,12 +508,12 @@
"warningColor": "#FFAB33" "warningColor": "#FFAB33"
}, },
"customFields": { "customFields": {
"song": { "Song": {
"label": "Song", "label": "Song",
"type": "string", "type": "string",
"colour": "#339E4E" "colour": "#339E4E"
}, },
"artist": { "Artist": {
"label": "Artist", "label": "Artist",
"type": "string", "type": "string",
"colour": "#3E75E8" "colour": "#3E75E8"
+202 -236
View File
@@ -289,26 +289,26 @@ importers:
specifier: ^5.0.5 specifier: ^5.0.5
version: 5.0.5(encoding@0.1.13) version: 5.0.5(encoding@0.1.13)
cookie: cookie:
specifier: ^1.0.2 specifier: 1.0.2
version: 1.0.2 version: 1.0.2
cookie-parser: cookie-parser:
specifier: ^1.4.7 specifier: 1.4.7
version: 1.4.7 version: 1.4.7
cors: cors:
specifier: ^2.8.5 specifier: 2.8.5
version: 2.8.5 version: 2.8.5
dotenv: dotenv:
specifier: ^16.0.1 specifier: ^16.0.1
version: 16.3.1 version: 16.3.1
express: express:
specifier: ^4.21.1 specifier: 5.1.0
version: 4.21.2 version: 5.1.0
express-static-gzip: express-static-gzip:
specifier: ^2.2.0 specifier: 3.0.0
version: 2.2.0 version: 3.0.0
express-validator: express-validator:
specifier: ^7.2.0 specifier: 7.2.1
version: 7.2.0 version: 7.2.1
fast-equals: fast-equals:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1 version: 5.0.1
@@ -322,8 +322,8 @@ importers:
specifier: ^7.0.1 specifier: ^7.0.1
version: 7.0.1 version: 7.0.1
multer: multer:
specifier: ^1.4.5-lts.1 specifier: 2.0.1
version: 1.4.5-lts.1 version: 2.0.1
ontime-utils: ontime-utils:
specifier: workspace:* specifier: workspace:*
version: link:../../packages/utils version: link:../../packages/utils
@@ -344,17 +344,17 @@ importers:
version: 0.18.5 version: 0.18.5
devDependencies: devDependencies:
'@types/cookie-parser': '@types/cookie-parser':
specifier: ^1.4.8 specifier: 1.4.9
version: 1.4.8(@types/express@4.17.17) version: 1.4.9(@types/express@5.0.3)
'@types/cors': '@types/cors':
specifier: ^2.8.17 specifier: 2.8.19
version: 2.8.17 version: 2.8.19
'@types/express': '@types/express':
specifier: ^4.17.17 specifier: 5.0.3
version: 4.17.17 version: 5.0.3
'@types/multer': '@types/multer':
specifier: ^1.4.11 specifier: 1.4.13
version: 1.4.11 version: 1.4.13
'@types/node': '@types/node':
specifier: 'catalog:' specifier: 'catalog:'
version: 22.15.26 version: 22.15.26
@@ -2207,13 +2207,13 @@ packages:
'@types/connect@3.4.35': '@types/connect@3.4.35':
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
'@types/cookie-parser@1.4.8': '@types/cookie-parser@1.4.9':
resolution: {integrity: sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==} resolution: {integrity: sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==}
peerDependencies: peerDependencies:
'@types/express': '*' '@types/express': '*'
'@types/cors@2.8.17': '@types/cors@2.8.19':
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
'@types/debug@4.1.12': '@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -2227,11 +2227,11 @@ packages:
'@types/estree@1.0.7': '@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
'@types/express-serve-static-core@4.17.33': '@types/express-serve-static-core@5.0.6':
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==}
'@types/express@4.17.17': '@types/express@5.0.3':
resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==}
'@types/fs-extra@9.0.13': '@types/fs-extra@9.0.13':
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
@@ -2251,14 +2251,17 @@ packages:
'@types/lodash@4.14.191': '@types/lodash@4.14.191':
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
'@types/mime@1.3.5':
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
'@types/mime@3.0.1': '@types/mime@3.0.1':
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
'@types/ms@0.7.34': '@types/ms@0.7.34':
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
'@types/multer@1.4.11': '@types/multer@1.4.13':
resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} resolution: {integrity: sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==}
'@types/node@22.15.26': '@types/node@22.15.26':
resolution: {integrity: sha512-lgISkNrqdQ5DAzjBhnDNGKDuXDNo7/1V4FhNzsKREhWLZTOELQAptuAnJMzHtUl1qyEBBy9lNBKQ9WjyiSloTw==} resolution: {integrity: sha512-lgISkNrqdQ5DAzjBhnDNGKDuXDNo7/1V4FhNzsKREhWLZTOELQAptuAnJMzHtUl1qyEBBy9lNBKQ9WjyiSloTw==}
@@ -2296,6 +2299,9 @@ packages:
'@types/semver@7.5.5': '@types/semver@7.5.5':
resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==} resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
'@types/send@0.17.5':
resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==}
'@types/serve-static@1.15.0': '@types/serve-static@1.15.0':
resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==}
@@ -2451,8 +2457,8 @@ packages:
abbrev@1.1.1: abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
accepts@1.3.8: accepts@2.0.0:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
acorn-globals@7.0.1: acorn-globals@7.0.1:
@@ -2571,9 +2577,6 @@ packages:
resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
array-flatten@1.1.1:
resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
array-includes@3.1.6: array-includes@3.1.6:
resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2651,9 +2654,9 @@ packages:
bluebird@3.7.2: bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
body-parser@1.20.3: body-parser@2.2.0:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>=18'}
boolean@3.2.0: boolean@3.2.0:
resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
@@ -2741,10 +2744,6 @@ packages:
resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
engines: {node: '>=8'} engines: {node: '>=8'}
call-bind-apply-helpers@1.0.1:
resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==}
engines: {node: '>= 0.4'}
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2881,9 +2880,9 @@ packages:
concat-map@0.0.1: concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
concat-stream@1.6.2: concat-stream@2.0.0:
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
engines: {'0': node >= 0.8} engines: {'0': node >= 6.0}
config-file-ts@0.2.4: config-file-ts@0.2.4:
resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==} resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==}
@@ -2891,8 +2890,8 @@ packages:
config-file-ts@0.2.8-rc1: config-file-ts@0.2.8-rc1:
resolution: {integrity: sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==} resolution: {integrity: sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==}
content-disposition@0.5.4: content-disposition@1.0.0:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
content-type@1.0.5: content-type@1.0.5:
@@ -2912,9 +2911,9 @@ packages:
cookie-signature@1.0.6: cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie@0.7.1: cookie-signature@1.2.2:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
engines: {node: '>= 0.6'} engines: {node: '>=6.6.0'}
cookie@0.7.2: cookie@0.7.2:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
@@ -2998,14 +2997,6 @@ packages:
resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
debug@4.3.7: debug@4.3.7:
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@@ -3065,10 +3056,6 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
detect-libc@2.0.4: detect-libc@2.0.4:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -3182,10 +3169,6 @@ packages:
emoji-regex@9.2.2: emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
encodeurl@2.0.0: encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@@ -3225,10 +3208,6 @@ packages:
es-module-lexer@1.7.0: es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
es-object-atoms@1.0.0:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1: es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3410,16 +3389,16 @@ packages:
exponential-backoff@3.1.2: exponential-backoff@3.1.2:
resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
express-static-gzip@2.2.0: express-static-gzip@3.0.0:
resolution: {integrity: sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==} resolution: {integrity: sha512-36O10S0asHl3QojOBQQ0ZjXNtElmhgPS6erSUCCZymXkB/CK1mnGqOj4BTJN+FYRDIzVFnzo3wLFCZJvAk6rQQ==}
express-validator@7.2.0: express-validator@7.2.1:
resolution: {integrity: sha512-I2ByKD8panjtr8Y05l21Wph9xk7kk64UMyvJCl/fFM/3CTJq8isXYPLeKW/aZBCdb/LYNv63PwhY8khw8VWocA==} resolution: {integrity: sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==}
engines: {node: '>= 8.0.0'} engines: {node: '>= 8.0.0'}
express@4.21.2: express@5.1.0:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
engines: {node: '>= 0.10.0'} engines: {node: '>= 18'}
extend@3.0.2: extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@@ -3478,8 +3457,8 @@ packages:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
finalhandler@1.3.1: finalhandler@2.1.0:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
find-root@1.1.0: find-root@1.1.0:
@@ -3546,9 +3525,9 @@ packages:
framesync@6.1.2: framesync@6.1.2:
resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==}
fresh@0.5.2: fresh@2.0.0:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.8'}
fs-constants@1.0.0: fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
@@ -3615,10 +3594,6 @@ packages:
get-intrinsic@1.2.2: get-intrinsic@1.2.2:
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
get-intrinsic@1.2.6:
resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==}
engines: {node: '>= 0.4'}
get-intrinsic@1.2.7: get-intrinsic@1.2.7:
resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -3827,10 +3802,6 @@ packages:
engines: {node: ^8.11.2 || >=10} engines: {node: ^8.11.2 || >=10}
os: [darwin] os: [darwin]
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
iconv-lite@0.6.3: iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -3959,6 +3930,9 @@ packages:
is-potential-custom-element-name@1.0.1: is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
is-regex@1.1.4: is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -4198,17 +4172,18 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
merge-descriptors@1.0.3: media-typer@1.1.0:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
merge-descriptors@2.0.0:
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
engines: {node: '>=18'}
merge2@1.4.1: merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
micromatch@4.0.5: micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
@@ -4217,14 +4192,17 @@ packages:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
mime-types@2.1.35: mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
mime@1.6.0: mime-types@3.0.1:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>=4'} engines: {node: '>= 0.6'}
hasBin: true
mime@2.6.0: mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
@@ -4322,15 +4300,12 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
multer@1.4.5-lts.1: multer@2.0.1:
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} resolution: {integrity: sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==}
engines: {node: '>= 6.0.0'} engines: {node: '>= 10.16.0'}
nanoid@3.3.11: nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
@@ -4349,6 +4324,10 @@ packages:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
no-case@3.0.4: no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
@@ -4519,8 +4498,9 @@ packages:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'} engines: {node: '>=16 || 14 >=14.18'}
path-to-regexp@0.1.12: path-to-regexp@8.2.0:
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
engines: {node: '>=16'}
path-type@4.0.0: path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
@@ -4639,8 +4619,8 @@ packages:
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
qs@6.13.0: qs@6.14.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
querystringify@2.2.0: querystringify@2.2.0:
@@ -4657,8 +4637,8 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
raw-body@2.5.2: raw-body@3.0.0:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
react-clientside-effect@1.2.6: react-clientside-effect@1.2.6:
@@ -4867,6 +4847,10 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
run-parallel@1.2.0: run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -4919,17 +4903,17 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
send@0.19.0: send@1.2.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 18'}
serialize-error@7.0.1: serialize-error@7.0.1:
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
engines: {node: '>=10'} engines: {node: '>=10'}
serve-static@1.16.2: serve-static@2.2.0:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 18'}
server-timing@3.3.3: server-timing@3.3.3:
resolution: {integrity: sha512-TP0xWAca4oM8H/PSdeaGgp2qm+HrZ2cWCRcMXS2t500a7Wum/hSojlpTW43VZsIUSVNlKPFGDknH34IqF+mbBg==} resolution: {integrity: sha512-TP0xWAca4oM8H/PSdeaGgp2qm+HrZ2cWCRcMXS2t500a7Wum/hSojlpTW43VZsIUSVNlKPFGDknH34IqF+mbBg==}
@@ -5313,6 +5297,10 @@ packages:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
typed-array-length@1.0.4: typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
@@ -5414,10 +5402,6 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
uuid@9.0.1: uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true hasBin: true
@@ -7600,11 +7584,11 @@ snapshots:
dependencies: dependencies:
'@types/node': 22.15.26 '@types/node': 22.15.26
'@types/cookie-parser@1.4.8(@types/express@4.17.17)': '@types/cookie-parser@1.4.9(@types/express@5.0.3)':
dependencies: dependencies:
'@types/express': 4.17.17 '@types/express': 5.0.3
'@types/cors@2.8.17': '@types/cors@2.8.19':
dependencies: dependencies:
'@types/node': 22.15.26 '@types/node': 22.15.26
@@ -7618,17 +7602,17 @@ snapshots:
'@types/estree@1.0.7': {} '@types/estree@1.0.7': {}
'@types/express-serve-static-core@4.17.33': '@types/express-serve-static-core@5.0.6':
dependencies: dependencies:
'@types/node': 22.15.26 '@types/node': 22.15.26
'@types/qs': 6.9.7 '@types/qs': 6.9.7
'@types/range-parser': 1.2.4 '@types/range-parser': 1.2.4
'@types/send': 0.17.5
'@types/express@4.17.17': '@types/express@5.0.3':
dependencies: dependencies:
'@types/body-parser': 1.19.2 '@types/body-parser': 1.19.2
'@types/express-serve-static-core': 4.17.33 '@types/express-serve-static-core': 5.0.6
'@types/qs': 6.9.7
'@types/serve-static': 1.15.0 '@types/serve-static': 1.15.0
'@types/fs-extra@9.0.13': '@types/fs-extra@9.0.13':
@@ -7649,13 +7633,15 @@ snapshots:
'@types/lodash@4.14.191': {} '@types/lodash@4.14.191': {}
'@types/mime@1.3.5': {}
'@types/mime@3.0.1': {} '@types/mime@3.0.1': {}
'@types/ms@0.7.34': {} '@types/ms@0.7.34': {}
'@types/multer@1.4.11': '@types/multer@1.4.13':
dependencies: dependencies:
'@types/express': 4.17.17 '@types/express': 5.0.3
'@types/node@22.15.26': '@types/node@22.15.26':
dependencies: dependencies:
@@ -7695,6 +7681,11 @@ snapshots:
'@types/semver@7.5.5': {} '@types/semver@7.5.5': {}
'@types/send@0.17.5':
dependencies:
'@types/mime': 1.3.5
'@types/node': 22.15.26
'@types/serve-static@1.15.0': '@types/serve-static@1.15.0':
dependencies: dependencies:
'@types/mime': 3.0.1 '@types/mime': 3.0.1
@@ -7904,10 +7895,10 @@ snapshots:
abbrev@1.1.1: {} abbrev@1.1.1: {}
accepts@1.3.8: accepts@2.0.0:
dependencies: dependencies:
mime-types: 2.1.35 mime-types: 3.0.1
negotiator: 0.6.3 negotiator: 1.0.0
acorn-globals@7.0.1: acorn-globals@7.0.1:
dependencies: dependencies:
@@ -8104,8 +8095,6 @@ snapshots:
dependencies: dependencies:
tslib: 2.6.2 tslib: 2.6.2
array-flatten@1.1.1: {}
array-includes@3.1.6: array-includes@3.1.6:
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -8185,20 +8174,17 @@ snapshots:
bluebird@3.7.2: {} bluebird@3.7.2: {}
body-parser@1.20.3: body-parser@2.2.0:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
content-type: 1.0.5 content-type: 1.0.5
debug: 2.6.9 debug: 4.4.1
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0 http-errors: 2.0.0
iconv-lite: 0.4.24 iconv-lite: 0.6.3
on-finished: 2.4.1 on-finished: 2.4.1
qs: 6.13.0 qs: 6.14.0
raw-body: 2.5.2 raw-body: 3.0.0
type-is: 1.6.18 type-is: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -8357,11 +8343,6 @@ snapshots:
normalize-url: 6.1.0 normalize-url: 6.1.0
responselike: 2.0.1 responselike: 2.0.1
call-bind-apply-helpers@1.0.1:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind-apply-helpers@1.0.2: call-bind-apply-helpers@1.0.2:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@@ -8374,7 +8355,7 @@ snapshots:
call-bound@1.0.3: call-bound@1.0.3:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.1 call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.2.7 get-intrinsic: 1.2.7
callsites@3.1.0: {} callsites@3.1.0: {}
@@ -8500,11 +8481,11 @@ snapshots:
concat-map@0.0.1: {} concat-map@0.0.1: {}
concat-stream@1.6.2: concat-stream@2.0.0:
dependencies: dependencies:
buffer-from: 1.1.2 buffer-from: 1.1.2
inherits: 2.0.4 inherits: 2.0.4
readable-stream: 2.3.8 readable-stream: 3.6.2
typedarray: 0.0.6 typedarray: 0.0.6
config-file-ts@0.2.4: config-file-ts@0.2.4:
@@ -8517,7 +8498,7 @@ snapshots:
glob: 10.4.5 glob: 10.4.5
typescript: 5.5.3 typescript: 5.5.3
content-disposition@0.5.4: content-disposition@1.0.0:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
@@ -8534,7 +8515,7 @@ snapshots:
cookie-signature@1.0.6: {} cookie-signature@1.0.6: {}
cookie@0.7.1: {} cookie-signature@1.2.2: {}
cookie@0.7.2: {} cookie@0.7.2: {}
@@ -8625,10 +8606,6 @@ snapshots:
whatwg-url: 11.0.0 whatwg-url: 11.0.0
optional: true optional: true
debug@2.6.9:
dependencies:
ms: 2.0.0
debug@4.3.7: debug@4.3.7:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
@@ -8672,8 +8649,6 @@ snapshots:
depd@2.0.0: {} depd@2.0.0: {}
destroy@1.2.0: {}
detect-libc@2.0.4: {} detect-libc@2.0.4: {}
detect-node-es@1.1.0: {} detect-node-es@1.1.0: {}
@@ -8838,8 +8813,6 @@ snapshots:
emoji-regex@9.2.2: {} emoji-regex@9.2.2: {}
encodeurl@1.0.2: {}
encodeurl@2.0.0: {} encodeurl@2.0.0: {}
encoding@0.1.13: encoding@0.1.13:
@@ -8903,10 +8876,6 @@ snapshots:
es-module-lexer@1.7.0: {} es-module-lexer@1.7.0: {}
es-object-atoms@1.0.0:
dependencies:
es-errors: 1.3.0
es-object-atoms@1.1.1: es-object-atoms@1.1.1:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@@ -9179,50 +9148,47 @@ snapshots:
exponential-backoff@3.1.2: {} exponential-backoff@3.1.2: {}
express-static-gzip@2.2.0: express-static-gzip@3.0.0:
dependencies: dependencies:
mime-types: 3.0.1
parseurl: 1.3.3 parseurl: 1.3.3
serve-static: 1.16.2 serve-static: 2.2.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
express-validator@7.2.0: express-validator@7.2.1:
dependencies: dependencies:
lodash: 4.17.21 lodash: 4.17.21
validator: 13.12.0 validator: 13.12.0
express@4.21.2: express@5.1.0:
dependencies: dependencies:
accepts: 1.3.8 accepts: 2.0.0
array-flatten: 1.1.1 body-parser: 2.2.0
body-parser: 1.20.3 content-disposition: 1.0.0
content-disposition: 0.5.4
content-type: 1.0.5 content-type: 1.0.5
cookie: 0.7.1 cookie: 0.7.2
cookie-signature: 1.0.6 cookie-signature: 1.2.2
debug: 2.6.9 debug: 4.4.1
depd: 2.0.0
encodeurl: 2.0.0 encodeurl: 2.0.0
escape-html: 1.0.3 escape-html: 1.0.3
etag: 1.8.1 etag: 1.8.1
finalhandler: 1.3.1 finalhandler: 2.1.0
fresh: 0.5.2 fresh: 2.0.0
http-errors: 2.0.0 http-errors: 2.0.0
merge-descriptors: 1.0.3 merge-descriptors: 2.0.0
methods: 1.1.2 mime-types: 3.0.1
on-finished: 2.4.1 on-finished: 2.4.1
once: 1.4.0
parseurl: 1.3.3 parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7 proxy-addr: 2.0.7
qs: 6.13.0 qs: 6.14.0
range-parser: 1.2.1 range-parser: 1.2.1
safe-buffer: 5.2.1 router: 2.2.0
send: 0.19.0 send: 1.2.0
serve-static: 1.16.2 serve-static: 2.2.0
setprototypeof: 1.2.0
statuses: 2.0.1 statuses: 2.0.1
type-is: 1.6.18 type-is: 2.0.1
utils-merge: 1.0.1
vary: 1.1.2 vary: 1.1.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -9284,15 +9250,14 @@ snapshots:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
finalhandler@1.3.1: finalhandler@2.1.0:
dependencies: dependencies:
debug: 2.6.9 debug: 4.4.1
encodeurl: 2.0.0 encodeurl: 2.0.0
escape-html: 1.0.3 escape-html: 1.0.3
on-finished: 2.4.1 on-finished: 2.4.1
parseurl: 1.3.3 parseurl: 1.3.3
statuses: 2.0.1 statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -9351,7 +9316,7 @@ snapshots:
dependencies: dependencies:
tslib: 2.4.0 tslib: 2.4.0
fresh@0.5.2: {} fresh@2.0.0: {}
fs-constants@1.0.0: {} fs-constants@1.0.0: {}
@@ -9432,19 +9397,6 @@ snapshots:
has-symbols: 1.0.3 has-symbols: 1.0.3
hasown: 2.0.0 hasown: 2.0.0
get-intrinsic@1.2.6:
dependencies:
call-bind-apply-helpers: 1.0.1
dunder-proto: 1.0.1
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.0.0
function-bind: 1.1.2
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-intrinsic@1.2.7: get-intrinsic@1.2.7:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@@ -9738,10 +9690,6 @@ snapshots:
node-addon-api: 1.7.2 node-addon-api: 1.7.2
optional: true optional: true
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
iconv-lite@0.6.3: iconv-lite@0.6.3:
dependencies: dependencies:
safer-buffer: 2.1.2 safer-buffer: 2.1.2
@@ -9851,6 +9799,8 @@ snapshots:
is-potential-custom-element-name@1.0.1: is-potential-custom-element-name@1.0.1:
optional: true optional: true
is-promise@4.0.0: {}
is-regex@1.1.4: is-regex@1.1.4:
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -10117,12 +10067,12 @@ snapshots:
media-typer@0.3.0: {} media-typer@0.3.0: {}
merge-descriptors@1.0.3: {} media-typer@1.1.0: {}
merge-descriptors@2.0.0: {}
merge2@1.4.1: {} merge2@1.4.1: {}
methods@1.1.2: {}
micromatch@4.0.5: micromatch@4.0.5:
dependencies: dependencies:
braces: 3.0.2 braces: 3.0.2
@@ -10130,11 +10080,15 @@ snapshots:
mime-db@1.52.0: {} mime-db@1.52.0: {}
mime-db@1.54.0: {}
mime-types@2.1.35: mime-types@2.1.35:
dependencies: dependencies:
mime-db: 1.52.0 mime-db: 1.52.0
mime@1.6.0: {} mime-types@3.0.1:
dependencies:
mime-db: 1.54.0
mime@2.6.0: {} mime@2.6.0: {}
@@ -10215,15 +10169,13 @@ snapshots:
mkdirp@1.0.4: {} mkdirp@1.0.4: {}
ms@2.0.0: {}
ms@2.1.3: {} ms@2.1.3: {}
multer@1.4.5-lts.1: multer@2.0.1:
dependencies: dependencies:
append-field: 1.0.0 append-field: 1.0.0
busboy: 1.6.0 busboy: 1.6.0
concat-stream: 1.6.2 concat-stream: 2.0.0
mkdirp: 0.5.6 mkdirp: 0.5.6
object-assign: 4.1.1 object-assign: 4.1.1
type-is: 1.6.18 type-is: 1.6.18
@@ -10237,6 +10189,8 @@ snapshots:
negotiator@0.6.3: {} negotiator@0.6.3: {}
negotiator@1.0.0: {}
no-case@3.0.4: no-case@3.0.4:
dependencies: dependencies:
lower-case: 2.0.2 lower-case: 2.0.2
@@ -10405,7 +10359,7 @@ snapshots:
lru-cache: 10.4.3 lru-cache: 10.4.3
minipass: 7.1.2 minipass: 7.1.2
path-to-regexp@0.1.12: {} path-to-regexp@8.2.0: {}
path-type@4.0.0: {} path-type@4.0.0: {}
@@ -10499,7 +10453,7 @@ snapshots:
dependencies: dependencies:
side-channel: 1.0.4 side-channel: 1.0.4
qs@6.13.0: qs@6.14.0:
dependencies: dependencies:
side-channel: 1.1.0 side-channel: 1.1.0
@@ -10512,11 +10466,11 @@ snapshots:
range-parser@1.2.1: {} range-parser@1.2.1: {}
raw-body@2.5.2: raw-body@3.0.0:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
http-errors: 2.0.0 http-errors: 2.0.0
iconv-lite: 0.4.24 iconv-lite: 0.6.3
unpipe: 1.0.0 unpipe: 1.0.0
react-clientside-effect@1.2.6(react@18.3.1): react-clientside-effect@1.2.6(react@18.3.1):
@@ -10753,6 +10707,16 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.41.1 '@rollup/rollup-win32-x64-msvc': 4.41.1
fsevents: 2.3.3 fsevents: 2.3.3
router@2.2.0:
dependencies:
debug: 4.4.1
depd: 2.0.0
is-promise: 4.0.0
parseurl: 1.3.3
path-to-regexp: 8.2.0
transitivePeerDependencies:
- supports-color
run-parallel@1.2.0: run-parallel@1.2.0:
dependencies: dependencies:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
@@ -10803,17 +10767,15 @@ snapshots:
semver@7.6.2: {} semver@7.6.2: {}
send@0.19.0: send@1.2.0:
dependencies: dependencies:
debug: 2.6.9 debug: 4.4.1
depd: 2.0.0 encodeurl: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3 escape-html: 1.0.3
etag: 1.8.1 etag: 1.8.1
fresh: 0.5.2 fresh: 2.0.0
http-errors: 2.0.0 http-errors: 2.0.0
mime: 1.6.0 mime-types: 3.0.1
ms: 2.1.3 ms: 2.1.3
on-finished: 2.4.1 on-finished: 2.4.1
range-parser: 1.2.1 range-parser: 1.2.1
@@ -10826,12 +10788,12 @@ snapshots:
type-fest: 0.13.1 type-fest: 0.13.1
optional: true optional: true
serve-static@1.16.2: serve-static@2.2.0:
dependencies: dependencies:
encodeurl: 2.0.0 encodeurl: 2.0.0
escape-html: 1.0.3 escape-html: 1.0.3
parseurl: 1.3.3 parseurl: 1.3.3
send: 0.19.0 send: 1.2.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -10868,14 +10830,14 @@ snapshots:
dependencies: dependencies:
call-bound: 1.0.3 call-bound: 1.0.3
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.2.6 get-intrinsic: 1.2.7
object-inspect: 1.13.3 object-inspect: 1.13.3
side-channel-weakmap@1.0.2: side-channel-weakmap@1.0.2:
dependencies: dependencies:
call-bound: 1.0.3 call-bound: 1.0.3
es-errors: 1.3.0 es-errors: 1.3.0
get-intrinsic: 1.2.6 get-intrinsic: 1.2.7
object-inspect: 1.13.3 object-inspect: 1.13.3
side-channel-map: 1.0.1 side-channel-map: 1.0.1
@@ -11218,6 +11180,12 @@ snapshots:
media-typer: 0.3.0 media-typer: 0.3.0
mime-types: 2.1.35 mime-types: 2.1.35
type-is@2.0.1:
dependencies:
content-type: 1.0.5
media-typer: 1.1.0
mime-types: 3.0.1
typed-array-length@1.0.4: typed-array-length@1.0.4:
dependencies: dependencies:
call-bind: 1.0.2 call-bind: 1.0.2
@@ -11311,8 +11279,6 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
utils-merge@1.0.1: {}
uuid@9.0.1: {} uuid@9.0.1: {}
validator@13.12.0: {} validator@13.12.0: {}