introduce clustering

deepsource-autofix-76c6eb20
Dan Gowans 2023-02-17 11:25:57 -05:00
parent 90c107b0f3
commit 96ffa90546
22 changed files with 469 additions and 246 deletions

2
app.js
View File

@ -30,7 +30,7 @@ import { apiGetHandler } from './handlers/permissions.js';
import { getSafeRedirectURL } from './helpers/functions.authentication.js'; import { getSafeRedirectURL } from './helpers/functions.authentication.js';
import debug from 'debug'; import debug from 'debug';
import { useTestDatabases } from './data/databasePaths.js'; import { useTestDatabases } from './data/databasePaths.js';
const debugApp = debug('lot-occupancy-system:app'); const debugApp = debug(`lot-occupancy-system:app:${process.pid}`);
databaseInitializer.initializeDatabase(); databaseInitializer.initializeDatabase();
const _dirname = '.'; const _dirname = '.';
export const app = express(); export const app = express();

3
app.ts
View File

@ -39,7 +39,8 @@ import { getSafeRedirectURL } from './helpers/functions.authentication.js'
import debug from 'debug' import debug from 'debug'
import { useTestDatabases } from './data/databasePaths.js' import { useTestDatabases } from './data/databasePaths.js'
const debugApp = debug('lot-occupancy-system:app')
const debugApp = debug(`lot-occupancy-system:app:${process.pid}`)
/* /*
* INITIALIZE THE DATABASE * INITIALIZE THE DATABASE

View File

@ -1,48 +1,28 @@
import { app } from '../app.js'; import cluster from 'node:cluster';
import http from 'node:http'; import os from 'node:os';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import * as configFunctions from '../helpers/functions.config.js'; import * as configFunctions from '../helpers/functions.config.js';
import exitHook from 'exit-hook'; import exitHook from 'exit-hook';
import ntfyPublish from '@cityssm/ntfy-publish'; import ntfyPublish from '@cityssm/ntfy-publish';
import Debug from 'debug'; import Debug from 'debug';
const debug = Debug('lot-occupancy-system:www'); const debug = Debug('lot-occupancy-system:www');
function onError(error) { const directoryName = dirname(fileURLToPath(import.meta.url));
if (error.syscall !== 'listen') { const processCount = Math.min(configFunctions.getProperty('application.maximumProcesses'), os.cpus().length);
throw error; debug(`Primary pid: ${process.pid}`);
} debug(`Launching ${processCount} processes`);
switch (error.code) { cluster.setupPrimary({
case 'EACCES': { exec: directoryName + '/wwwProcess.js'
debug('Requires elevated privileges'); });
process.exit(1); for (let index = 0; index < processCount; index += 1) {
} cluster.fork();
case 'EADDRINUSE': {
debug('Port is already in use.');
process.exit(1);
}
default: {
throw error;
}
}
}
function onListening(server) {
const addr = server.address();
if (addr !== null) {
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port.toString();
debug('Listening on ' + bind);
}
} }
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid.toString()} has been killed`);
console.log('Starting another worker');
cluster.fork();
});
const ntfyStartupConfig = configFunctions.getProperty('application.ntfyStartup'); const ntfyStartupConfig = configFunctions.getProperty('application.ntfyStartup');
const httpPort = configFunctions.getProperty('application.httpPort');
const httpServer = http.createServer(app);
httpServer.listen(httpPort);
httpServer.on('error', onError);
httpServer.on('listening', () => {
onListening(httpServer);
});
debug('HTTP listening on ' + httpPort.toString());
exitHook(() => {
debug('Closing HTTP');
httpServer.close();
});
if (ntfyStartupConfig) { if (ntfyStartupConfig) {
const topic = ntfyStartupConfig.topic; const topic = ntfyStartupConfig.topic;
const server = ntfyStartupConfig.server; const server = ntfyStartupConfig.server;

View File

@ -1,8 +1,7 @@
/* eslint-disable no-process-exit, unicorn/no-process-exit */ import cluster from 'node:cluster'
import os from 'node:os'
import { app } from '../app.js' import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import http from 'node:http'
import * as configFunctions from '../helpers/functions.config.js' import * as configFunctions from '../helpers/functions.config.js'
@ -14,73 +13,32 @@ import type * as ntfyTypes from '@cityssm/ntfy-publish/types'
import Debug from 'debug' import Debug from 'debug'
const debug = Debug('lot-occupancy-system:www') const debug = Debug('lot-occupancy-system:www')
interface ServerError extends Error { const directoryName = dirname(fileURLToPath(import.meta.url))
syscall: string
code: string const processCount = Math.min(
configFunctions.getProperty('application.maximumProcesses'),
os.cpus().length
)
debug(`Primary pid: ${process.pid}`)
debug(`Launching ${processCount} processes`)
cluster.setupPrimary({
exec: directoryName + '/wwwProcess.js'
})
for (let index = 0; index < processCount; index += 1) {
cluster.fork()
} }
function onError(error: ServerError): void { cluster.on('exit', (worker, code, signal) => {
if (error.syscall !== 'listen') { console.log(`Worker ${worker.process.pid!.toString()} has been killed`)
throw error console.log('Starting another worker')
} cluster.fork()
})
// handle specific listen errors with friendly messages
switch (error.code) {
// eslint-disable-next-line no-fallthrough
case 'EACCES': {
debug('Requires elevated privileges')
process.exit(1)
// break;
}
// eslint-disable-next-line no-fallthrough
case 'EADDRINUSE': {
debug('Port is already in use.')
process.exit(1)
// break;
}
// eslint-disable-next-line no-fallthrough
default: {
throw error
}
}
}
function onListening(server: http.Server): void {
const addr = server.address()
if (addr !== null) {
const bind =
typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port.toString()
debug('Listening on ' + bind)
}
}
/*
* Initialize HTTP
*/
const ntfyStartupConfig = configFunctions.getProperty('application.ntfyStartup') const ntfyStartupConfig = configFunctions.getProperty('application.ntfyStartup')
const httpPort = configFunctions.getProperty('application.httpPort')
const httpServer = http.createServer(app)
httpServer.listen(httpPort)
httpServer.on('error', onError)
httpServer.on('listening', () => {
onListening(httpServer)
})
debug('HTTP listening on ' + httpPort.toString())
exitHook(() => {
debug('Closing HTTP')
httpServer.close()
})
if (ntfyStartupConfig) { if (ntfyStartupConfig) {
const topic = ntfyStartupConfig.topic const topic = ntfyStartupConfig.topic
const server = ntfyStartupConfig.server const server = ntfyStartupConfig.server

1
bin/wwwProcess.d.ts vendored 100644
View File

@ -0,0 +1 @@
export {};

42
bin/wwwProcess.js 100644
View File

@ -0,0 +1,42 @@
import { app } from '../app.js';
import http from 'node:http';
import * as configFunctions from '../helpers/functions.config.js';
import exitHook from 'exit-hook';
import Debug from 'debug';
const debug = Debug(`lot-occupancy-system:wwwProcess:${process.pid}`);
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
switch (error.code) {
case 'EACCES': {
debug('Requires elevated privileges');
process.exit(1);
}
case 'EADDRINUSE': {
debug('Port is already in use.');
process.exit(1);
}
default: {
throw error;
}
}
}
function onListening(server) {
const addr = server.address();
if (addr !== null) {
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port.toString();
debug('HTTP Listening on ' + bind);
}
}
const httpPort = configFunctions.getProperty('application.httpPort');
const httpServer = http.createServer(app);
httpServer.listen(httpPort);
httpServer.on('error', onError);
httpServer.on('listening', () => {
onListening(httpServer);
});
exitHook(() => {
debug('Closing HTTP');
httpServer.close();
});

75
bin/wwwProcess.ts 100644
View File

@ -0,0 +1,75 @@
/* eslint-disable no-process-exit, unicorn/no-process-exit */
import { app } from '../app.js'
import http from 'node:http'
import * as configFunctions from '../helpers/functions.config.js'
import exitHook from 'exit-hook'
import Debug from 'debug'
const debug = Debug(`lot-occupancy-system:wwwProcess:${process.pid}`)
interface ServerError extends Error {
syscall: string
code: string
}
function onError(error: ServerError): void {
if (error.syscall !== 'listen') {
throw error
}
// handle specific listen errors with friendly messages
switch (error.code) {
// eslint-disable-next-line no-fallthrough
case 'EACCES': {
debug('Requires elevated privileges')
process.exit(1)
// break;
}
// eslint-disable-next-line no-fallthrough
case 'EADDRINUSE': {
debug('Port is already in use.')
process.exit(1)
// break;
}
// eslint-disable-next-line no-fallthrough
default: {
throw error
}
}
}
function onListening(server: http.Server): void {
const addr = server.address()
if (addr !== null) {
const bind =
typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port.toString()
debug('HTTP Listening on ' + bind)
}
}
/*
* Initialize HTTP
*/
const httpPort = configFunctions.getProperty('application.httpPort')
const httpServer = http.createServer(app)
httpServer.listen(httpPort)
httpServer.on('error', onError)
httpServer.on('listening', () => {
onListening(httpServer)
})
exitHook(() => {
debug('Closing HTTP')
httpServer.close()
})

View File

@ -6,6 +6,9 @@ import { getOccupancyTypes as getOccupancyTypesFromDatabase } from './lotOccupan
import { getOccupancyTypeFields as getOccupancyTypeFieldsFromDatabase } from './lotOccupancyDB/getOccupancyTypeFields.js'; import { getOccupancyTypeFields as getOccupancyTypeFieldsFromDatabase } from './lotOccupancyDB/getOccupancyTypeFields.js';
import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupancyDB/getWorkOrderTypes.js'; import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupancyDB/getWorkOrderTypes.js';
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js'; import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js';
import { getConfigTableMaxTimeMillis } from './lotOccupancyDB/getConfigTableMaxTimeMillis.js';
import { setIntervalAsync, clearIntervalAsync } from 'set-interval-async';
import { asyncExitHook } from 'exit-hook';
let lotOccupantTypes; let lotOccupantTypes;
export async function getLotOccupantTypes() { export async function getLotOccupantTypes() {
if (lotOccupantTypes === undefined) { if (lotOccupantTypes === undefined) {
@ -191,3 +194,25 @@ export function clearCacheByTableName(tableName) {
} }
} }
} }
function clearAllCaches() {
clearLotOccupantTypesCache();
clearLotStatusesCache();
clearLotTypesCache();
clearOccupancyTypesCache();
clearWorkOrderMilestoneTypesCache();
clearWorkOrderTypesCache();
}
let configTimeMillis = 0;
async function checkCacheIntegrity() {
const timeMillis = await getConfigTableMaxTimeMillis();
if (timeMillis > configTimeMillis) {
configTimeMillis = timeMillis;
clearAllCaches();
}
}
const cacheTimer = setIntervalAsync(checkCacheIntegrity, 10 * 60 * 1000);
asyncExitHook(async () => {
await clearIntervalAsync(cacheTimer);
}, {
minimumWait: 250
});

