Migrate to express 5 (#7572)

Main changes:
- type checker cares about the return value (`void`) of handlers, so no
more `return res.send("...")` as that returns `express` type.
- regexes on slugs no longer supported, but we weren't really using them
in any meaningful way. The two places that had to be updated:
- `/clientstate/:clientstate64` - now uses a regex directly and tests added (thanks @partouf for spotting #4844)
- `/bits/:bits.html` - was some `\w+` but I believe that's unnecessary
for the same reasons
- actually call the Sentry handler. I don't know if this actually worked
before but the API checks suggest not.
This commit is contained in:
Matt Godbolt
2025-04-19 14:34:41 -05:00
committed by GitHub
parent 6400235a18
commit 20048616da
12 changed files with 318 additions and 331 deletions

12
app.ts
View File

@@ -47,6 +47,7 @@ import systemdSocket from 'systemd-socket';
import _ from 'underscore';
import urljoin from 'url-join';
import {unwrap} from './lib/assert.js';
import * as aws from './lib/aws.js';
import * as normalizer from './lib/clientstate-normalizer.js';
import {GoldenLayoutRootStruct} from './lib/clientstate-normalizer.js';
@@ -635,9 +636,10 @@ async function main() {
logger.debug('Compilers:', compilers);
prevCompilers = compilers;
await clientOptionsHandler.setCompilers(compilers);
routeApi.apiHandler.setCompilers(compilers);
routeApi.apiHandler.setLanguages(languages);
routeApi.apiHandler.setOptions(clientOptionsHandler);
const apiHandler = unwrap(routeApi.apiHandler);
apiHandler.setCompilers(compilers);
apiHandler.setLanguages(languages);
apiHandler.setOptions(clientOptionsHandler);
}
await onCompilerChange(initialCompilers);
@@ -687,7 +689,7 @@ async function main() {
next({status: 404, message: `page "${req.path}" could not be found`});
})
// sentry error handler must be the first error handling middleware
.use(Sentry.Handlers.errorHandler)
.use(Sentry.Handlers.errorHandler())
// eslint-disable-next-line no-unused-vars
.use((err: any, req: express.Request, res: express.Response, _next: express.NextFunction) => {
const status = err.status || err.statusCode || err.status_code || err.output?.statusCode || 500;
@@ -837,7 +839,7 @@ async function main() {
res.set('Content-Type', 'application/javascript');
res.end(`window.compilerExplorerOptions = ${clientOptionsHandler.getJSON()};`);
})
.use('/bits/:bits(\\w+).html', cached, csp, (req, res) => {
.use('/bits/:bits.html', cached, csp, (req, res) => {
res.render(
`bits/${sanitize(req.params.bits)}`,
renderConfig(

View File

@@ -43,8 +43,7 @@ import {StorageBase} from '../storage/index.js';
import {CompileHandler} from './compile.js';
function methodNotAllowed(req: express.Request, res: express.Response) {
res.send('Method Not Allowed');
return res.status(405).end();
res.status(405).send('Method Not Allowed').end();
}
export class ApiHandler {

View File

@@ -48,7 +48,8 @@ export class AssemblyDocumentationController implements HttpController {
const instruction = (req.params.opcode || '__UNKNOWN_OPCODE').toUpperCase();
const information = provider.getInstructionInformation(instruction);
if (information === null) {
return res.status(404).send({error: `Unknown opcode '${instruction}'`});
res.status(404).send({error: `Unknown opcode '${instruction}'`});
return;
}
const contentType = req.accepts(['text', 'json']);
@@ -67,7 +68,7 @@ export class AssemblyDocumentationController implements HttpController {
}
}
} catch {
return res.status(404).send({error: `No documentation for '${req.params.arch}'`});
res.status(404).send({error: `No documentation for '${req.params.arch}'`});
}
}
}

View File

@@ -44,7 +44,8 @@ export class FormattingController implements HttpController {
const formatter = this.formattingService.getFormatterById(req.params.tool);
// Ensure the target formatter exists
if (formatter === null) {
return res.status(422).json({exit: 2, answer: `Unknown format tool '${req.params.tool}'`});
res.status(422).json({exit: 2, answer: `Unknown format tool '${req.params.tool}'`});
return;
}
// Ensure there is source code to format
if (!req.body || !req.body.source) {
@@ -53,7 +54,8 @@ export class FormattingController implements HttpController {
}
// Ensure that the requested style is supported by the formatter
if (!formatter.isValidStyle(req.body.base)) {
return res.status(422).json({exit: 3, answer: `Style '${req.body.base}' is not supported`});
res.status(422).json({exit: 3, answer: `Style '${req.body.base}' is not supported`});
return;
}
// Do the formatting
try {

View File

@@ -62,7 +62,8 @@ export class HealthcheckController implements HttpController {
// If this is a worker, we don't require that the server has languages configured.
if (!this.isExecutionWorker && !this.compileHandler.hasLanguages()) {
logger.error('*** HEALTH CHECK FAILURE: no languages/compilers detected');
return res.status(500).send();
res.status(500).send();
return;
}
// If we have a healthcheck file, we require that it exists and it is non-empty. The /efs/.health file contents
@@ -74,14 +75,16 @@ export class HealthcheckController implements HttpController {
throw new Error('File is empty');
}
res.set('Content-Type', 'text/html');
return res.send(content);
res.send(content);
return;
} catch (e) {
logger.error(`*** HEALTH CHECK FAILURE: while reading file '${this.healthCheckFilePath}' got ${e}`);
SentryCapture(e, 'Health check');
return res.status(500).send();
res.status(500).send();
return;
}
}
return res.send('Everything is awesome');
res.send('Everything is awesome');
}
}

View File

@@ -486,7 +486,8 @@ export class CompileHandler implements ICompileHandler {
handlePopularArguments(req: express.Request, res: express.Response) {
const compiler = this.compilerFor(req);
if (!compiler) {
return res.sendStatus(404);
res.sendStatus(404);
return;
}
res.send(compiler.possibleArguments.getPopularArguments(this.getUsedOptions(req)));
}
@@ -494,7 +495,8 @@ export class CompileHandler implements ICompileHandler {
handleOptimizationArguments(req: express.Request, res: express.Response) {
const compiler = this.compilerFor(req);
if (!compiler) {
return res.sendStatus(404);
res.sendStatus(404);
return;
}
res.send(compiler.possibleArguments.getOptimizationArguments(this.getUsedOptions(req)));
}
@@ -513,18 +515,20 @@ export class CompileHandler implements ICompileHandler {
handleApiError(error: any, res: express.Response, next: express.NextFunction) {
if (error.message) {
return res.status(400).send({
res.status(400).send({
error: true,
message: error.message,
});
return;
}
return next(error);
next(error);
}
handleCmake(req: express.Request, res: express.Response, next: express.NextFunction) {
const compiler = this.compilerFor(req);
if (!compiler) {
return res.sendStatus(404);
res.sendStatus(404);
return;
}
const remote = compiler.getRemote();
@@ -561,14 +565,16 @@ export class CompileHandler implements ICompileHandler {
return this.handleApiError(e, res, next);
});
} catch (e) {
return this.handleApiError(e, res, next);
this.handleApiError(e, res, next);
return;
}
}
handle(req: express.Request, res: express.Response, next: express.NextFunction) {
const compiler = this.compilerFor(req);
if (!compiler) {
return res.sendStatus(404);
res.sendStatus(404);
return;
}
const remote = compiler.getRemote();
@@ -585,7 +591,8 @@ export class CompileHandler implements ICompileHandler {
try {
parsedRequest = this.parseRequest(req, compiler);
} catch (error) {
return this.handleApiError(error, res, next);
this.handleApiError(error, res, next);
return;
}
const {source, options, backendOptions, filters, bypassCache, tools, executeParameters, libraries} =

View File

@@ -35,9 +35,10 @@ const ceProps = props.propsFor('compiler-explorer');
*/
export const jsonOnly: express.Handler = (req, res, next) => {
if (req.headers['content-type'] !== 'application/json') {
return res.status(400).json({message: 'bad request, expected json content'});
res.status(400).json({message: 'bad request, expected json content'});
return;
}
return next();
next();
};
/** Add static headers to the response */

View File

@@ -29,7 +29,7 @@ import express from 'express';
import {AppDefaultArguments, CompilerExplorerOptions} from '../../app.js';
import {isString} from '../../shared/common-utils.js';
import {Language} from '../../types/languages.interfaces.js';
import {assert} from '../assert.js';
import {assert, unwrap} from '../assert.js';
import {ClientStateGoldenifier, ClientStateNormalizer} from '../clientstate-normalizer.js';
import {ClientState} from '../clientstate.js';
import {CompilationEnvironment} from '../compilation-env.js';
@@ -67,7 +67,7 @@ export type ShortLinkMetaData = {
export class RouteAPI {
renderGoldenLayout: any;
storageHandler: StorageBase;
apiHandler: ApiHandler;
apiHandler: ApiHandler | undefined;
constructor(
private readonly router: express.Router,
@@ -89,17 +89,19 @@ export class RouteAPI {
this.apiHandler.setReleaseInfo(config.defArgs.gitReleaseName, config.defArgs.releaseBuildNumber);
} else {
this.apiHandler = undefined as any;
this.apiHandler = undefined;
}
}
InitializeRoutes() {
if (this.apiHandler) {
this.router.use('/api', this.apiHandler.handle);
}
this.router
.use('/api', this.apiHandler.handle)
.get('/z/:id', cached, csp, this.storedStateHandler.bind(this))
.get('/z/:id/code/:session', cached, csp, this.storedCodeHandler.bind(this))
.get('/resetlayout/:id', cached, csp, this.storedStateHandlerResetLayout.bind(this))
.get('/clientstate/:clientstatebase64([^]*)', cached, csp, this.unstoredStateHandler.bind(this))
.get(/^\/clientstate\/(?<clientstatebase64>.*)/, cached, csp, this.unstoredStateHandler.bind(this))
.get('/fromsimplelayout', cached, csp, this.simpleLayoutHandler.bind(this));
}
@@ -111,7 +113,7 @@ export class RouteAPI {
.then(result => {
const config = JSON.parse(result.config);
let clientstate: ClientState | null = null;
let clientstate: ClientState | null;
if (config.content) {
const normalizer = new ClientStateNormalizer();
normalizer.fromGoldenLayout(config);
@@ -268,7 +270,7 @@ export class RouteAPI {
const sources = utils.glGetMainContents(config.content);
if (sources.editors.length === 1) {
const editor = sources.editors[0];
lang = this.apiHandler.languages[editor.language];
lang = unwrap(this.apiHandler).languages[editor.language];
source = editor.source;
} else {
const normalizer = new ClientStateNormalizer();
@@ -277,7 +279,7 @@ export class RouteAPI {
if (clientstate.trees && clientstate.trees.length === 1) {
const tree = clientstate.trees[0];
lang = this.apiHandler.languages[tree.compilerLanguageId];
lang = unwrap(this.apiHandler).languages[tree.compilerLanguageId];
if (tree.isCMakeProject) {
const firstSource = tree.files.find(file => {
@@ -304,7 +306,7 @@ export class RouteAPI {
metadata.ogTitle += ` - ${lang.name}`;
if (sources && sources.compilers.length === 1) {
const compilerId = sources.compilers[0].compiler;
const compiler = this.apiHandler.compilers.find(c => c.id === compilerId);
const compiler = unwrap(this.apiHandler).compilers.find(c => c.id === compilerId);
if (compiler) {
metadata.ogTitle += ` (${compiler.name})`;
}

496
package-lock.json generated
View File

@@ -31,7 +31,7 @@
"cross-env": "^7.0.3",
"enhanced-ms": "^4.1.0",
"events": "^3.3.0",
"express": "^4.21.2",
"express": "^5.1.0",
"file-saver": "^2.0.5",
"golden-layout": "^1.5.9",
"http-proxy": "^1.18.1",
@@ -82,7 +82,7 @@
"@types/bootstrap": "^5.2.10",
"@types/chai": "^5.2.1",
"@types/compression": "^1.7.5",
"@types/express": "^4.17.21",
"@types/express": "^5.0.1",
"@types/file-saver": "^2.0.7",
"@types/http-proxy": "^1.17.16",
"@types/jquery": "^3.5.32",
@@ -4593,22 +4593,21 @@
"license": "MIT"
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/express-serve-static-core": "^5.0.0",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.19.6",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5303,22 +5302,34 @@
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -5498,12 +5509,6 @@
],
"license": "MIT"
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
@@ -5741,69 +5746,23 @@
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/body-parser/node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
"node": ">=18"
}
},
"node_modules/boolbase": {
@@ -6508,9 +6467,9 @@
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
@@ -6538,10 +6497,13 @@
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"license": "MIT",
"engines": {
"node": ">=6.6.0"
}
},
"node_modules/cookiejar": {
"version": "2.1.4",
@@ -7040,7 +7002,6 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -7095,16 +7056,6 @@
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@@ -7624,79 +7575,66 @@
}
},
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"engines": {
"node": ">= 0.10.0"
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"node_modules/express/node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
"engines": {
"node": ">= 0.8"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"node_modules/express/node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"side-channel": "^1.0.6"
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
"node": ">= 0.6"
}
},
"node_modules/extend": {
@@ -7900,38 +7838,22 @@
}
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -8546,7 +8468,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -9997,12 +9918,12 @@
"license": "CC0-1.0"
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/memfs": {
@@ -10026,10 +9947,13 @@
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
@@ -10044,6 +9968,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@@ -10063,18 +9988,6 @@
"node": ">=8.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
@@ -10351,16 +10264,6 @@
"@sinonjs/commons": "^3.0.1"
}
},
"node_modules/nise/node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/nock": {
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/nock/-/nock-14.0.3.tgz",
@@ -10771,10 +10674,13 @@
"license": "ISC"
},
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
"license": "MIT",
"engines": {
"node": ">=16"
}
},
"node_modules/pathe": {
"version": "2.0.3",
@@ -11754,7 +11660,6 @@
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -11785,32 +11690,20 @@
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"iconv-lite": "0.6.3",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
@@ -12118,6 +12011,28 @@
"fsevents": "~2.3.2"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0",
"parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/router/node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
"node_modules/rrweb-cssom": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
@@ -12335,53 +12250,48 @@
}
},
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
"debug": "^4.3.5",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"mime-types": "^3.0.1",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 0.8.0"
"node": ">= 18"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"node_modules/send/node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -12420,18 +12330,18 @@
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"parseurl": "^1.3.3",
"send": "^1.2.0"
},
"engines": {
"node": ">= 0.8.0"
"node": ">= 18"
}
},
"node_modules/setimmediate": {
@@ -13615,13 +13525,26 @@
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/type-is/node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">= 0.6"
@@ -13758,15 +13681,6 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",

View File

@@ -40,7 +40,7 @@
"cross-env": "^7.0.3",
"enhanced-ms": "^4.1.0",
"events": "^3.3.0",
"express": "^4.21.2",
"express": "^5.1.0",
"file-saver": "^2.0.5",
"golden-layout": "^1.5.9",
"http-proxy": "^1.18.1",
@@ -91,7 +91,7 @@
"@types/bootstrap": "^5.2.10",
"@types/chai": "^5.2.1",
"@types/compression": "^1.7.5",
"@types/express": "^4.17.21",
"@types/express": "^5.0.1",
"@types/file-saver": "^2.0.7",
"@types/http-proxy": "^1.17.16",
"@types/jquery": "^3.5.32",

View File

@@ -31,7 +31,9 @@ import {cached, cors} from '../../lib/handlers/middleware.js';
describe('middleware functions', () => {
it('adds cache controls headers with cached()', async () => {
const app = express();
app.use('/', cached, (_, res) => res.send('Hello World!'));
app.use('/', cached, (_, res) => {
res.send('Hello World!');
});
await request(app)
.get('/')
.expect(200, 'Hello World!')
@@ -40,7 +42,9 @@ describe('middleware functions', () => {
it('adds cors headers with cors()', async () => {
const app = express();
app.use('/', cors, (_, res) => res.send('Hello World!'));
app.use('/', cors, (_, res) => {
res.send('Hello World!');
});
await request(app)
.get('/')
.expect(200, 'Hello World!')

View File

@@ -24,9 +24,17 @@
import zlib from 'node:zlib';
import {describe, expect, it} from 'vitest';
import {beforeAll, describe, expect, it} from 'vitest';
import {extractJsonFromBufferAndInflateIfRequired} from '../../lib/handlers/route-api.js';
import express from 'express';
import request from 'supertest';
import {GoldenLayoutRootStruct} from '../../lib/clientstate-normalizer.js';
import {
HandlerConfig,
RouteAPI,
ShortLinkMetaData,
extractJsonFromBufferAndInflateIfRequired,
} from '../../lib/handlers/route-api.js';
function possibleCompression(buffer: Buffer): boolean {
// code used in extractJsonFromBufferAndInflateIfRequired
@@ -63,3 +71,47 @@ describe('extractJsonFromBufferAndInflateIfRequired test cases', () => {
expect(() => extractJsonFromBufferAndInflateIfRequired(buffer)).toThrow();
});
});
describe('clientStateHandler', () => {
let app: express.Express;
beforeAll(() => {
app = express();
const apiHandler = new RouteAPI(app, {
renderGoldenLayout: (
config: GoldenLayoutRootStruct,
metadata: ShortLinkMetaData,
req: express.Request,
res: express.Response,
) => {
res.send('This is ok');
},
} as unknown as HandlerConfig);
apiHandler.InitializeRoutes();
});
it('should return 200 for /clientstate', async () => {
// A valid document is a base64 encoded JSON object with a sessions array.
const document = Buffer.from(JSON.stringify({sessions: []}), 'utf-8').toString('base64');
const response = await request(app).get(`/clientstate/${document}`);
expect(response.status).toBe(200);
});
it('should return 200 for /clientstate even if the base64 contains `/`', async () => {
let document = '';
for (let i = 0; i < 1000; i++) {
document = Buffer.from(
JSON.stringify({
sessions: [],
magic: String.fromCharCode(i, i + 1234),
}),
'utf-8',
).toString('base64');
if (document.includes('/')) {
break;
}
}
expect(document).toContain('/');
const response = await request(app).get(`/clientstate/${document}`);
expect(response.status).toBe(200);
});
});