diff options
| -rw-r--r-- | .env.defaults | 1 | ||||
| -rw-r--r-- | app/directory.ts | 140 | ||||
| -rw-r--r-- | app/index.ts | 7 | ||||
| -rw-r--r-- | app/logger.ts | 23 | ||||
| -rw-r--r-- | app/routes/page/router.ts | 19 | ||||
| -rw-r--r-- | app/routes/special/router.ts | 3 | ||||
| -rw-r--r-- | app/wikiparser.ts | 47 | ||||
| -rw-r--r-- | package-lock.json | 412 | ||||
| -rw-r--r-- | package.json | 3 | ||||
| -rw-r--r-- | static/css/globalstyles.css | 6 |
10 files changed, 625 insertions, 36 deletions
diff --git a/.env.defaults b/.env.defaults index 3f2229f..1c11ae4 100644 --- a/.env.defaults +++ b/.env.defaults @@ -3,3 +3,4 @@ PAGES_DIR=pages REBUILD_COOLDOWN_MIN=10 PURGE_COOLDOWN_MIN=10 PARSER_MAX_RECURSION=20 +LOGGING_LEVEL=info diff --git a/app/directory.ts b/app/directory.ts index fd9c008..ee6a5a9 100644 --- a/app/directory.ts +++ b/app/directory.ts @@ -1,9 +1,9 @@ -import { parse } from './wikiparser.js'; +import * as parser from './wikiparser.js'; import { readFileSync } from 'fs'; import glob from 'glob'; +import { logger } from './logger.js' export class PageDirectory { - pages: Record<string, Page>; primaryPages: Page[]; pagePath: string; @@ -33,28 +33,74 @@ export class PageDirectory { const pages = glob.sync(`**/*.wiki`, { cwd: this.pagePath }) pages.forEach(page => { - page = page.replace('.wiki', '').replace('/', ':').replace(/[^a-z0-9:]/gi, '_').toLowerCase(); + page = this.convertNameToStandard(page.replace('.wiki', '').replace('/', ':')); this.pages[page] = { standardName: page, + raw: this.loadRaw(page), buildTime: 0, - metadata: {} + metadata: { + dependencies: [], + dependents: [], + errors: [] + } } }); - // Build templates first - Object.keys(this.pages).forEach(name => { - if (name.includes('Template:')) { - this.pages[name] = this.buildPage(name); + const dependencyGraph: Record<string, string[]> = {}; + + Object.keys(this.pages).forEach(name => dependencyGraph[name] = Array.from(parser.findDependencies(this.pages[name].raw)).map(e => this.convertNameToStandard(e))); + + function traverse(dependents: string[], dependencies: string[], recursionCount: number) { + if (recursionCount > parseInt(process.env.PARSER_MAX_RECURSION, 10)) { + throw new RecursionError('max recursion reached'); } + dependencies?.forEach((dependency: string) => { + if (dependencyGraph[dependency]?.length != 0) { + dependents.forEach((dependent: string) => { + if (dependencyGraph[dependency]?.includes(dependent)) { + throw new DependencyError(`circular dependency between ${dependent} and ${dependency}`, [dependent, dependency]); + } + }); + traverse([...dependents, dependency], dependencyGraph[dependency], recursionCount + 1); + } + }); + } + + Object.keys(dependencyGraph).forEach(name => { + dependencyGraph[name].forEach(dependency => { + try { + traverse([name, dependency], dependencyGraph[dependency], 1); + } catch (e) { + if (e instanceof RecursionError) { + this.pages[name].metadata.errors.push({ + identifier: 'max-recursion-reached', + message: `maximum dependency depth of ${process.env.PARSER_MAX_RECURSION} reached` + }) + logger.warn(`max recursion for ${name} reached`) + } else if (e instanceof DependencyError) { + if (e.pages.includes(name)) { + this.pages[name].metadata.errors.push({ + identifier: 'circular-dependency', + message: e.message + }) + logger.warn(`${e.pages[0]} has a circular dependency with ${e.pages[1]}`) + } else { + logger.warn(`transclusions on page ${name} may not resolve due to dependency errors in its dependency tree`) + } + } else { + throw e; + } + } + }); }); const primaryPages = []; Object.keys(this.pages).forEach(name => { - if (!name.includes('Template:')) { + if (this.pages[name].metadata.errors.length == 0) { this.pages[name] = this.buildPage(name); - } - if (this.pages[name].metadata.includeInNavbar) { - primaryPages.push(this.pages[name]); + if (this.pages[name].metadata.includeInNavbar) { + primaryPages.push(this.pages[name]); + } } }); @@ -88,11 +134,7 @@ export class PageDirectory { if (!page) { return undefined; } - - if (!page.html) { - return this.buildPage(name) - } - + return page; } @@ -147,6 +189,17 @@ export class PageDirectory { getPrimaryPages(): Page[] { return this.primaryPages; } + + private loadRaw(name: string): string { + name = this.convertNameToStandard(name); + let data: string; + try { + data = readFileSync(`${this.pagePath}/${this.convertStandardToFilePath(name)}`, 'utf-8'); + } catch { + return undefined; + } + return data; + } /** * Build a page. @@ -157,12 +210,13 @@ export class PageDirectory { private buildPage(name: string): Page { name = this.convertNameToStandard(name); let data: string; - try { - data = readFileSync(`${this.pagePath}/${this.convertStandardToFilePath(name)}`, 'utf-8'); - } catch { - return undefined; + if (this.pages[name]?.raw) { + data = this.pages[name]?.raw + } else { + data = this.loadRaw(name) } - const result = parse(this, data); + + const result = parser.parse(this, data); const title = result.metadata.displayTitle ?? name const content = `${result.metadata.notitle ? '' : `<h1>${title}</h1>`}${result.html}`; @@ -175,7 +229,10 @@ export class PageDirectory { includeInNavbar: result.metadata.primary ?? false, sortOrder: result.metadata.sortOrder ?? -1, showTitle: !result.metadata.notitle ?? true, - displayTitle: title + displayTitle: title, + dependencies: [], + dependents: [], + errors: [] } }; this.pages[name] = page; @@ -189,7 +246,11 @@ export class PageDirectory { * @param name non-standard name for a page */ private convertNameToStandard(name: string): string { - return name.replace(/[^a-z0-9:]/gi, '_').toLowerCase(); + name = name.replace(/[^a-z0-9:]/gi, '_').toLowerCase(); + if (!name.includes(':')) { + name = `main:${name}`; + } + return name; } /** @@ -198,7 +259,7 @@ export class PageDirectory { * @param name standard name for a page */ private convertStandardToFilePath(name: string): string { - const [first, second] = name.split(':'); + const [first, second] = name.replace('main:', '').split(':'); const [title, subpage] = ((second) ? second : first).split('.') const namespace = (second) ? first : undefined @@ -209,7 +270,7 @@ export class PageDirectory { export type Page = { html?: string; raw?: string; - standardName: string, + standardName: string; buildTime: number; metadata: PageMetadata; }; @@ -219,4 +280,31 @@ export type PageMetadata = { sortOrder?: number; showTitle?: boolean; includeInNavbar?: boolean; + dependencies: string[]; + dependents: string[]; + errors: PageError[]; }; + +export type PageError = { + identifier: string; + message: string; +} + +export class DependencyError extends Error { + pages: string[] + + constructor(message: string, pages: string[]) { + super(message); + this.pages = pages; + + Object.setPrototypeOf(this, DependencyError.prototype); + } +} + +export class RecursionError extends Error { + constructor(message: string) { + super(message); + + Object.setPrototypeOf(this, RecursionError.prototype); + } +} diff --git a/app/index.ts b/app/index.ts index 9865190..8514380 100644 --- a/app/index.ts +++ b/app/index.ts @@ -4,14 +4,13 @@ import dotenv from 'dotenv-defaults'; import * as page from './routes/page/router.js'; import * as special from './routes/special/router.js'; import { navbar } from './middlewares/index.js' +import { logger } from './logger.js' dotenv.config() const app = express(); const directory = new PageDirectory(process.env.PAGES_DIR); -directory.rebuild(); - app.set('view engine', 'ejs'); app.set('views', 'views'); @@ -33,11 +32,11 @@ app.use(navbar, (req, res) => { }); const server = app.listen(process.env.PORT, () => { - console.log(`App listening on port ${process.env.PORT}`); + logger.info(`App listening on port ${process.env.PORT}`); }); const exit = () => { - console.info('Stopping server...'); + logger.info('Stopping server...'); server.close(() => { process.exit(0); }) diff --git a/app/logger.ts b/app/logger.ts new file mode 100644 index 0000000..4ce0150 --- /dev/null +++ b/app/logger.ts @@ -0,0 +1,23 @@ +import winston from 'winston'; + +const enumerateErrorFormat = winston.format((info) => { + if (info instanceof Error) { + Object.assign(info, { message: info.stack }); + } + return info; + }); + +export const logger = winston.createLogger({ + level: process.env.LOGGING_LEVEL === 'development' ? 'debug' : 'info', + format: winston.format.combine( + enumerateErrorFormat(), + winston.format.colorize(), + winston.format.splat(), + winston.format.printf(({ level, message }) => `${level}: ${message}`) + ), + transports: [ + new winston.transports.Console({ + stderrLevels: ['error'], + }), + ], +}); diff --git a/app/routes/page/router.ts b/app/routes/page/router.ts index af5e844..dc819f8 100644 --- a/app/routes/page/router.ts +++ b/app/routes/page/router.ts @@ -26,11 +26,26 @@ router.get('/:page?', navbar, (req, res, next) => { return; } + let html: string; + let title: string; + + if (page.metadata.errors.length != 0) { + html = '<div class="box-red">This page could not be built due to the following errors:<br><ul>' + page.metadata.errors.forEach(e => { + html += `<li>${e.identifier}: ${e.message}</li>` + }); + html += '</ul>Go <a href="/">home</a>?</div>' + title = 'Page error' + } else { + html = page.html; + title = page.metadata.displayTitle; + } + res.render('page.ejs', { navbar: res.locals.navbarHtml, path: res.locals.path, - content: page.html, - title: page.metadata.displayTitle, + content: html, + title: title, buildTime: new Date(page.buildTime) }); }); diff --git a/app/routes/special/router.ts b/app/routes/special/router.ts index e88919c..26130c5 100644 --- a/app/routes/special/router.ts +++ b/app/routes/special/router.ts @@ -1,5 +1,6 @@ import express from 'express'; import { navbar, page } from '../../middlewares/index.js'; +import { logger } from './../../logger.js' export const router = express.Router({ mergeParams: true }); @@ -30,6 +31,7 @@ router.get('/special/purge/:page/confirm', (req, res, next) => { return; } + logger.info(`Purge for page ${page.standardName} requested by ${req.headers['x-forwarded-for'] || req.socket.remoteAddress }`) if (res.locals.directory.purge(res.locals.path)) { res.status(200).send(); } else { @@ -44,6 +46,7 @@ router.get('/special/rebuild', navbar, (req, res) => { }); router.get('/special/rebuild/confirm', (req, res) => { + logger.info(`Directory rebuild requested by ${req.headers['x-forwarded-for'] || req.socket.remoteAddress }`) if (res.locals.directory.rebuild()) { res.status(200).send(); } else { diff --git a/app/wikiparser.ts b/app/wikiparser.ts index 544b6e5..f52d646 100644 --- a/app/wikiparser.ts +++ b/app/wikiparser.ts @@ -38,6 +38,49 @@ const re = (regex, flag = 'mgi') => { const r = String.raw; const arg = r`\s*([^|}]+?)\s*`; +export function findDependencies(data: string): Set<string> { + const pages = new Set<string>(); + + let outText = data; + for (let l = 0, last = ''; l < parseInt(process.env.PARSER_MAX_RECURSION, 10); l++) { + if (last === outText) break; last = outText; + + outText = outText + // Remove non-template magic words + .replace(re(r`<(/?) \s* (?= script|link|meta|iframe|frameset|object|embed|applet|form|input|button|textarea )`), '<$1') + .replace(re(r`(?<= <[^>]+ ) (\bon(\w+))`), 'data-$2') + .replace(/<!--[^]+?-->/g, '') + .replace(re(r`{{ \s* displayTitle: ([^}]+) }}`), '') + .replace(re(r`{{ \s* navbarSortOrder: ([^}]+) }}`), '') + .replace(re(r`{{ \s* ! \s* }}`), '|') + .replace(re(r`{{ \s* = \s* }}`), '=') + .replace(re(r`{{ \s* [Rr]eflist \s* }}`), '<references/>') + .replace(re(r`{{ \s* #? urlencode: ${arg} }}`), '') + .replace(re(r`{{ \s* #? urldecode: ${arg} }}`), '') + .replace(re(r`{{ \s* #? lc: ${arg} }}`), '') + .replace(re(r`{{ \s* #? uc: ${arg} }}`), '') + .replace(re(r`{{ \s* #? lcfirst: ${arg} }}`), '') + .replace(re(r`{{ \s* #? ucfirst: ${arg} }}`), '') + .replace(re(r`{{ \s* #? len: ${arg} }}`), '') + .replace(re(r`{{ \s* #? pos: ${arg} \|${arg} (?: \s*\|${arg} )? }}`), '') + .replace(re(r`{{ \s* #? sub: ${arg} \|${arg} (?:\|${arg})? }}`), '') + .replace(re(r`{{ \s* #? padleft: ${arg} \|${arg} \|${arg} }}`), '') + .replace(re(r`{{ \s* #? padright: ${arg} \|${arg} \|${arg} }}`), '') + .replace(re(r`{{ \s* #? replace: ${arg} \|${arg} \|${arg} }}`), '') + .replace(re(r`{{ \s* #? explode: ${arg} \|${arg} \|${arg} }}`), '') + .replace(re(r`{{ \s* (#\w+) \s* : \s* ( [^{}]+ ) \s* }} ( ?!} )`), '') + + // Templates: {{template}} + .replace(re(r`{{ \s* ([^#}|]+?) (\|[^}]+)? }} (?!})`), (_, title, params = '') => { + if (/{{/.test(params)) return _; + const page = title.includes(':') ? title : `Template:${title}` + pages.add(page); + return ''; + }) + } + return pages; +} + export function parse(directory, data): Result { const vars = {}; const metadata: any = {}; @@ -131,8 +174,8 @@ export function parse(directory, data): Result { // Retrieve template content let content = directory.get(page); - if (!content) { - return `<a class="internal-link redlink" title="${title}" href="${page}">Template:${title}</a>`; + if (!content?.html) { + return `<a class="internal-link redlink" title="${title}" href="/${page}">Template:${title}</a>`; } // Remove non-template sections diff --git a/package-lock.json b/package-lock.json index bd6578f..4e52a2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "ejs": "^3.1.6", "escape-html": "^1.0.3", "express": "^4.17.1", - "glob": "^7.2.0" + "glob": "^7.2.0", + "winston": "^3.3.3" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.7.0", @@ -26,6 +27,16 @@ "eslint": "^8.5.0" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", @@ -658,6 +669,15 @@ "node": ">=4" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -671,6 +691,32 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -816,6 +862,11 @@ "node": ">=0.10.0" } }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1248,6 +1299,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1316,6 +1372,11 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1499,6 +1560,11 @@ "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1529,6 +1595,17 @@ "node": ">=0.12.0" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1576,6 +1653,11 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1595,6 +1677,23 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/logform": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", + "dependencies": { + "colors": "^1.2.1", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", + "triple-beam": "^1.3.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1729,6 +1828,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -1898,6 +2005,19 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -1972,6 +2092,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2060,6 +2185,14 @@ "node": ">=8" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2069,6 +2202,14 @@ "node": ">=8" } }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "engines": { + "node": "*" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -2077,6 +2218,33 @@ "node": ">= 0.6" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2112,6 +2280,11 @@ "node": ">=4" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2138,6 +2311,11 @@ "node": ">=0.6" } }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -2226,6 +2404,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2263,6 +2446,43 @@ "node": ">= 8" } }, + "node_modules/winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.1.tgz", + "integrity": "sha512-ciZRlU4CSjHqHe8RQG1iPxKMRVwv6ZJ0RC7DxStKWd0KjpAhPDy5gVYSCpIUq+5CUsP+IyNOTZy1X0tO2QZqjg==", + "dependencies": { + "logform": "^2.2.0", + "readable-stream": "^3.4.0", + "triple-beam": "^1.2.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston/node_modules/async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -2285,6 +2505,16 @@ } }, "dependencies": { + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@eslint/eslintrc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", @@ -2753,6 +2983,15 @@ "supports-color": "^5.3.0" } }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2766,6 +3005,29 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2878,6 +3140,11 @@ "jake": "^10.6.1" } }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -3211,6 +3478,11 @@ "reusify": "^1.0.4" } }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3267,6 +3539,11 @@ "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", "dev": true }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -3399,6 +3676,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3420,6 +3702,11 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3458,6 +3745,11 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3474,6 +3766,25 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "logform": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", + "requires": { + "colors": "^1.2.1", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3572,6 +3883,14 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -3682,6 +4001,16 @@ "unpipe": "1.0.0" } }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -3723,6 +4052,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3795,17 +4129,45 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3829,6 +4191,11 @@ "has-flag": "^3.0.0" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3849,6 +4216,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -3909,6 +4281,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -3934,6 +4311,39 @@ "isexe": "^2.0.0" } }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "dependencies": { + "async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + } + } + }, + "winston-transport": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.1.tgz", + "integrity": "sha512-ciZRlU4CSjHqHe8RQG1iPxKMRVwv6ZJ0RC7DxStKWd0KjpAhPDy5gVYSCpIUq+5CUsP+IyNOTZy1X0tO2QZqjg==", + "requires": { + "logform": "^2.2.0", + "readable-stream": "^3.4.0", + "triple-beam": "^1.2.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index cbae2c7..293868e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "ejs": "^3.1.6", "escape-html": "^1.0.3", "express": "^4.17.1", - "glob": "^7.2.0" + "glob": "^7.2.0", + "winston": "^3.3.3" }, "type": "module", "devDependencies": { diff --git a/static/css/globalstyles.css b/static/css/globalstyles.css index 3c39e38..6826ab9 100644 --- a/static/css/globalstyles.css +++ b/static/css/globalstyles.css @@ -99,3 +99,9 @@ a { padding: 10px; background-color: rgba(0, 0, 0, 0.1); } + +.box-red { + border: solid 1px rgba(255, 0, 0, 0.5); + padding: 10px; + background-color: rgba(255, 0, 0, 0.2); +} |