View File

@ -15,6 +15,11 @@ import { getWorkOrderTypes as getWorkOrderTypesFromDatabase } from './lotOccupan
import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js' import { getWorkOrderMilestoneTypes as getWorkOrderMilestoneTypesFromDatabase } from './lotOccupancyDB/getWorkOrderMilestoneTypes.js'
import { getConfigTableMaxTimeMillis } from './lotOccupancyDB/getConfigTableMaxTimeMillis.js'
import { setIntervalAsync, clearIntervalAsync } from 'set-interval-async'
import { asyncExitHook } from 'exit-hook'
import type * as recordTypes from '../types/recordTypes' import type * as recordTypes from '../types/recordTypes'
/* /*
@ -332,3 +337,38 @@ export function clearCacheByTableName(tableName: string): void {
} }
} }
} }
function clearAllCaches(): void {
clearLotOccupantTypesCache()
clearLotStatusesCache()
clearLotTypesCache()
clearOccupancyTypesCache()
clearWorkOrderMilestoneTypesCache()
clearWorkOrderTypesCache()
}
/*
* Config Time Millis
*/
let configTimeMillis = 0
async function checkCacheIntegrity(): Promise<void> {
const timeMillis = await getConfigTableMaxTimeMillis()
if (timeMillis > configTimeMillis) {
configTimeMillis = timeMillis
clearAllCaches()
}
}
const cacheTimer = setIntervalAsync(checkCacheIntegrity, 10 * 60 * 1000)
asyncExitHook(
async () => {
await clearIntervalAsync(cacheTimer)
},
{
minimumWait: 250
}
)

View File

@ -7,6 +7,7 @@ export declare function getProperty(propertyName: 'application.userDomain'): str
export declare function getProperty(propertyName: 'application.useTestDatabases'): boolean; export declare function getProperty(propertyName: 'application.useTestDatabases'): boolean;
export declare function getProperty(propertyName: 'application.ntfyStartup'): configTypes.ConfigNtfyStartup | undefined; export declare function getProperty(propertyName: 'application.ntfyStartup'): configTypes.ConfigNtfyStartup | undefined;
export declare function getProperty(propertyName: 'activeDirectory'): configTypes.ConfigActiveDirectory; export declare function getProperty(propertyName: 'activeDirectory'): configTypes.ConfigActiveDirectory;
export declare function getProperty(propertyName: 'application.maximumProcesses'): number;
export declare function getProperty(propertyName: 'users.testing'): string[]; export declare function getProperty(propertyName: 'users.testing'): string[];
export declare function getProperty(propertyName: 'users.canLogin'): string[]; export declare function getProperty(propertyName: 'users.canLogin'): string[];
export declare function getProperty(propertyName: 'users.canUpdate'): string[]; export declare function getProperty(propertyName: 'users.canUpdate'): string[];

View File

@ -6,6 +6,7 @@ configFallbackValues.set('application.backgroundURL', '/images/cemetery-backgrou
configFallbackValues.set('application.logoURL', '/images/cemetery-logo.png'); configFallbackValues.set('application.logoURL', '/images/cemetery-logo.png');
configFallbackValues.set('application.httpPort', 7000); configFallbackValues.set('application.httpPort', 7000);
configFallbackValues.set('application.useTestDatabases', false); configFallbackValues.set('application.useTestDatabases', false);
configFallbackValues.set('application.maximumProcesses', 4);
configFallbackValues.set('reverseProxy.disableCompression', false); configFallbackValues.set('reverseProxy.disableCompression', false);
configFallbackValues.set('reverseProxy.disableEtag', false); configFallbackValues.set('reverseProxy.disableEtag', false);
configFallbackValues.set('reverseProxy.urlPrefix', ''); configFallbackValues.set('reverseProxy.urlPrefix', '');

View File

@ -20,6 +20,7 @@ configFallbackValues.set(
configFallbackValues.set('application.logoURL', '/images/cemetery-logo.png') configFallbackValues.set('application.logoURL', '/images/cemetery-logo.png')
configFallbackValues.set('application.httpPort', 7000) configFallbackValues.set('application.httpPort', 7000)
configFallbackValues.set('application.useTestDatabases', false) configFallbackValues.set('application.useTestDatabases', false)
configFallbackValues.set('application.maximumProcesses', 4)
configFallbackValues.set('reverseProxy.disableCompression', false) configFallbackValues.set('reverseProxy.disableCompression', false)
configFallbackValues.set('reverseProxy.disableEtag', false) configFallbackValues.set('reverseProxy.disableEtag', false)
@ -113,6 +114,10 @@ export function getProperty(
propertyName: 'activeDirectory' propertyName: 'activeDirectory'
): configTypes.ConfigActiveDirectory ): configTypes.ConfigActiveDirectory
export function getProperty(
propertyName: 'application.maximumProcesses'
): number
export function getProperty(propertyName: 'users.testing'): string[] export function getProperty(propertyName: 'users.testing'): string[]
export function getProperty(propertyName: 'users.canLogin'): string[] export function getProperty(propertyName: 'users.canLogin'): string[]
export function getProperty(propertyName: 'users.canUpdate'): string[] export function getProperty(propertyName: 'users.canUpdate'): string[]

View File

@ -0,0 +1,2 @@
export declare function getConfigTableMaxTimeMillis(): Promise<number>;
export default getConfigTableMaxTimeMillis;

View File

@ -0,0 +1,34 @@
import { acquireConnection } from './pool.js';
export async function getConfigTableMaxTimeMillis() {
const database = await acquireConnection();
const result = database
.prepare(`select max(timeMillis) as timeMillis from (
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotOccupantTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotStatuses
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypeFields
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypePrints
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from WorkOrderTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from WorkOrderMilestoneTypes
)`)
.get();
database.release();
return result?.timeMillis ?? 0;
}
export default getConfigTableMaxTimeMillis;

View File

@ -0,0 +1,41 @@
import { acquireConnection } from './pool.js'
export async function getConfigTableMaxTimeMillis(): Promise<number> {
const database = await acquireConnection()
const result = database
.prepare(
`select max(timeMillis) as timeMillis from (
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotOccupantTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotStatuses
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from LotTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypeFields
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from OccupancyTypePrints
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from WorkOrderTypes
UNION
select max(max(recordUpdate_timeMillis, ifnull(recordDelete_timeMillis,0))) as timeMillis
from WorkOrderMilestoneTypes
)`
)
.get()
database.release()
return result?.timeMillis ?? 0
}
export default getConfigTableMaxTimeMillis

266
package-lock.json generated
View File

@ -18,7 +18,7 @@
"@fortawesome/fontawesome-free": "^5.15.4", "@fortawesome/fontawesome-free": "^5.15.4",
"activedirectory2": "^2.1.0", "activedirectory2": "^2.1.0",
"better-sqlite-pool": "^0.3.1", "better-sqlite-pool": "^0.3.1",
"better-sqlite3": "^8.0.1", "better-sqlite3": "^8.1.0",
"bulma-calendar": "^6.1.18", "bulma-calendar": "^6.1.18",
"camelcase": "^7.0.1", "camelcase": "^7.0.1",
"compression": "^1.7.4", "compression": "^1.7.4",
@ -40,6 +40,7 @@
"papaparse": "^5.3.2", "papaparse": "^5.3.2",
"randomcolor": "^0.6.2", "randomcolor": "^0.6.2",
"session-file-store": "^1.5.0", "session-file-store": "^1.5.0",
"set-interval-async": "^3.0.3",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -55,9 +56,9 @@
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/csurf": "^1.11.2", "@types/csurf": "^1.11.2",
"@types/debug": "^4.1.7", "@types/debug": "^4.1.7",
"@types/ejs": "^3.1.1", "@types/ejs": "^3.1.2",
"@types/express": "^4.17.16", "@types/express": "^4.17.17",
"@types/express-session": "^1.17.5", "@types/express-session": "^1.17.6",
"@types/gulp": "^4.0.10", "@types/gulp": "^4.0.10",
"@types/gulp-changed": "^0.0.35", "@types/gulp-changed": "^0.0.35",
"@types/gulp-minify": "^3.1.1", "@types/gulp-minify": "^3.1.1",
@ -70,8 +71,8 @@
"@types/randomcolor": "^0.5.7", "@types/randomcolor": "^0.5.7",
"@types/session-file-store": "^1.2.2", "@types/session-file-store": "^1.2.2",
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.50.0", "@typescript-eslint/eslint-plugin": "^5.52.0",
"@typescript-eslint/parser": "^5.50.0", "@typescript-eslint/parser": "^5.52.0",
"axe-core": "^4.6.3", "axe-core": "^4.6.3",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"bulma-divider": "^0.2.0", "bulma-divider": "^0.2.0",
@ -81,7 +82,7 @@
"bulma-tooltip": "^3.0.2", "bulma-tooltip": "^3.0.2",
"cypress": "^12.5.1", "cypress": "^12.5.1",
"cypress-axe": "^1.3.0", "cypress-axe": "^1.3.0",
"eslint": "^8.33.0", "eslint": "^8.34.0",
"eslint-config-standard-with-typescript": "^34.0.0", "eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
@ -93,7 +94,7 @@
"gulp-minify": "^3.1.0", "gulp-minify": "^3.1.0",
"gulp-sass": "^5.1.0", "gulp-sass": "^5.1.0",
"nodemon": "^2.0.20", "nodemon": "^2.0.20",
"sass": "^1.58.0" "sass": "^1.58.1"
}, },
"engines": { "engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0" "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
@ -941,9 +942,9 @@
} }
}, },
"node_modules/@types/ejs": { "node_modules/@types/ejs": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz",
"integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", "integrity": "sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==",
"dev": true "dev": true
}, },
"node_modules/@types/expect": { "node_modules/@types/expect": {
@ -953,13 +954,13 @@
"dev": true "dev": true
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.16", "version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==", "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.31", "@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*", "@types/qs": "*",
"@types/serve-static": "*" "@types/serve-static": "*"
} }
@ -976,9 +977,9 @@
} }
}, },
"node_modules/@types/express-session": { "node_modules/@types/express-session": {
"version": "1.17.5", "version": "1.17.6",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.6.tgz",
"integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", "integrity": "sha512-L6sB04HVA4HEZo1hDL65JXdZdBJtzZnCiw/P7MnO4w6746tJCNtXlHtzEASyI9ccn9zyOw6IbqQuhVa03VpO4w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/express": "*" "@types/express": "*"
@ -1279,14 +1280,14 @@
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==", "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/type-utils": "5.50.0", "@typescript-eslint/type-utils": "5.52.0",
"@typescript-eslint/utils": "5.50.0", "@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"grapheme-splitter": "^1.0.4", "grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0", "ignore": "^5.2.0",
@ -1313,14 +1314,14 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==", "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -1340,13 +1341,13 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
"integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==", "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.50.0" "@typescript-eslint/visitor-keys": "5.52.0"
}, },
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -1357,13 +1358,13 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
"integrity": "sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ==", "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"@typescript-eslint/utils": "5.50.0", "@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"tsutils": "^3.21.0" "tsutils": "^3.21.0"
}, },
@ -1384,9 +1385,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
"integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==", "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -1397,13 +1398,13 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
"integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==", "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.50.0", "@typescript-eslint/visitor-keys": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"globby": "^11.1.0", "globby": "^11.1.0",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -1424,16 +1425,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
"integrity": "sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw==", "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12", "@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0", "eslint-utils": "^3.0.0",
"semver": "^7.3.7" "semver": "^7.3.7"
@ -1450,12 +1451,12 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
"integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==", "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"eslint-visitor-keys": "^3.3.0" "eslint-visitor-keys": "^3.3.0"
}, },
"engines": { "engines": {
@ -2193,9 +2194,9 @@
} }
}, },
"node_modules/better-sqlite3": { "node_modules/better-sqlite3": {
"version": "8.0.1", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.0.1.tgz", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.1.0.tgz",
"integrity": "sha512-JhTZjpyapA1icCEjIZB4TSSgkGdFgpWZA2Wszg7Cf4JwJwKQmbvuNnJBeR+EYG/Z29OXvR4G//Rbg31BW/Z7Yg==", "integrity": "sha512-p1m09H+Oi8R9TPj810pdNswMFuVgRNgCJEWypp6jlkOgSwMIrNyuj3hW78xEuBRGok5RzeaUW8aBtTWF3l/TQA==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
@ -3948,9 +3949,9 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "8.33.0", "version": "8.34.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint/eslintrc": "^1.4.1", "@eslint/eslintrc": "^1.4.1",
@ -10310,9 +10311,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.58.0", "version": "1.58.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz",
"integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",
@ -10458,6 +10459,14 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true "dev": true
}, },
"node_modules/set-interval-async": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/set-interval-async/-/set-interval-async-3.0.3.tgz",
"integrity": "sha512-o4DyBv6mko+A9cH3QKek4SAAT5UyJRkfdTi6JHii6ZCKUYFun8SwgBmQrOXd158JOwBQzA+BnO8BvT64xuCaSw==",
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/set-value": { "node_modules/set-value": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@ -13266,9 +13275,9 @@
} }
}, },
"@types/ejs": { "@types/ejs": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz",
"integrity": "sha512-RQul5wEfY7BjWm0sYY86cmUN/pcXWGyVxWX93DFFJvcrxax5zKlieLwA3T77xJGwNcZW0YW6CYG70p1m8xPFmA==", "integrity": "sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==",
"dev": true "dev": true
}, },
"@types/expect": { "@types/expect": {
@ -13278,13 +13287,13 @@
"dev": true "dev": true
}, },
"@types/express": { "@types/express": {
"version": "4.17.16", "version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.16.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-LkKpqRZ7zqXJuvoELakaFYuETHjZkSol8EV6cNnyishutDBCCdv6+dsKPbKkCcIk57qRphOLY5sEgClw1bO3gA==", "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.31", "@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*", "@types/qs": "*",
"@types/serve-static": "*" "@types/serve-static": "*"
} }
@ -13301,9 +13310,9 @@
} }
}, },
"@types/express-session": { "@types/express-session": {
"version": "1.17.5", "version": "1.17.6",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz", "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.6.tgz",
"integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==", "integrity": "sha512-L6sB04HVA4HEZo1hDL65JXdZdBJtzZnCiw/P7MnO4w6746tJCNtXlHtzEASyI9ccn9zyOw6IbqQuhVa03VpO4w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/express": "*" "@types/express": "*"
@ -13604,14 +13613,14 @@
} }
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz",
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==", "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/type-utils": "5.50.0", "@typescript-eslint/type-utils": "5.52.0",
"@typescript-eslint/utils": "5.50.0", "@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"grapheme-splitter": "^1.0.4", "grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0", "ignore": "^5.2.0",
@ -13622,53 +13631,53 @@
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz",
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==", "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"debug": "^4.3.4" "debug": "^4.3.4"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz",
"integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==", "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.50.0" "@typescript-eslint/visitor-keys": "5.52.0"
} }
}, },
"@typescript-eslint/type-utils": { "@typescript-eslint/type-utils": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz",
"integrity": "sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ==", "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"@typescript-eslint/utils": "5.50.0", "@typescript-eslint/utils": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"tsutils": "^3.21.0" "tsutils": "^3.21.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz",
"integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==", "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz",
"integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==", "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/visitor-keys": "5.50.0", "@typescript-eslint/visitor-keys": "5.52.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"globby": "^11.1.0", "globby": "^11.1.0",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -13677,28 +13686,28 @@
} }
}, },
"@typescript-eslint/utils": { "@typescript-eslint/utils": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz",
"integrity": "sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw==", "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12", "@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.50.0", "@typescript-eslint/scope-manager": "5.52.0",
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"@typescript-eslint/typescript-estree": "5.50.0", "@typescript-eslint/typescript-estree": "5.52.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0", "eslint-utils": "^3.0.0",
"semver": "^7.3.7" "semver": "^7.3.7"
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "5.50.0", "version": "5.52.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz",
"integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==", "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "5.50.0", "@typescript-eslint/types": "5.52.0",
"eslint-visitor-keys": "^3.3.0" "eslint-visitor-keys": "^3.3.0"
} }
}, },
@ -14226,9 +14235,9 @@
"integrity": "sha512-hCo2vmZStPq6qST1IaGf87ULgw52JYVrXJqhzvoBM38cygeN+bKrGxEq/QEwUvT6jI/zs7KvUm76luSDT2dR+Q==" "integrity": "sha512-hCo2vmZStPq6qST1IaGf87ULgw52JYVrXJqhzvoBM38cygeN+bKrGxEq/QEwUvT6jI/zs7KvUm76luSDT2dR+Q=="
}, },
"better-sqlite3": { "better-sqlite3": {
"version": "8.0.1", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.0.1.tgz", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.1.0.tgz",
"integrity": "sha512-JhTZjpyapA1icCEjIZB4TSSgkGdFgpWZA2Wszg7Cf4JwJwKQmbvuNnJBeR+EYG/Z29OXvR4G//Rbg31BW/Z7Yg==", "integrity": "sha512-p1m09H+Oi8R9TPj810pdNswMFuVgRNgCJEWypp6jlkOgSwMIrNyuj3hW78xEuBRGok5RzeaUW8aBtTWF3l/TQA==",
"requires": { "requires": {
"bindings": "^1.5.0", "bindings": "^1.5.0",
"prebuild-install": "^7.1.0" "prebuild-install": "^7.1.0"
@ -15578,9 +15587,9 @@
"dev": true "dev": true
}, },
"eslint": { "eslint": {
"version": "8.33.0", "version": "8.34.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@eslint/eslintrc": "^1.4.1", "@eslint/eslintrc": "^1.4.1",
@ -20393,9 +20402,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"sass": { "sass": {
"version": "1.58.0", "version": "1.58.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz",
"integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",
@ -20517,6 +20526,11 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true "dev": true
}, },
"set-interval-async": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/set-interval-async/-/set-interval-async-3.0.3.tgz",
"integrity": "sha512-o4DyBv6mko+A9cH3QKek4SAAT5UyJRkfdTi6JHii6ZCKUYFun8SwgBmQrOXd158JOwBQzA+BnO8BvT64xuCaSw=="
},
"set-value": { "set-value": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",

View File

@ -41,7 +41,7 @@
"@fortawesome/fontawesome-free": "^5.15.4", "@fortawesome/fontawesome-free": "^5.15.4",
"activedirectory2": "^2.1.0", "activedirectory2": "^2.1.0",
"better-sqlite-pool": "^0.3.1", "better-sqlite-pool": "^0.3.1",
"better-sqlite3": "^8.0.1", "better-sqlite3": "^8.1.0",
"bulma-calendar": "^6.1.18", "bulma-calendar": "^6.1.18",
"camelcase": "^7.0.1", "camelcase": "^7.0.1",
"compression": "^1.7.4", "compression": "^1.7.4",
@ -63,6 +63,7 @@
"papaparse": "^5.3.2", "papaparse": "^5.3.2",
"randomcolor": "^0.6.2", "randomcolor": "^0.6.2",
"session-file-store": "^1.5.0", "session-file-store": "^1.5.0",
"set-interval-async": "^3.0.3",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -78,9 +79,9 @@
"@types/cookie-parser": "^1.4.3", "@types/cookie-parser": "^1.4.3",
"@types/csurf": "^1.11.2", "@types/csurf": "^1.11.2",
"@types/debug": "^4.1.7", "@types/debug": "^4.1.7",
"@types/ejs": "^3.1.1", "@types/ejs": "^3.1.2",
"@types/express": "^4.17.16", "@types/express": "^4.17.17",
"@types/express-session": "^1.17.5", "@types/express-session": "^1.17.6",
"@types/gulp": "^4.0.10", "@types/gulp": "^4.0.10",
"@types/gulp-changed": "^0.0.35", "@types/gulp-changed": "^0.0.35",
"@types/gulp-minify": "^3.1.1", "@types/gulp-minify": "^3.1.1",
@ -93,8 +94,8 @@
"@types/randomcolor": "^0.5.7", "@types/randomcolor": "^0.5.7",
"@types/session-file-store": "^1.2.2", "@types/session-file-store": "^1.2.2",
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.50.0", "@typescript-eslint/eslint-plugin": "^5.52.0",
"@typescript-eslint/parser": "^5.50.0", "@typescript-eslint/parser": "^5.52.0",
"axe-core": "^4.6.3", "axe-core": "^4.6.3",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"bulma-divider": "^0.2.0", "bulma-divider": "^0.2.0",
@ -104,7 +105,7 @@
"bulma-tooltip": "^3.0.2", "bulma-tooltip": "^3.0.2",
"cypress": "^12.5.1", "cypress": "^12.5.1",
"cypress-axe": "^1.3.0", "cypress-axe": "^1.3.0",
"eslint": "^8.33.0", "eslint": "^8.34.0",
"eslint-config-standard-with-typescript": "^34.0.0", "eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
@ -116,6 +117,6 @@
"gulp-minify": "^3.1.0", "gulp-minify": "^3.1.0",
"gulp-sass": "^5.1.0", "gulp-sass": "^5.1.0",
"nodemon": "^2.0.20", "nodemon": "^2.0.20",
"sass": "^1.58.0" "sass": "^1.58.1"
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -4,9 +4,9 @@ import { exec } from 'node:child_process';
import * as http from 'node:http'; import * as http from 'node:http';
import { app } from '../app.js'; import { app } from '../app.js';
function runCypress(browser, done) { function runCypress(browser, done) {
let cypressCommand = `cypress run --config-file cypress.config.js --browser ${browser} --tag "${browser},${process.version}"`; let cypressCommand = `cypress run --config-file cypress.config.js --browser ${browser}"`;
if ((process.env.CYPRESS_RECORD_KEY ?? '') !== '') { if ((process.env.CYPRESS_RECORD_KEY ?? '') !== '') {
cypressCommand += ' --record'; cypressCommand += ` --tag "${browser},${process.version} --record`;
} }
const childProcess = exec(cypressCommand); const childProcess = exec(cypressCommand);
childProcess.stdout?.on('data', (data) => { childProcess.stdout?.on('data', (data) => {

View File

@ -10,10 +10,10 @@ import * as http from 'node:http'
import { app } from '../app.js' import { app } from '../app.js'
function runCypress(browser: 'chrome' | 'firefox', done: () => void): void { function runCypress(browser: 'chrome' | 'firefox', done: () => void): void {
let cypressCommand = `cypress run --config-file cypress.config.js --browser ${browser} --tag "${browser},${process.version}"` let cypressCommand = `cypress run --config-file cypress.config.js --browser ${browser}"`
if ((process.env.CYPRESS_RECORD_KEY ?? '') !== '') { if ((process.env.CYPRESS_RECORD_KEY ?? '') !== '') {
cypressCommand += ' --record' cypressCommand += ` --tag "${browser},${process.version} --record`
} }
const childProcess = exec(cypressCommand) const childProcess = exec(cypressCommand)

View File

@ -70,6 +70,7 @@ interface ConfigApplication {
userDomain?: string; userDomain?: string;
useTestDatabases?: boolean; useTestDatabases?: boolean;
ntfyStartup?: ConfigNtfyStartup; ntfyStartup?: ConfigNtfyStartup;
maximumProcesses?: number;
} }
export interface ConfigNtfyStartup { export interface ConfigNtfyStartup {
topic: string; topic: string;

View File

@ -71,6 +71,7 @@ interface ConfigApplication {
userDomain?: string userDomain?: string
useTestDatabases?: boolean useTestDatabases?: boolean
ntfyStartup?: ConfigNtfyStartup ntfyStartup?: ConfigNtfyStartup
maximumProcesses?: number
} }
export interface ConfigNtfyStartup { export interface ConfigNtfyStartup {